Skip to content

Conversation

@ahorowitz123
Copy link
Contributor

@ahorowitz123 ahorowitz123 commented Sep 10, 2025

Portal metadata editor onto preview body so dropdown options don't get cut off

Before:
Screenshot 2025-09-10 at 5 31 27 PM

After:
Screenshot 2025-09-10 at 5 29 16 PM

Summary by CodeRabbit

  • New Features
    • Exposed the preview body element to downstream components to improve integrations between preview and sidebar.
  • Bug Fixes
    • Fixed metadata dropdown positioning so long options display fully and align correctly.
    • Prevented sidebar overlap and improved sidebar layout stability and interactions.
    • Moved reload notification inside the content area for clearer visibility.
  • Refactor
    • Reworked preview and sidebar structure to improve layout consistency and element anchoring.
  • Tests
    • Added a visual Storybook test to validate metadata dropdown behavior and positioning.

@ahorowitz123 ahorowitz123 requested review from a team as code owners September 10, 2025 21:23
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 10, 2025

Walkthrough

Listen up! Adds a new PreviewContext exposing a previewBodyRef, provides it from ContentPreview (attached to bcpr-body), passes the ref.current into MetadataInstanceEditor → MetadataInstanceForm, adjusts ContentPreview layout (sidebar/reload placement), and adds a Storybook visual test for metadata dropdown positioning. (39 words)

Changes

Cohort / File(s) Summary of Changes
Preview Context
src/elements/content-preview/PreviewContext.ts
New React context PreviewContext and exported interface PreviewContextType with previewBodyRef: React.RefObject<HTMLDivElement>; sets displayName and default exports the context.
ContentPreview provider & layout
src/elements/content-preview/ContentPreview.js
Adds previewBodyRef = React.createRef(), attaches ref={this.previewBodyRef} to bcpr-body, wraps providers with PreviewContext.Provider (providing the ref object), moves LoadableSidebar outside the inner container, relocates ReloadNotification inside the top-level content container, and updates inner bcpr-content rendering (Measure wrapper when file present).
Metadata editor wiring
src/elements/content-sidebar/MetadataInstanceEditor.tsx
Imports useContext and PreviewContext; reads previewBodyRef via useContext(PreviewContext), derives customRef = previewContext?.previewBodyRef?.current, and passes customRef into MetadataInstanceForm (customRef={customRef}).
Storybook visual test
src/elements/content-preview/stories/tests/ContentPreview-metadata-visual.stories.js
New visual story metadataDropdownPositioning with MSW handlers mocking v2 endpoints and a play interaction that opens the long-option metadata dropdown to verify positioning; default story export configured with metadata-enabled sidebar args.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant U as User
    participant CP as ContentPreview
    participant PCtx as PreviewContext.Provider
    participant MIE as MetadataInstanceEditor
    participant MIF as MetadataInstanceForm

    U->>CP: Mount ContentPreview
    CP->>PCtx: Provide { previewBodyRef } (ref object)
    Note right of CP #D3E4CD: `bcpr-body` attached to previewBodyRef

    PCtx-->>MIE: useContext() -> previewBodyRef (ref object)
    MIE->>MIF: Render with customRef = previewBodyRef?.current
    MIF->>CP: Portal dropdowns / anchors to preview body element as needed
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

ready-to-merge

Suggested reviewers

  • jpan-box
  • jfox-box

Pre-merge checks (3 passed)

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed I pity the fool who can't see this title is on point: "fix(metadataeditor): portal dropdown to preview container" succinctly describes portaling the metadata editor dropdown to the preview area to avoid clipping, which matches the PR's primary change. It follows conventional-commit style and names the affected area (metadataeditor) so reviewers scanning history will understand the intent. The title is concise, specific, and appropriate for merge-history usage.
Description Check ✅ Passed I pity the fool who skips the description: it clearly states the intent to portal the metadata editor onto the preview body to prevent dropdown clipping and includes before/after screenshots that visually demonstrate the fix. The repository's template is included as an HTML comment and does not demand additional structured sections, and the current text supplies a problem statement, the change made, and visual verification for reviewers. This is sufficient for reviewers to understand and assess the change, though a short testing checklist or related-issue link would be helpful.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

Poem

I pity the fool who skips this patch, ha!
A body ref now points the preview, snap!
Metadata finds its portal true,
Dropdowns pop where they ought to do,
Ship it strong — no more UI scrap!


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a41ce3b and 1c5dc1a.

📒 Files selected for processing (1)
  • src/elements/content-preview/ContentPreview.js (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/elements/content-preview/ContentPreview.js (2)
src/elements/content-preview/preview-header/PreviewHeader.js (1)
  • PreviewHeader (51-177)
src/elements/content-preview/PreviewMask.tsx (1)
  • PreviewMask (13-23)
🪛 Biome (2.1.2)
src/elements/content-preview/ContentPreview.js

[error] 217-217: Parenthesized expression didnt contain anything

Expected an expression here

(parse)

⏰ 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-preview/ContentPreview.js (5)

29-30: Import looks solid, fool.

Default import of PreviewContext from the TS module is fine. No blockers here.


1337-1362: Provider placement LGTM, fool.

Wrapping with PreviewContext.Provider and passing the ref object (not .current) is the right call for downstream consumers. Stable identity via a class field is good.


1363-1387: Portal target wiring: guard for null on first render, sucka.

ref={this.previewBodyRef} on .bcpr-body is correct, but consumers portaling into previewBodyRef.current must handle null until mount.

Confirm Metadata* consumers do a null-check before createPortal (e.g., if (!previewBodyRef?.current) return null;). If not, I can draft the patch.


1389-1408: Sidebar relocation: sanity-check z-index/pointer-events, fool.

Moving LoadableSidebar outside .bcpr-container but inside .bcpr-body can alter stacking contexts. Verify dropdowns from the metadata editor still overlay the sidebar and preview controls as intended.

Smoke-test with the new Storybook visual and a file with annotations/toolbars open to confirm layering (dropdown over preview and sidebar) across common themes and zoom levels.


1411-1414: ReloadNotification placement LGTM.

Lives outside .bcpr-body, won’t interfere with the portal target. Clean.

✨ 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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@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: 2

🧹 Nitpick comments (5)
src/elements/content-preview/PreviewContext.ts (1)

3-9: Pass a stable ref object through context, fool!

Passing the element value risks stale/null reads and extra re-renders. Share the RefObject instead so consumers always read the latest via .current. I pity the stale ref!

Apply:

-export interface PreviewContextType {
-    previewBodyRef: HTMLDivElement | null;
-}
+export interface PreviewContextType {
+    previewBodyRef: React.RefObject<HTMLDivElement>;
+}
 
-const PreviewContext = React.createContext<PreviewContextType | null>(null);
+const PreviewContext = React.createContext<PreviewContextType | null>(null);
+PreviewContext.displayName = 'PreviewContext';
src/elements/content-sidebar/MetadataInstanceEditor.tsx (1)

55-57: Use .current if context provides a RefObject

If PreviewContext switches to provide a RefObject, grab the element via .current before passing to MetadataInstanceForm. Don’t make your portals homeless, sucker!

Please confirm MetadataInstanceForm’s customRef prop expects HTMLElement | null.

-const previewContext: PreviewContextType | null = useContext(PreviewContext);
-const customRef = previewContext?.previewBodyRef;
+const previewContext: PreviewContextType | null = useContext(PreviewContext);
+const customRef = previewContext?.previewBodyRef?.current;

 ...
-            customRef={customRef}
+            customRef={customRef}

Also applies to: 78-79

src/elements/content-preview/ContentPreview.js (2)

29-30: Flow/TS interop nit: add suppression for TS import

This file is Flow; PreviewContext.ts is TS. For consistency with nearby imports, add a Flow suppression to avoid type tooling grief.

-// $FlowFixMe TypeScript file
-import ThemingStyles from '../common/theming';
+// $FlowFixMe TypeScript file
+import ThemingStyles from '../common/theming';
+// $FlowFixMe TypeScript file
+import PreviewContext from './PreviewContext';

1334-1336: Provide the RefObject via context, not .current

Passing .current locks the value to whatever it was at render time and forces extra updates. Pass the ref object; consumers read .current when needed. That’s how you keep it fresh, fool.

-                    <PreviewContext.Provider value={{ previewBodyRef: this.previewBodyRef.current }}>
+                    <PreviewContext.Provider value={{ previewBodyRef: this.previewBodyRef }}>

Optional perf nit: cache the provider value on this to avoid a new object each render.

src/elements/content-preview/stories/tests/ContentPreview-metadata-visual.stories.js (1)

1-3: Add an assertion that the dropdown is portaled inside preview body

Prove the fix by asserting the listbox lives under .bcpr-body. Don’t just click — verify, sucka!

-import { userEvent, within } from 'storybook/test';
+import { userEvent, within, expect } from 'storybook/test';
 ...
     play: async ({ canvasElement }) => {
         const canvas = within(canvasElement);

         const editButton = await canvas.findByRole('button', { name: 'Edit Long Dropdown Test Template' });
         await userEvent.click(editButton);

         const dropdownField = await canvas.findByRole('combobox', { name: 'Department Selection' });
         await userEvent.click(dropdownField);

-        await canvas.findByRole('option', {
+        const option = await canvas.findByRole('option', {
             name: 'Engineering - Software Development - Frontend React TypeScript Team Alpha Division',
         });
+        const listbox = option.closest('[role="listbox"]');
+        expect(listbox).not.toBeNull();
+        expect(listbox?.closest('.bcpr-body')).not.toBeNull();
     },

Also applies to: 141-153

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 681bace and 12f6672.

📒 Files selected for processing (4)
  • src/elements/content-preview/ContentPreview.js (3 hunks)
  • src/elements/content-preview/PreviewContext.ts (1 hunks)
  • src/elements/content-preview/stories/tests/ContentPreview-metadata-visual.stories.js (1 hunks)
  • src/elements/content-sidebar/MetadataInstanceEditor.tsx (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/elements/content-sidebar/MetadataInstanceEditor.tsx (1)
src/elements/content-preview/PreviewContext.ts (1)
  • PreviewContextType (3-5)
src/elements/content-preview/stories/tests/ContentPreview-metadata-visual.stories.js (1)
src/elements/common/__mocks__/mockRequests.ts (2)
  • mockUserRequest (10-30)
  • mockEventRequest (5-8)
src/elements/content-preview/ContentPreview.js (3)
src/features/message-preview-content/MessagePreviewContent.js (1)
  • previewRef (40-40)
src/elements/content-preview/PreviewMask.tsx (1)
  • PreviewMask (13-23)
src/elements/content-preview/PreviewNavigation.js (1)
  • PreviewNavigation (153-161)
🪛 Biome (2.1.2)
src/elements/content-preview/ContentPreview.js

[error] 216-216: return types can only be used in TypeScript files

remove this type annotation

(parse)

⏰ 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

Copy link
Contributor

@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-preview/ContentPreview.js (1)

217-218: Fix TS generic in Flow/JS file to resolve Biome parse error

Biome chokes here (“Parenthesized expression didn’t contain anything”). Drop the TypeScript generic; keep it plain JS. I pity the fool who lets the build fail!

Apply this diff:

-    previewBodyRef = React.createRef<HTMLDivElement>();
+    previewBodyRef = React.createRef();
🧹 Nitpick comments (1)
src/elements/content-preview/ContentPreview.js (1)

1335-1337: Stabilize PreviewContext value to avoid unnecessary re-renders

You’re creating a new object every render. Memoize the provider value so consumers don’t re-render for no reason, fool.

Apply this diff here:

-                    <PreviewContext.Provider value={{ previewBodyRef: this.previewBodyRef }}>
+                    <PreviewContext.Provider value={this.previewContextValue}>

And add this class field (right after the previewBodyRef field) so the identity is stable:

previewContextValue = { previewBodyRef: this.previewBodyRef };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 12f6672 and e4728a5.

📒 Files selected for processing (3)
  • src/elements/content-preview/ContentPreview.js (3 hunks)
  • src/elements/content-preview/PreviewContext.ts (1 hunks)
  • src/elements/content-sidebar/MetadataInstanceEditor.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/elements/content-preview/PreviewContext.ts
  • src/elements/content-sidebar/MetadataInstanceEditor.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
src/elements/content-preview/ContentPreview.js (1)
src/elements/content-preview/PreviewMask.tsx (1)
  • PreviewMask (13-23)
🪛 Biome (2.1.2)
src/elements/content-preview/ContentPreview.js

[error] 217-217: Parenthesized expression didnt contain anything

Expected an expression here

(parse)

⏰ 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). (1)
  • GitHub Check: Summary
🔇 Additional comments (4)
src/elements/content-preview/ContentPreview.js (4)

29-31: LGTM: PreviewContext import is clean

Import looks good with the Flow suppression. Rock solid, fool.


1361-1373: LGTM: Ref wiring and Measure usage

Hooking bcpr-body to the ref and wrapping bcpr-content with Measure looks right. Smooth move, sucka.


1387-1405: Sidebar relocation: verify stacking and hover behavior

Moving LoadableSidebar outside bcpr-container may tweak stacking/hover regions. Please sanity-check:

  • z-index over preview content and dropdowns
  • nav arrow visibility logic when hovering sidebar (onMouseMove is on bcpr-container)

Don’t ship surprises, fool.


1409-1412: ReloadNotification placement change: verify layering

After moving it below bcpr-body, confirm it still overlays preview and is clickable above masks/sidebars.

@bfoxx1906 bfoxx1906 self-requested a review September 17, 2025 16:27
Copy link
Collaborator

@bfoxx1906 bfoxx1906 left a comment

Choose a reason for hiding this comment

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

looks straight forward.

@bfoxx1906 bfoxx1906 self-requested a review September 17, 2025 16:28
@mergify mergify bot merged commit 540a957 into box:master Sep 17, 2025
7 checks passed
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