Skip to content

feat(metadata-view): Implement metadata sidepanel #4230

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Aug 18, 2025

Conversation

tjiang-box
Copy link
Contributor

@tjiang-box tjiang-box commented Aug 15, 2025

Included Changes:

  1. Metadata Button will get displayed only when there is at least one item selected
  2. Metadata SidePanel will get displayed only when Metadata button is clicked
  3. Metadata Instance Form should show value for the selected item
  4. It supports both view mode and edit more in sidepanel

TODO:

  1. complete form onChange function
  2. complete form on Submit function
  3. support metadata instance form value display for multiple selected items

Landing Page
Screenshot 2025-08-15 at 3 22 18 PM

At least one Item is selected - Metadata Button gets displayed
Screenshot 2025-08-15 at 3 22 28 PM

Metadata SidePanel ViewMode - after Metadata button is clicked
Screenshot 2025-08-15 at 3 22 34 PM

Metadata SidePanel EditMode - after edit button is clicked
Screenshot 2025-08-15 at 3 22 40 PM

Summary by CodeRabbit

  • New Features

    • Feature‑gated metadata side panel in Content Explorer with open/close toggle, selection‑aware Metadata action, and view/edit modes; SubHeader now exposes a toggle prop and passes selection to the right-side controls.
  • Refactor

    • Centralized selection-label logic into a reusable hook and added helper to build metadata template instances; selection wiring moved into Content Explorer state.
  • Style

    • Two‑column Content Explorer layout and side‑panel styling updates.
  • Tests

    • Unit tests for side‑panel view/edit flows, expanded metadata‑V2 tests, and a Storybook visual test.

@tjiang-box tjiang-box requested a review from a team as a code owner August 15, 2025 20:27
Copy link

coderabbitai bot commented Aug 15, 2025

Walkthrough

Adds a feature-flagged Metadata Side Panel to Content Explorer, threads selection state and a panel toggle through SubHeader components, extracts selection-label utilities, updates layout/styles, and adds tests plus a Storybook visual test.

Changes

Cohort / File(s) Summary of Changes
Sub-header wiring
src/elements/common/sub-header/SubHeader.tsx, src/elements/common/sub-header/SubHeaderRight.tsx, src/elements/common/sub-header/SubHeaderLeftV2.tsx
SubHeaderProps now includes onMetadataSidePanelToggle?: () => void and forwards it to SubHeaderRight; SubHeaderRightProps extended with onMetadataSidePanelToggle?: () => void and selectedItemIds?: Selection; SubHeaderLeftV2 uses useSelectedItemText hook instead of inline useMemo.
Content Explorer core & side panel
src/elements/content-explorer/ContentExplorer.tsx, src/elements/content-explorer/MetadataSidePanel.tsx, src/elements/content-explorer/utils.ts
Adds MetadataSidePanel component and MetadataSidePanelProps (currentCollection, metadataTemplate, selectedItemIds, onClose), introduces isMetadataSidePanelOpen state plus onMetadataSidePanelToggle / closeMetadataSidePanel handlers, wires selection changes to open/close the panel, and adds utilities useSelectedItemText and getTemplateInstance.
Styles
src/elements/content-explorer/ContentExplorer.scss, src/elements/content-explorer/MetadataSidePanel.scss
Adds layout classes for Content Explorer main/row layout and side-panel structure; adds .bce-MetadataSidePanel-subtitle and .bce-MetadataSidePanel-content (hides a specific delete button inside panel content).
Tests
src/elements/content-explorer/__tests__/ContentExplorer.test.tsx, src/elements/content-explorer/__tests__/MetadataSidePanel.test.tsx
Updates ContentExplorer tests to pass features and exercise metadata V2 selection/button flows; adds unit tests for MetadataSidePanel covering render, close, selection subtitles, and view/edit interactions.
Storybook visual test
src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx
Adds sidePanelOpenWithSingleItemSelected visual story that selects a row and opens the Metadata side panel via the Metadata button.

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant SubHeaderRight
  participant SubHeader
  participant ContentExplorer
  participant MetadataSidePanel

  User->>SubHeaderRight: Click "Metadata" button
  SubHeaderRight->>SubHeader: call onMetadataSidePanelToggle()
  SubHeader->>ContentExplorer: invoke onMetadataSidePanelToggle()
  ContentExplorer->>ContentExplorer: toggle isMetadataSidePanelOpen
  alt feature enabled & metadata view & open
    ContentExplorer-->>MetadataSidePanel: render with selectedItemIds
  end
Loading
sequenceDiagram
  participant MetadataView
  participant ContentExplorer
  participant MetadataSidePanel

  MetadataView->>ContentExplorer: onSelectionChange(ids)
  ContentExplorer->>ContentExplorer: set selectedItemIds
  alt selection empty
    ContentExplorer->>ContentExplorer: closeMetadataSidePanel()
    ContentExplorer--x MetadataSidePanel: Unmount
  else selection non-empty
    ContentExplorer->>MetadataSidePanel: pass selectedItemIds
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

ready-to-merge

Suggested reviewers

  • greg-in-a-box
  • tjuanitas

Poem

I hop through lists and open a pane,
A tiny side room for metadata to explain.
Click the button, names appear in sight,
Toggle, edit, close — the rabbit’s delight. 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (11)
src/elements/content-explorer/ContentExplorer.scss (1)

24-26: Use logical margin for RTL support (or prefer flex gap on the container).

Using margin-left will render incorrectly in RTL locales. Either switch to margin-inline-start or move spacing responsibility to the flex container via column-gap.

Option A: minimally fix RTL

-            margin-left: var(--space-4);
+            margin-inline-start: var(--space-4);

Option B: prefer container gap and remove panel margin (cleaner as layout concern belongs to the flex container)

 .bce-container {
     display: flex;
     height: 100%;
     min-height: 0;
+    column-gap: var(--space-4);
 }
 .bce-sidepanel {
-    margin-left: var(--space-4);
 }
src/elements/content-explorer/MetadataSidePanel.scss (1)

19-21: Avoid hiding UI via a brittle data-target selector; prefer a prop-driven or semantic control.

Targeting [data-target-id='TextButton-deleteButton'] in CSS is fragile and risks regressions if the attribute or component structure changes. It also couples styling to an implementation/testing detail.

Recommended approaches:

  • Hide the delete action at the component level (e.g., pass a prop to omit it from render or conditionally filter actions).
  • If style-only is required, use a more stable className on the specific button provided by the component API.
  • As a last resort, add a BEM/namespace wrapper for the specific panel section and scope this rule tightly to avoid unintended effects.

I can help wire a prop through the component tree to omit the delete button entirely — want me to draft that?

src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx (1)

136-160: Make the play-step more robust by awaiting rendered elements before interaction.

Minimize flakiness by using findByRole instead of a manual waitFor + getByRole and by awaiting the Metadata button before clicking.

Apply this diff:

     play: async ({ canvas }) => {
-        await waitFor(() => {
-            expect(canvas.getByRole('row', { name: /Child 2/i })).toBeInTheDocument();
-        });
-
-        // Select the first row by clicking its checkbox
-        const firstRow = canvas.getByRole('row', { name: /Child 2/i });
-        const checkbox = within(firstRow).getByRole('checkbox');
+        const firstRow = await canvas.findByRole('row', { name: /Child 2/i });
+        const checkbox = within(firstRow).getByRole('checkbox'); // within(...) after findByRole avoids race
         await userEvent.click(checkbox);
 
-        const metadataButton = canvas.getByRole('button', { name: 'Metadata' });
+        const metadataButton = await canvas.findByRole('button', { name: 'Metadata' });
         await userEvent.click(metadataButton);
     },

Optional: If possible, assert the panel opened by awaiting a stable element unique to the panel (e.g., its header/title or a landmark/region role).

src/elements/common/sub-header/SubHeader.tsx (1)

31-31: Consider documenting the toggle callback's behavior.

The optional onToggleMetadataSidePanel prop should have a JSDoc comment explaining when it's called and what UI state it affects.

Add documentation above the interface property:

     onSortChange: (sortBy: string, sortDirection: string) => void;
+    /** Callback triggered when the metadata side panel toggle button is clicked */
     onToggleMetadataSidePanel?: () => void;
src/elements/content-explorer/__tests__/MetadataSidePanel.test.tsx (1)

65-127: Consider adding test coverage for edge cases.

While the test suite covers the main functionality well, consider adding tests for:

  • Empty collection handling
  • Error states when metadata template is missing or invalid
  • Keyboard navigation/accessibility
  • Multiple template scenarios

Would you like me to generate additional test cases for these edge cases?

src/elements/content-explorer/utils.ts (2)

51-52: Document the TODO for multiple item support.

The TODO comment indicates incomplete functionality for multiple selected items. This should be tracked properly.

Would you like me to open an issue to track the implementation of multiple selected items support? This would involve aggregating field values across all selected items and handling conflicts appropriately.


32-66: Consider adding error boundary for metadata template processing.

The getTemplateInstance function should handle cases where the metadata template or selected items have unexpected structures.

Consider wrapping the field mapping in a try-catch or adding validation:

 export function getTemplateInstance(metadataTemplate: MetadataTemplate, selectedItems: BoxItem[]) {
     const { displayName, fields, hidden, id, scope, templateKey, type } = metadataTemplate;
+    
+    if (!selectedItems.length) {
+        return {
+            canEdit: true,
+            displayName,
+            hidden,
+            id,
+            fields: [],
+            scope,
+            templateKey,
+            type,
+        };
+    }
src/elements/content-explorer/ContentExplorer.tsx (1)

1856-1867: Consider extracting the side panel visibility condition.

The condition for showing the metadata side panel is complex and could be extracted for better readability.

+        const shouldShowMetadataSidePanel = 
+            isDefaultViewMetadata && 
+            isMetadataViewV2Feature && 
+            this.state.isMetadataSidePanelOpen;
+
         // ... in render
-                                {isDefaultViewMetadata &&
-                                    isMetadataViewV2Feature &&
-                                    this.state.isMetadataSidePanelOpen && (
+                                {shouldShowMetadataSidePanel && (
                                         <div className="bce-sidepanel">
src/elements/content-explorer/MetadataSidePanel.tsx (3)

79-83: Hide decorative icon from assistive tech

The file icon is purely decorative next to the subtitle. Mark it as hidden to reduce noise for screen readers.

Apply this diff:

-                        <div className="bce-sidepanel-subtitle">
-                            <FileDefault />
+                        <div className="bce-sidepanel-subtitle">
+                            <FileDefault aria-hidden="true" />
                             <Text as="span" color="textOnLightSecondary" variant="subtitle">
                                 {selectedItemText}
                             </Text>
                         </div>

96-96: Avoid passing null to optional function props

Passing null to props that are likely treated as optional functions/fetchers can cause “null is not a function” at runtime. Prefer omitting the prop or using undefined.

The previous diff already removes fetchSuggestions={null} and replaces other = {null} with = {undefined}. Please confirm the typings in @box/metadata-editor and @box/blueprint-web accept omitted/undefined values. If they require explicit no-op callbacks, we can wire safe stubs instead.


41-46: Minor: memoize selectedItems to avoid unnecessary recomputation

Not critical, but filter runs on every render. You can memoize selectedItems based on currentCollection.items and selectedItemIds.

Example:

-import React, { useState } from 'react';
+import React, { useMemo, useState } from 'react';
@@
-    const selectedItems =
-        selectedItemIds === 'all'
-            ? currentCollection.items
-            : currentCollection.items.filter(item => selectedItemIds.has(item.id));
+    const selectedItems = useMemo(
+        () =>
+            selectedItemIds === 'all'
+                ? currentCollection.items
+                : currentCollection.items.filter(item => selectedItemIds.has(item.id)),
+        [currentCollection.items, selectedItemIds],
+    );
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b68cd70 and c04c623.

📒 Files selected for processing (11)
  • src/elements/common/sub-header/SubHeader.tsx (3 hunks)
  • src/elements/common/sub-header/SubHeaderLeftV2.tsx (2 hunks)
  • src/elements/common/sub-header/SubHeaderRight.tsx (5 hunks)
  • src/elements/content-explorer/ContentExplorer.scss (1 hunks)
  • src/elements/content-explorer/ContentExplorer.tsx (7 hunks)
  • src/elements/content-explorer/MetadataSidePanel.scss (1 hunks)
  • src/elements/content-explorer/MetadataSidePanel.tsx (1 hunks)
  • src/elements/content-explorer/__tests__/ContentExplorer.test.tsx (3 hunks)
  • src/elements/content-explorer/__tests__/MetadataSidePanel.test.tsx (1 hunks)
  • src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx (1 hunks)
  • src/elements/content-explorer/utils.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
src/elements/common/sub-header/SubHeaderLeftV2.tsx (1)
src/elements/content-explorer/utils.ts (1)
  • useSelectedItemText (11-29)
src/elements/content-explorer/MetadataSidePanel.tsx (1)
src/elements/content-explorer/utils.ts (2)
  • useSelectedItemText (11-29)
  • getTemplateInstance (32-66)
src/elements/content-explorer/ContentExplorer.tsx (2)
src/elements/content-picker/Content.js (1)
  • Content (51-98)
src/features/metadata-based-view/__tests__/MetadataBasedItemList.test.js (1)
  • fieldsToShow (102-107)
src/elements/content-explorer/__tests__/ContentExplorer.test.tsx (3)
src/elements/content-explorer/ContentExplorer.tsx (1)
  • ContentExplorer (1969-1969)
src/elements/common/__mocks__/mockMetadata.ts (1)
  • mockSchema (226-226)
src/features/metadata-based-view/__tests__/MetadataBasedItemList.test.js (1)
  • fieldsToShow (102-107)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: lint_test_build
  • GitHub Check: Summary
🔇 Additional comments (15)
src/elements/content-explorer/MetadataSidePanel.scss (1)

1-18: Solid structural styles for the panel; flex alignment and spacing look good.

The basic layout (header/title/subtitle/content) and use of spacing tokens are consistent and clear. No issues from a layout perspective.

src/elements/common/sub-header/SubHeaderLeftV2.tsx (1)

24-24: Good reuse of shared hook for selection text.

Shifting to useSelectedItemText centralizes logic and keeps SubHeaderLeftV2 lean. Dependencies and memoization in the hook look appropriate given immutable updates to items.

src/elements/common/sub-header/SubHeaderRight.tsx (1)

100-104: Conditional rendering of the Metadata button is clean and correctly gated.

Gating on view, feature flag, and selection state avoids accidental surfacing of the action. Wiring onClick to onToggleMetadataSidePanel keeps the component stateless.

src/elements/common/sub-header/SubHeader.tsx (1)

114-114: Props are correctly forwarded to SubHeaderRight.

The implementation properly passes the new onToggleMetadataSidePanel and selectedItemIds props to the SubHeaderRight component for handling the metadata button visibility and actions.

Also applies to: 118-118

src/elements/content-explorer/__tests__/MetadataSidePanel.test.tsx (1)

7-10: Good practice mocking the scrollTo method.

Mocking Element.prototype.scrollTo prevents test failures when the component attempts to scroll, which is appropriate for unit tests.

src/elements/content-explorer/__tests__/ContentExplorer.test.tsx (2)

426-487: Well-structured metadata view V2 test setup.

The test properly sets up the metadata view V2 configuration with dynamic columns, fields, and query construction based on the mock schema. The implementation correctly enables the feature flag and validates the UI behavior.


489-504: Comprehensive UI interaction test.

The test effectively validates the metadata view button visibility after selection and proper rendering of the content explorer with metadata view V2 enabled.

src/elements/content-explorer/utils.ts (1)

11-29: Efficient selection text computation with proper memoization.

The useSelectedItemText hook correctly handles all selection states (empty, single, multiple, and 'all') with appropriate internationalization and memoization.

src/elements/content-explorer/ContentExplorer.tsx (6)

173-173: State property added correctly for metadata side panel.

The isMetadataSidePanelOpen boolean state is properly initialized to false and follows the existing state management pattern.

Also applies to: 299-299


1549-1554: Good integration with selection change handler.

The code correctly closes the metadata side panel when selection becomes empty, ensuring consistent UI state.


1634-1639: Consistent panel closing on selection clear.

The clearSelectedItemIds method properly closes the metadata side panel when clearing selections, maintaining UI consistency.


1647-1651: Well-implemented toggle and close methods.

The toggle and close methods for the metadata side panel are simple and follow React best practices.

Also applies to: 1659-1661


1779-1868: Clean layout implementation with proper feature gating.

The render method correctly:

  1. Wraps content in a container/main structure for the two-column layout
  2. Feature-gates the side panel rendering
  3. Passes all required props to the MetadataSidePanel component

1860-1865: MetadataSidePanelProps Verified

The props passed to MetadataSidePanel (currentCollection, closeMetadataSidePanel, metadataTemplate, selectedItemIds) exactly match its exported MetadataSidePanelProps interface. No further changes needed.

src/elements/content-explorer/MetadataSidePanel.tsx (1)

31-40: Solid initial structure and state wiring

Props are clearly defined; view/edit toggling and unsaved changes flow are set up for future save logic. Good separation of edit vs view modes.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (7)
src/elements/content-explorer/ContentExplorer.tsx (7)

451-455: Ensure V2 helper is structurally compatible with the original helper

this.metadataQueryAPIHelper is declared as MetadataQueryAPIHelper, but can receive an instance of MetadataQueryAPIHelperV2. This relies on structural compatibility. Verify both classes expose the same surface used here: fetchMetadataQueryResults and updateMetadata with compatible signatures. If not, typing should be adjusted to a shared interface.

Optionally define a minimal interface (e.g., IMetadataQueryHelper) capturing the used methods and type metadataQueryAPIHelper to that interface to enforce compile-time safety.


1635-1639: Type the empty Set to match Selection’s Key type

Minor TS nit: new Set() defaults to a broad generic. Explicitly typing improves clarity and avoids potential type drift.

-        this.setState({
-            selectedItemIds: new Set(),
-            isMetadataSidePanelOpen: false,
-        });
+        this.setState({
+            selectedItemIds: new Set<React.Key>(),
+            isMetadataSidePanelOpen: false,
+        });

1647-1651: Toggle/close methods are straightforward

Implementation is concise. Optional: guard toggle when no selection is present to align with UI affordance and avoid accidental opens via programmatic calls.

Also applies to: 1659-1661


1781-1784: Header render condition should track current view, not default view

Using defaultView to decide Header visibility is static and may hide the Header even after navigating away from metadata view. Prefer checking the current view.

-                                    {!isDefaultViewMetadata && (
+                                    {view !== VIEW_METADATA && (
                                         <Header view={view} logoUrl={logoUrl} onSearch={this.search} />
                                     )}

Please verify there’s no product requirement to always hide Header when defaultView is metadata. If the intent is to hide Header only in metadata view, the change above brings behavior in line.


1785-1810: SubHeader wiring for panel toggle and selection is good; consider exposing open state

Props threading looks good. Optional: also pass isMetadataSidePanelOpen so the toggle control can reflect pressed state (e.g., aria-pressed) for better accessibility feedback.


1856-1868: Tighten side panel render condition and add landmark semantics

To prevent the panel from appearing outside metadata view or during error view, gate on the current view and !isErrorView. Also, add landmark roles to improve accessibility.

-                                {isDefaultViewMetadata &&
-                                    isMetadataViewV2Feature &&
-                                    this.state.isMetadataSidePanelOpen && (
-                                        <div className="bce-sidepanel">
+                                {view === VIEW_METADATA &&
+                                    isMetadataViewV2Feature &&
+                                    !isErrorView &&
+                                    this.state.isMetadataSidePanelOpen && (
+                                        <div className="bce-sidepanel" role="complementary" aria-label="Metadata">
                                             <MetadataSidePanel
                                                 currentCollection={currentCollection}
                                                 closeMetadataSidePanel={this.closeMetadataSidePanel}
                                                 metadataTemplate={metadataTemplate}
                                                 selectedItemIds={this.state.selectedItemIds}
                                             />
                                         </div>
                                     )}

Optional (related): add role="main" to <div className="bce-main"> to establish landmarks clearly.


1780-1781: Add a main landmark to the primary content container

Minor a11y improvement: add role="main" to the main region.

-                            <div className="bce-container">
-                                <div className="bce-main">
+                            <div className="bce-container">
+                                <div className="bce-main" role="main">
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c04c623 and 7a1372d.

📒 Files selected for processing (11)
  • src/elements/common/sub-header/SubHeader.tsx (3 hunks)
  • src/elements/common/sub-header/SubHeaderLeftV2.tsx (2 hunks)
  • src/elements/common/sub-header/SubHeaderRight.tsx (5 hunks)
  • src/elements/content-explorer/ContentExplorer.scss (1 hunks)
  • src/elements/content-explorer/ContentExplorer.tsx (7 hunks)
  • src/elements/content-explorer/MetadataSidePanel.scss (1 hunks)
  • src/elements/content-explorer/MetadataSidePanel.tsx (1 hunks)
  • src/elements/content-explorer/__tests__/ContentExplorer.test.tsx (3 hunks)
  • src/elements/content-explorer/__tests__/MetadataSidePanel.test.tsx (1 hunks)
  • src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx (1 hunks)
  • src/elements/content-explorer/utils.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
  • src/elements/content-explorer/MetadataSidePanel.scss
  • src/elements/content-explorer/ContentExplorer.scss
  • src/elements/common/sub-header/SubHeaderLeftV2.tsx
  • src/elements/common/sub-header/SubHeader.tsx
  • src/elements/content-explorer/MetadataSidePanel.tsx
  • src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx
  • src/elements/common/sub-header/SubHeaderRight.tsx
  • src/elements/content-explorer/utils.ts
  • src/elements/content-explorer/tests/ContentExplorer.test.tsx
  • src/elements/content-explorer/tests/MetadataSidePanel.test.tsx
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/elements/content-explorer/ContentExplorer.tsx (2)
src/elements/content-picker/Content.js (1)
  • Content (51-98)
src/features/metadata-based-view/__tests__/MetadataBasedItemList.test.js (1)
  • fieldsToShow (102-107)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Summary
  • GitHub Check: lint_test_build
🔇 Additional comments (5)
src/elements/content-explorer/ContentExplorer.tsx (5)

27-27: Import of MetadataSidePanel looks correct

New component import is clear and scoped to Content Explorer.


173-173: State wiring for isMetadataSidePanelOpen is solid

Type and initial value are added consistently in State and constructor.

Also applies to: 299-301


1760-1760: Feature flag gate is correctly computed

Reads cleanly and centralizes the V2 feature enablement.


1811-1839: Content props extension for metadata view integration looks consistent

Passing metadataTemplate and metadataViewProps into Content aligns with the new side panel flow. No concerns here.


1841-1855: Footer/Pagination placement under the new layout is appropriate

Conditional render on error view preserves existing behavior.

greg-in-a-box
greg-in-a-box previously approved these changes Aug 15, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/elements/content-explorer/ContentExplorer.tsx (1)

1568-1572: Fix TS compile error from conditional spread in setState

Spreading a boolean when isSelectionEmpty is false is a TS error (“Spread types may only be created from object types”). Use a ternary to always spread an object.

Apply this diff:

                     const isSelectionEmpty = ids !== 'all' && ids.size === 0;
                     this.setState({
                         selectedItemIds: ids,
-                        ...(isSelectionEmpty && { isMetadataSidePanelOpen: false }),
+                        ...(isSelectionEmpty ? { isMetadataSidePanelOpen: false } : {}),
                     });
🧹 Nitpick comments (3)
src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx (1)

178-191: Harden the play steps to reduce flakiness and assert the panel opened

Use async querying for the Metadata button and add an assertion that the side panel actually opened after the click.

Apply this diff:

     play: async ({ canvas }) => {
         await waitFor(() => {
             expect(canvas.getByRole('row', { name: /Child 2/i })).toBeInTheDocument();
         });

         // Select the first row by clicking its checkbox
         const firstRow = canvas.getByRole('row', { name: /Child 2/i });
         const checkbox = within(firstRow).getByRole('checkbox');
         await userEvent.click(checkbox);

-        const metadataButton = canvas.getByRole('button', { name: 'Metadata' });
-        await userEvent.click(metadataButton);
+        const metadataButton = await canvas.findByRole('button', { name: /metadata/i });
+        await userEvent.click(metadataButton);
+
+        // Assert that the side panel opened
+        await waitFor(() => {
+            expect(document.querySelector('.bce-sidepanel')).toBeInTheDocument();
+        });
     },
src/elements/content-explorer/ContentExplorer.tsx (2)

1855-1866: Gate side panel by current view, add ARIA for testability/a11y

Rendering should likely depend on the active view (metadata) rather than the initial defaultView. Also, add a landmark role and label so tests and screen readers can target the side panel reliably.

Apply this diff:

-                                {isDefaultViewMetadata &&
-                                    isMetadataViewV2Feature &&
-                                    this.state.isMetadataSidePanelOpen && (
-                                        <div className="bce-sidepanel">
+                                {view === VIEW_METADATA &&
+                                    isMetadataViewV2Feature &&
+                                    this.state.isMetadataSidePanelOpen && (
+                                        <div
+                                            className="bce-sidepanel"
+                                            role="complementary"
+                                            aria-label="Metadata side panel"
+                                        >

Follow-up:

  • Verify whether the intent is to only show the side panel during metadata view. If yes, the change above aligns behavior with user expectations and your story’s flow.

1666-1670: Guard toggle to avoid opening panel with empty selection

Defensive check prevents opening the panel programmatically when no items are selected. The UI button is gated, but this keeps state consistent if methods are called directly.

     toggleMetadataSidePanel = () => {
-        this.setState(prevState => ({
-            isMetadataSidePanelOpen: !prevState.isMetadataSidePanelOpen,
-        }));
+        const { selectedItemIds } = this.state;
+        const isSelectionEmpty = selectedItemIds !== 'all' && selectedItemIds.size === 0;
+        if (isSelectionEmpty) {
+            return;
+        }
+        this.setState(prevState => ({
+            isMetadataSidePanelOpen: !prevState.isMetadataSidePanelOpen,
+        }));
     };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 61a4281 and 33cf986.

📒 Files selected for processing (2)
  • src/elements/content-explorer/ContentExplorer.tsx (7 hunks)
  • src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/elements/content-explorer/ContentExplorer.tsx (2)
src/elements/content-sidebar/versions/VersionsItem.js (1)
  • canPreview (108-108)
src/features/metadata-based-view/__tests__/MetadataBasedItemList.test.js (1)
  • fieldsToShow (102-107)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: lint_test_build
  • GitHub Check: Summary
🔇 Additional comments (5)
src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx (1)

167-177: Story args look good and align with the new flow

Reusing metadataViewV2ElementProps, enabling row selection, and keeping columns consistent with v2 is appropriate for this visual scenario.

src/elements/content-explorer/ContentExplorer.tsx (4)

1654-1658: Good: clearing selection also closes the side panel

This keeps the UI consistent with the requirement that the metadata side panel is actionable only with a selection.


1758-1758: Feature flag guard is clean and centralized

Using isFeatureEnabled for the v2 path is consistent with the rest of the codebase and reads clearly.


1777-1807: SubHeader wiring looks correct

Passing selectedItemIds and onToggleMetadataSidePanel into SubHeader enables the “Metadata” button visibility and action. The new container structure (.bce-container/.bce-main) appears cohesive.


1809-1837: Content props integration is coherent

Providing metadataTemplate, metadataViewProps, and onMetadataUpdate ties the v2 metadata flow together. No issues spotted.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (7)
src/elements/content-explorer/ContentExplorer.tsx (1)

1568-1572: Fix TS compile error: avoid spreading a boolean in setState

...(isSelectionEmpty && { ... }) evaluates to false when not empty, which TypeScript disallows spreading. Use a ternary that always yields an object.

Apply this diff:

-                    const isSelectionEmpty = ids !== 'all' && ids.size === 0;
-                    this.setState({
-                        selectedItemIds: ids,
-                        ...(isSelectionEmpty && { isMetadataSidePanelOpen: false }),
-                    });
+                    const isSelectionEmpty = ids !== 'all' && ids.size === 0;
+                    this.setState({
+                        selectedItemIds: ids,
+                        ...(isSelectionEmpty ? { isMetadataSidePanelOpen: false } : {}),
+                    });
src/elements/content-explorer/__tests__/MetadataSidePanel.test.tsx (3)

104-109: Fix typo: “calls”

Make the test title grammatically correct and align with the updated label.

Apply this diff:

-    test('call closeMetadataSidePanel when close button is clicked', async () => {
+    test('calls closeMetadataSidePanel when close button is clicked', async () => {
         renderComponent();
-        const closeButton = screen.getByLabelText('Clear selection');
+        const closeButton = screen.getByLabelText('Close');
         await userEvent.click(closeButton);
         expect(mockCloseMetadataSidePanel).toHaveBeenCalledTimes(1);
     });

111-115: Fix typo: “renders correct subtitle”

Update the test description.

Apply this diff:

-    test('render correct subtitle when multiple items are selected', () => {
+    test('renders correct subtitle when multiple items are selected', () => {
         renderComponent({ selectedItemIds: new Set(['1', '2']) });
         const subtitle = screen.getByText('2 files selected');
         expect(subtitle).toBeInTheDocument();
     });

117-126: Fix typo: “renders cancel and submit”

Update the test description for grammar.

Apply this diff:

-    test('render cancel and submit button when in edit mode', async () => {
+    test('renders cancel and submit button when in edit mode', async () => {
         renderComponent();
         const editTemplateButton = screen.getByLabelText('Edit Mock Template');
         await userEvent.click(editTemplateButton);

         const cancelButton = screen.getByRole('button', { name: 'Cancel' });
         expect(cancelButton).toBeInTheDocument();
         const submitButton = screen.getByRole('button', { name: 'Save' });
         expect(submitButton).toBeInTheDocument();
     });
src/elements/content-explorer/MetadataSidePanel.tsx (3)

41-47: Defensively compute template instance to avoid runtime errors

getTemplateInstance dereferences nested metadata; if selection is empty or the first item lacks the template, it will throw. Guard the call and fall back to null.

Apply this diff:

-    const templateInstance = getTemplateInstance(metadataTemplate, selectedItems);
+    const hasTemplateData =
+        selectedItems.length > 0 &&
+        Boolean(selectedItems[0]?.metadata?.[metadataTemplate.scope]?.[metadataTemplate.templateKey]);
+    const templateInstance = hasTemplateData ? getTemplateInstance(metadataTemplate, selectedItems) : null;

93-124: Render metadata components only when a valid template instance exists; prefer undefined over null for optional props

Avoid rendering the editor/view when templateInstance is null and use undefined instead of null for optional function props to satisfy stricter typings.

Apply this diff:

-                <div className="bce-MetadataSidePanel-content">
-                    <AutofillContextProvider fetchSuggestions={null} isAiSuggestionsFeatureEnabled={false}>
-                        {isEditing ? (
+                <div className="bce-MetadataSidePanel-content">
+                    <AutofillContextProvider isAiSuggestionsFeatureEnabled={false}>
+                        {templateInstance && (isEditing ? (
                             <MetadataInstanceForm
                                 areAiSuggestionsAvailable={false}
                                 isAiSuggestionsFeatureEnabled={false}
                                 isBetaLanguageEnabled={false}
                                 isDeleteButtonDisabled={true}
                                 isDeleteConfirmationModalCheckboxEnabled={false}
                                 isLargeFile={false}
                                 isMultilevelTaxonomyFieldEnabled={false}
                                 isUnsavedChangesModalOpen={isUnsavedChangesModalOpen}
                                 selectedTemplateInstance={templateInstance}
                                 onCancel={handleMetadataInstanceFormCancel}
                                 onChange={handleMetadataInstanceFormChange}
-                                onDelete={null}
+                                onDelete={undefined}
                                 onDiscardUnsavedChanges={handleMetadataInstanceFormDiscardUnsavedChanges}
                                 onSubmit={handleMetadataInstanceFormSubmit}
                                 setIsUnsavedChangesModalOpen={setIsUnsavedChangesModalOpen}
-                                taxonomyOptionsFetcher={null}
+                                taxonomyOptionsFetcher={undefined}
                             />
-                        ) : (
+                        ) : (
                             <MetadataInstance
                                 areAiSuggestionsAvailable={false}
                                 isAiSuggestionsFeatureEnabled={false}
                                 isBetaLanguageEnabled={false}
                                 onEdit={handleMetadataInstanceEdit}
                                 templateInstance={templateInstance}
-                                taxonomyNodeFetcher={null}
+                                taxonomyNodeFetcher={undefined}
                             />
-                        )}
+                        )}
                     </AutofillContextProvider>
                 </div>

85-90: A11y: Update “Close” button aria-label

The X button currently uses the “Clear selection” label but actually closes the panel. Replace the clearSelection key with the existing close key:

< IconButton
-   aria-label={formatMessage(messages.clearSelection)}
+   aria-label={formatMessage(messages.close)}
    icon={XMark}
    onClick={onClose}
    size="large"
/>

The close key is already defined in src/elements/common/messages.js, so no additional localization is needed.

🧹 Nitpick comments (6)
src/elements/content-explorer/ContentExplorer.tsx (2)

1666-1670: Guard toggle against empty selection

Optional defensive check so the panel can’t be opened with an empty selection even if a caller misfires. Keeps behavior aligned with the button’s visibility logic.

Apply this diff:

-    toggleMetadataSidePanel = () => {
-        this.setState(prevState => ({
-            isMetadataSidePanelOpen: !prevState.isMetadataSidePanelOpen,
-        }));
-    };
+    toggleMetadataSidePanel = () => {
+        this.setState(prevState => {
+            const isSelectionEmpty =
+                prevState.selectedItemIds !== 'all' && prevState.selectedItemIds.size === 0;
+            return isSelectionEmpty
+                ? { isMetadataSidePanelOpen: false }
+                : { isMetadataSidePanelOpen: !prevState.isMetadataSidePanelOpen };
+        });
+    };

1856-1862: Render gate likely should key off current view, not defaultView

Using isDefaultViewMetadata means the side panel only renders when the initial prop defaultView is metadata, even if the user is currently in metadata view. If the intent is “panel in metadata view,” check view === VIEW_METADATA.

Apply this diff:

-                            {isDefaultViewMetadata && isMetadataViewV2Feature && isMetadataSidePanelOpen && (
+                            {view === VIEW_METADATA && isMetadataViewV2Feature && isMetadataSidePanelOpen && (

If this was intentional, please confirm the UX requirement (panel only in default metadata view) and ignore.

src/elements/content-explorer/MetadataSidePanel.scss (1)

6-12: Avoid brittle targeting by data-target-id where possible

Hiding [data-target-id='TextButton-deleteButton'] can be fragile if the internal component structure changes. Prefer explicit props to disable delete actions if the metadata editor exposes them (you’re already using isDeleteButtonDisabled for the form). If the view component lacks such a prop, consider a follow-up to add one rather than relying on DOM targeting.

I can help check the metadata-editor surface for a supported prop or sketch an API addition if needed.

src/elements/content-explorer/__tests__/ContentExplorer.test.tsx (1)

489-507: Consider asserting panel open after clicking “Metadata”

You validate the button appears after selection; adding an assertion that clicking it renders the side panel would increase confidence in the integration.

I can add a follow-up test that clicks “Metadata” and expects the panel title (“Metadata”) and selected item text to be visible.

src/elements/content-explorer/MetadataSidePanel.tsx (2)

79-83: A11y: mark decorative icon as hidden

The file icon is purely visual; add aria-hidden so it isn’t announced by screen readers.

Apply this diff:

-                        <FileDefault />
+                        <FileDefault aria-hidden="true" />

56-69: Track TODOs: onChange/onSubmit multi-select support

Open items remain:

  • Implement form onChange/onSubmit
  • Support displaying values for multiple selected items

Flagging to ensure they’re tracked before feature rollout.

I can help scaffold the onSubmit flow against metadata-query APIs and outline a multi-select merge strategy (e.g., show “mixed” states with partial values).

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 33cf986 and 057f5b8.

📒 Files selected for processing (7)
  • src/elements/common/sub-header/SubHeaderRight.tsx (5 hunks)
  • src/elements/content-explorer/ContentExplorer.scss (1 hunks)
  • src/elements/content-explorer/ContentExplorer.tsx (9 hunks)
  • src/elements/content-explorer/MetadataSidePanel.scss (1 hunks)
  • src/elements/content-explorer/MetadataSidePanel.tsx (1 hunks)
  • src/elements/content-explorer/__tests__/ContentExplorer.test.tsx (3 hunks)
  • src/elements/content-explorer/__tests__/MetadataSidePanel.test.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/elements/common/sub-header/SubHeaderRight.tsx
  • src/elements/content-explorer/ContentExplorer.scss
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-06-17T13:30:02.172Z
Learnt from: rafalmaksymiuk
PR: box/box-ui-elements#4144
File: src/elements/content-sidebar/versions/VersionsList.js:24-33
Timestamp: 2025-06-17T13:30:02.172Z
Learning: In the box-ui-elements codebase, Flow components use .flow.js type definition files, not TypeScript .ts files. The InternalSidebarNavigation type is a union type where different variants may have different properties like versionId, and proper type safety is ensured through conditional checks in methods like getSelectedVersionId.

Applied to files:

  • src/elements/content-explorer/__tests__/MetadataSidePanel.test.tsx
📚 Learning: 2025-06-25T13:10:19.128Z
Learnt from: rafalmaksymiuk
PR: box/box-ui-elements#4160
File: src/elements/content-sidebar/SidebarToggle.js:11-11
Timestamp: 2025-06-25T13:10:19.128Z
Learning: In the box-ui-elements codebase, Flow files support `import type` syntax for type-only imports. The `import type` statement is valid Flow syntax, not just TypeScript, and should not be flagged as an error when used in Flow files (indicated by flow comments).

Applied to files:

  • src/elements/content-explorer/__tests__/MetadataSidePanel.test.tsx
🧬 Code Graph Analysis (3)
src/elements/content-explorer/__tests__/MetadataSidePanel.test.tsx (2)
src/elements/content-explorer/MetadataSidePanel.tsx (1)
  • MetadataSidePanelProps (24-29)
src/elements/content-explorer/ContentExplorer.tsx (1)
  • render (1689-1960)
src/elements/content-explorer/MetadataSidePanel.tsx (1)
src/elements/content-explorer/utils.ts (2)
  • useSelectedItemText (11-29)
  • getTemplateInstance (32-58)
src/elements/content-explorer/__tests__/ContentExplorer.test.tsx (3)
src/elements/content-explorer/ContentExplorer.tsx (1)
  • ContentExplorer (1963-1963)
src/elements/common/__mocks__/mockMetadata.ts (1)
  • mockSchema (226-226)
src/features/metadata-based-view/__tests__/MetadataBasedItemList.test.js (1)
  • fieldsToShow (102-107)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: lint_test_build
  • GitHub Check: Summary
🔇 Additional comments (4)
src/elements/content-explorer/ContentExplorer.tsx (2)

1654-1658: Good: clearing selection also closes the panel

Resetting selectedItemIds and closing the side panel together keeps UI state consistent.


1779-1854: Nice layout consolidation and SUIT naming

The bce-ContentExplorer-main wrapper and threaded props (selection state, toggle handlers) are clean and align with the SUIT convention previously requested.

src/elements/content-explorer/MetadataSidePanel.scss (1)

1-4: SCSS looks good

Subtitle container styles are straightforward and local.

src/elements/content-explorer/__tests__/ContentExplorer.test.tsx (1)

82-88: Good: pass features through FeatureProvider and component props

Propagating features consistently allows exercising the v2 codepaths in tests.

Copy link
Contributor

@tjuanitas tjuanitas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor comments otherwise looks good

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/elements/content-explorer/ContentExplorer.tsx (1)

1566-1573: Fix conditional spread that can spread a boolean (TS compile error)

The pattern ...(isSelectionEmpty && { ... }) spreads a boolean when false. TypeScript rejects spreading non-object values.

Apply this diff:

-                    const isSelectionEmpty = ids !== 'all' && ids.size === 0;
-                    this.setState({
-                        selectedItemIds: ids,
-                        ...(isSelectionEmpty && { isMetadataSidePanelOpen: false }),
-                    });
+                    const isSelectionEmpty = ids !== 'all' && ids.size === 0;
+                    this.setState({
+                        selectedItemIds: ids,
+                        ...(isSelectionEmpty ? { isMetadataSidePanelOpen: false } : {}),
+                    });
🧹 Nitpick comments (4)
src/elements/common/sub-header/SubHeaderRight.tsx (2)

68-68: Use a TS-narrowing-friendly condition for hasSelectedItems

The current OR check doesn’t narrow selectedItemIds to Set on the right-hand side, which can surface TS errors for .size. Prefer instanceof Set or an &&-based narrowing to keep types sound and avoid relying on non-narrowing OR semantics.

Apply this diff:

-    const hasSelectedItems: boolean = !!(selectedItemIds && (selectedItemIds === 'all' || selectedItemIds.size > 0));
+    const hasSelectedItems =
+        selectedItemIds === 'all' || (selectedItemIds instanceof Set && selectedItemIds.size > 0);

99-103: Guard against missing click handler or make it explicit

Button renders gated by feature flag and selection, but onMetadataSidePanelToggle is optional. If this component is reused elsewhere, a missing handler yields a no-op button. Either gate on the presence of the handler or default it to a noop to make intent explicit.

Example options:

  • Gate rendering:
-{isMetadataView && isMetadataViewV2Feature && hasSelectedItems && (
+{isMetadataView && isMetadataViewV2Feature && hasSelectedItems && onMetadataSidePanelToggle && (
  • Or default in destructuring:
-    onMetadataSidePanelToggle,
+    onMetadataSidePanelToggle = () => {},
src/elements/content-explorer/ContentExplorer.tsx (2)

1760-1761: Nit: compute and reuse isMetadataView

For readability and consistency with SubHeaderRight, consider computing isMetadataView (view === VIEW_METADATA) once and reusing it in render conditions.

Example:

-        const isDefaultViewMetadata: boolean = defaultView === DEFAULT_VIEW_METADATA;
-        const isMetadataViewV2Feature = isFeatureEnabled(features, 'contentExplorer.metadataViewV2');
+        const isDefaultViewMetadata = defaultView === DEFAULT_VIEW_METADATA;
+        const isMetadataViewV2Feature = isFeatureEnabled(features, 'contentExplorer.metadataViewV2');
+        const isMetadataView = view === VIEW_METADATA;

1855-1862: Gate side panel on current view, not default view

Rendering the panel based on defaultView can keep it visible when the user navigates away from metadata view. Tie it to the current view instead, so the panel only shows in VIEW_METADATA.

Apply this diff:

-                            {isDefaultViewMetadata && isMetadataViewV2Feature && isMetadataSidePanelOpen && (
+                            {view === VIEW_METADATA && isMetadataViewV2Feature && isMetadataSidePanelOpen && (
                                 <MetadataSidePanel
                                     currentCollection={currentCollection}
                                     onClose={this.closeMetadataSidePanel}
                                     metadataTemplate={metadataTemplate}
                                     selectedItemIds={selectedItemIds}
                                 />
                             )}

Optionally, also close the panel when leaving metadata view to keep state consistent:

-        } else if (view === VIEW_METADATA) {
+        } else if (view === VIEW_METADATA) {
             this.showMetadataQueryResults();
+        } else if (this.state.isMetadataSidePanelOpen) {
+            this.setState({ isMetadataSidePanelOpen: false });
         }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 057f5b8 and 23d8f1c.

📒 Files selected for processing (5)
  • src/elements/common/sub-header/SubHeader.tsx (3 hunks)
  • src/elements/common/sub-header/SubHeaderRight.tsx (5 hunks)
  • src/elements/content-explorer/ContentExplorer.tsx (9 hunks)
  • src/elements/content-explorer/MetadataSidePanel.tsx (1 hunks)
  • src/elements/content-explorer/__tests__/MetadataSidePanel.test.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/elements/content-explorer/MetadataSidePanel.tsx
  • src/elements/content-explorer/tests/MetadataSidePanel.test.tsx
  • src/elements/common/sub-header/SubHeader.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: lint_test_build
  • GitHub Check: Summary
🔇 Additional comments (10)
src/elements/common/sub-header/SubHeaderRight.tsx (3)

5-5: LGTM: correct type-only import for Selection

Type-only import from react-aria-components is appropriate and keeps bundles lean.


31-31: LGTM: new toggle handler prop is well-scoped

Optional onMetadataSidePanelToggle prop looks good and matches usage downstream.


35-35: LGTM: wiring selectedItemIds as Selection

Prop aligns with react-aria Selection union and supports 'all' and Set cases.

src/elements/content-explorer/ContentExplorer.tsx (7)

27-27: LGTM: side panel import

Importing MetadataSidePanel here is expected; keeps ContentExplorer the integration point.


173-173: LGTM: add isMetadataSidePanelOpen to state

State flag is clearly named and scoped.


299-299: LGTM: initialize panel closed

Defaulting isMetadataSidePanelOpen to false is correct.


1654-1658: LGTM: clear selection also closes panel

Good UX to close the panel when selection is cleared.


1666-1670: LGTM: toggle handler

Simple and side-effect free toggle implementation. Consider also closing on navigation away from metadata view (see next comment).


1780-1808: LGTM: header/subheader/prop threading and new main wrapper

The new bce-ContentExplorer-main wrapper and threading of onMetadataSidePanelToggle and selectedItemIds through SubHeader look consistent with the new panel behavior.


1555-1576: Rename verified – no remaining onToggleMetadataSidePanel references
Ripgrep confirmed only the new onMetadataSidePanelToggle occurrences in the following locations:

  • src/elements/content-explorer/ContentExplorer.tsx (method definition and prop usage)
  • src/elements/common/sub-header/SubHeader.tsx (interface and JSX)
  • src/elements/common/sub-header/SubHeaderRight.tsx (interface and JSX)

No action needed.

@mergify mergify bot merged commit 4f89946 into box:master Aug 18, 2025
7 checks passed
bfoxx1906 pushed a commit to bfoxx1906/box-ui-elements that referenced this pull request Aug 19, 2025
* feat(metadata-view): Implement metadata sidepanel

* feat(metadata-view): resolve comments

* feat(metadata-view): resolve comments

* feat(metadata-view): resolve nits
mergify bot pushed a commit that referenced this pull request Aug 22, 2025
* feat(timestamped-comments): feature enabled timesetamp support

* feat(timestamped-comments): updating stories and adding toggle form

* feat(timestamped-comments): fixing flow errors

* feat(timestamped-comments): removing unnecessary prop

* feat(timestamped-comments): removing formatting

* feat(timestamped-comments): removing async modifier

* feat(timestamped-comments): removing unnecssary import

* feat(timestamped-comments): passing down file prop to BaseComment

* feat(metadata-instance-editor): remove ai agent selector split and fi… (#4226)

* feat(metadata-instance-editor): remove ai agent selector split and fix styling

* fix: tests

* fix: tests

* feat: use new box ai agent selector with alignment

* feat(metadata-view): Implement metadata sidepanel (#4230)

* feat(metadata-view): Implement metadata sidepanel

* feat(metadata-view): resolve comments

* feat(metadata-view): resolve comments

* feat(metadata-view): resolve nits

* feat(timestamped-comments): addressing PR feedback

* feat(timestamped-comments): addressing PR comments

* feat(timestamped-comments): addressing PR feedback

* feat(timestamped-comments): addressing PR feedback

* feat(timestamped-comments): fixing flow error

* feat(timestamped-comments): addressing PR feedback

* feat(timestamped-comments): fixing unit tests

---------

Co-authored-by: jlandisBox <[email protected]>
Co-authored-by: Jerry Jiang <[email protected]>
greg-in-a-box pushed a commit to greg-in-a-box/box-ui-elements that referenced this pull request Aug 22, 2025
…#4228)

* feat(timestamped-comments): feature enabled timesetamp support

* feat(timestamped-comments): updating stories and adding toggle form

* feat(timestamped-comments): fixing flow errors

* feat(timestamped-comments): removing unnecessary prop

* feat(timestamped-comments): removing formatting

* feat(timestamped-comments): removing async modifier

* feat(timestamped-comments): removing unnecssary import

* feat(timestamped-comments): passing down file prop to BaseComment

* feat(metadata-instance-editor): remove ai agent selector split and fi… (box#4226)

* feat(metadata-instance-editor): remove ai agent selector split and fix styling

* fix: tests

* fix: tests

* feat: use new box ai agent selector with alignment

* feat(metadata-view): Implement metadata sidepanel (box#4230)

* feat(metadata-view): Implement metadata sidepanel

* feat(metadata-view): resolve comments

* feat(metadata-view): resolve comments

* feat(metadata-view): resolve nits

* feat(timestamped-comments): addressing PR feedback

* feat(timestamped-comments): addressing PR comments

* feat(timestamped-comments): addressing PR feedback

* feat(timestamped-comments): addressing PR feedback

* feat(timestamped-comments): fixing flow error

* feat(timestamped-comments): addressing PR feedback

* feat(timestamped-comments): fixing unit tests

---------

Co-authored-by: jlandisBox <[email protected]>
Co-authored-by: Jerry Jiang <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants