Skip to content

UI: Fix code/copy buttons overlap with content#33889

Merged
Sidnioulz merged 10 commits intonextfrom
sidnioulz/issue-23642
Mar 6, 2026
Merged

UI: Fix code/copy buttons overlap with content#33889
Sidnioulz merged 10 commits intonextfrom
sidnioulz/issue-23642

Conversation

@Sidnioulz
Copy link
Copy Markdown
Member

@Sidnioulz Sidnioulz commented Feb 20, 2026

Closes #23642

This is the proper fix for the PR I had to revert earlier today (#33877).

What I did

Implemented the following design changes:

  • Reworked the styling and position of Preview actions
  • Moved the code copy button up into Preview actions
  • Fixed up other use cases of SyntaxHighlighter to have a flex-wrap layout where the copy button is guaranteed not to overlap with content

Checklist for Contributors

Testing

A story was added to showcase the wrapping in preview.

The changes in this PR are covered in the following automated tests:

  • stories
  • unit tests
  • integration tests
  • end-to-end tests

Manual testing

  1. Open Storybook, go to preview stories
  2. Play around with container width

Documentation

ø

Checklist for Maintainers

  • When this PR is ready for testing, make sure to add ci:normal, ci:merged or ci:daily GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in code/lib/cli-storybook/src/sandbox-templates.ts

  • Make sure this PR contains one of the labels below:

    Available labels
    • bug: Internal changes that fixes incorrect behavior.
    • maintenance: User-facing maintenance tasks.
    • dependencies: Upgrading (sometimes downgrading) dependencies.
    • build: Internal-facing build tooling & test updates. Will not show up in release changelog.
    • cleanup: Minor cleanup style change. Will not show up in release changelog.
    • documentation: Documentation only changes. Will not show up in release changelog.
    • feature request: Introducing a new feature.
    • BREAKING CHANGE: Changes that break compatibility in some way with current major version.
    • other: Changes that don't fit in the above categories.

🦋 Canary release

This PR does not have a canary release associated. You can request a canary release of this pull request by mentioning the @storybookjs/core team here.

core team members can create a canary release here or locally with gh workflow run --repo storybookjs/storybook publish.yml --field pr=<PR_NUMBER>

Summary by CodeRabbit

  • New Features

    • New mobile preview example showcasing long-button wrapping
    • Added opt-in copyable control for source blocks
    • Action bar flexible layout mode for alternative positioning
  • Bug Fixes

    • Copy-to-clipboard shows "Copied!" confirmation
    • Disabled copy UI when no code is available
  • Improvements

    • Simplified source rendering and expanded/error handling
    • Improved accessibility attributes and toolbar behavior

@nx-cloud
Copy link
Copy Markdown

nx-cloud bot commented Feb 20, 2026

View your CI Pipeline Execution ↗ for commit 2f69d12

Command Status Duration Result
nx run-many -t compile,check,knip,test,pretty-d... ❌ Failed 9m 52s View ↗

☁️ Nx Cloud last updated this comment at 2026-02-27 11:59:03 UTC

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 20, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Refactors Preview/source rendering and action bar layout: adds ActionBar flexLayout, inlines action bar markup, replaces legacy source handling with hasValidSource/hasSourceError flags, adds copyable prop to Source and copy-to-clipboard feedback, and updates SyntaxHighlighter layout to support flexible action bar placement.

Changes

Cohort / File(s) Summary
Preview component
code/addons/docs/src/blocks/components/Preview.tsx
Reworked rendering: removed legacy getSource and Relative wrapper; added ActionBar styled component, sourceId/useId, hasValidSource/hasSourceError flags, simplified border radius logic, added copy feedback state and handleCopyCode, and wired additionalActions.
Preview story
code/addons/docs/src/blocks/components/Preview.stories.tsx
Added exported story ActionBarWrapping that renders a Preview with a long-text Button and sets globals.viewport to mobile1 to illustrate action bar wrapping.
ActionBar component
code/core/src/components/components/ActionBar/ActionBar.tsx
Introduced public prop flexLayout?: boolean (default false); container styling supports inline-flex/flex alignment when true or absolute positioning when false; component signature updated to accept flexLayout.
SyntaxHighlighter integration
code/core/src/components/components/syntaxhighlighter/syntaxhighlighter.tsx
Changed wrapper to use display:flex and flex-wrap; adjusted scroller sizing; when copyable is enabled, passes flexLayout to ActionBar to switch layout mode.
Source component
code/addons/docs/src/blocks/components/Source.tsx
Added public prop copyable?: boolean (default true) to SourceCodeProps and passed it through to the StyledSyntaxHighlighter to optionally disable copy UI.

Sequence Diagram(s)

sequenceDiagram
  participant Story as Storybook Story
  participant Preview as Preview Component
  participant ActionBar as ActionBar
  participant Source as Source / SyntaxHighlighter
  participant Clipboard as Clipboard

  Story->>Preview: render Preview (with additionalActions)
  Preview->>ActionBar: render toolbar (flexLayout conditional)
  Preview->>Source: request source content / expanded state
  Source-->>Preview: return source or error
  Note over Preview,Source: hasValidSource / hasSourceError determine rendering
  ActionBar->>Clipboard: (user) click Copy
  Clipboard-->>ActionBar: copyToClipboard result
  ActionBar->>Preview: set copied state ("Copied!" feedback)
  Preview->>Story: update UI (Copied badge / source visibility)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • #33877 — Modifies Preview and SyntaxHighlighter render/layout, touching similar ActionBar/RelativeActionBar changes.
  • #33420 — Changes ActionBar/RelativeActionBar and copy behavior in SyntaxHighlighter/Preview, overlapping with this PR's layout and copy UI edits.

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

Copy link
Copy Markdown
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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@code/core/src/components/components/ActionBar/ActionBar.stories.tsx`:
- Around line 30-32: The Storybook meta uses args.sb11FlexLayout which doesn't
match ActionBar's prop flexLayout and the stories don't pass args through the
render so toggling has no effect; rename the meta arg from sb11FlexLayout to
flexLayout and update the story renders (the story function that returns
<ActionBar ...>) to accept and spread args (e.g., (args) => <ActionBar {...args}
/>) so the flexLayout arg is forwarded to the ActionBar component.

@storybook-app-bot
Copy link
Copy Markdown

storybook-app-bot bot commented Feb 20, 2026

Package Benchmarks

Commit: 2f69d12, ran on 27 February 2026 at 12:00:48 UTC

The following packages have significant changes to their size or dependencies:

@storybook/addon-docs

Before After Difference
Dependency count 18 18 0
Self size 1.64 MB 1.66 MB 🚨 +20 KB 🚨
Dependency size 9.25 MB 9.25 MB 🚨 +12 B 🚨
Bundle Size Analyzer Link Link

@Sidnioulz Sidnioulz force-pushed the sidnioulz/issue-23642 branch from 0516ea7 to 6700746 Compare February 23, 2026 12:59
Copy link
Copy Markdown
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.

♻️ Duplicate comments (1)
code/core/src/components/components/ActionBar/ActionBar.stories.tsx (1)

30-32: Story args don’t reach ActionBar (same as prior review).

sb11FlexLayout doesn’t match the ActionBar prop flexLayout, and the stories don’t spread args into <ActionBar>, so the control has no effect. Align the arg name and pass args through.

🔧 Suggested fix
-import { ActionBar } from './ActionBar';
+import { ActionBar, type ActionBarProps } from './ActionBar';

 export default {
   component: ActionBar,
   ...
-  args: {
-    sb11FlexLayout: true,
-  },
+  args: {
+    flexLayout: true,
+  },
 };

-export const SingleItem = () => <ActionBar actionItems={[{ title: 'Clear', onClick: action1 }]} />;
+export const SingleItem = (args: ActionBarProps) => (
+  <ActionBar {...args} actionItems={[{ title: 'Clear', onClick: action1 }]} />
+);

-export const ManyItems = () => (
+export const ManyItems = (args: ActionBarProps) => (
   <ActionBar
+    {...args}
     actionItems={[
       { title: 'Action string', onClick: action1 },
       { title: <div>Action node</div>, onClick: action2 },
       { title: 'Long action string', className: 'long-action-button', onClick: action3 },
     ]}
   />
 );

Please also run the ESLint command for this file:

#!/bin/bash
yarn lint:js:cmd code/core/src/components/components/ActionBar/ActionBar.stories.tsx

As per coding guidelines: **/*.{js,jsx,json,html,ts,tsx,mjs}: Run ESLint checks with the command 'yarn lint:js:cmd ' and fix all errors or warnings before committing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/src/components/components/ActionBar/ActionBar.stories.tsx` around
lines 30 - 32, The story args key is wrong and the args are not forwarded to the
component: rename the arg from sb11FlexLayout to flexLayout in the Story args
and update the story render so it spreads args into the component (use ActionBar
and pass {...args} when rendering) so the control affects the component; then
run the ESLint check for this file with `yarn lint:js:cmd
code/core/src/components/components/ActionBar/ActionBar.stories.tsx` and fix any
reported issues.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@code/core/src/components/components/ActionBar/ActionBar.stories.tsx`:
- Around line 30-32: The story args key is wrong and the args are not forwarded
to the component: rename the arg from sb11FlexLayout to flexLayout in the Story
args and update the story render so it spreads args into the component (use
ActionBar and pass {...args} when rendering) so the control affects the
component; then run the ESLint check for this file with `yarn lint:js:cmd
code/core/src/components/components/ActionBar/ActionBar.stories.tsx` and fix any
reported issues.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0516ea7 and 6700746.

📒 Files selected for processing (5)
  • code/addons/docs/src/blocks/components/Preview.stories.tsx
  • code/addons/docs/src/blocks/components/Preview.tsx
  • code/core/src/components/components/ActionBar/ActionBar.stories.tsx
  • code/core/src/components/components/ActionBar/ActionBar.tsx
  • code/core/src/components/components/syntaxhighlighter/syntaxhighlighter.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • code/core/src/components/components/syntaxhighlighter/syntaxhighlighter.tsx
  • code/core/src/components/components/ActionBar/ActionBar.tsx

@Sidnioulz Sidnioulz marked this pull request as draft February 23, 2026 15:41
@Sidnioulz Sidnioulz force-pushed the sidnioulz/issue-23642 branch from 0b9644c to be2b514 Compare February 26, 2026 14:59
@Sidnioulz Sidnioulz marked this pull request as ready for review February 26, 2026 16:06
Copy link
Copy Markdown
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: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@code/addons/docs/src/blocks/components/Preview.tsx`:
- Around line 190-194: PreviewContainer's computed border/radius depends on the
isExpanded prop but the component render only spreads withSource/withToolbar and
props; it never passes the current expanded state, so toggling expanded doesn't
change styling. Fix by passing the expanded state into PreviewContainer as
isExpanded (e.g., add isExpanded={expanded} when rendering PreviewContainer) so
its internal logic (isExpanded) receives the live expanded value; ensure the
prop name matches PreviewContainer's expected prop (isExpanded) and remove any
conflicting prop in props if present.
- Around line 160-162: The component currently initializes additionalActionItems
from props using useState which freezes the value at mount and ignores prop
updates; replace the state initialization with a derived value so
additionalActionItems reflects prop changes (e.g., remove the useState usage for
additionalActionItems and compute it from the additionalActions prop each
render, optionally using useMemo if you want to avoid reallocating the array
unnecessarily), updating references to additionalActionItems in Preview.tsx to
use the derived constant.
- Around line 173-181: The handleCopyCode callback currently ignores failures
from copyToClipboard and can produce unhandled promise rejections; wrap the
copyToClipboard(...).then(...) chain with .catch(...) to handle errors, use the
logger from 'storybook/internal/client-logger' to log the error, and ensure
component state is consistent (clear any transient copied state or leave it
false) on failure; update handleCopyCode (and add the logger import if missing)
to call logger.error(...) in the catch and to not leave setCopied(true) when the
copy fails, while keeping the existing success timeout logic using
COPIED_LABEL_ANIMATION_DURATION, setCopied, withSource?.code, and
globalThis.window.setTimeout.
- Around line 229-251: The Button and ToggleButton instances in Preview.tsx (the
disabled Button with "No code available", the ToggleButton controlling expanded
with props pressed/aria-expanded/aria-controls/onClick, and the Button that
calls handleCopyCode showing copied state) need explicit ariaLabel={false} per
repo convention; update each Button and ToggleButton JSX element (the Button
rendering "No code available", the ToggleButton using
expanded/pressed/aria-controls/sourceId/onClick/setExpanded, and the Copy button
invoking handleCopyCode and showing copied) to include ariaLabel={false} so they
rely on their visible children for accessible names.

In `@code/addons/docs/src/blocks/components/Source.tsx`:
- Around line 47-48: The SourceCodeProps interface has multiple members and doc
comments squashed onto single lines — split each property onto its own line with
its preceding JSDoc/comment above it and place the closing brace on its own
line; specifically update the interface named SourceCodeProps so that the
`dark?: boolean;` and `copyable?: boolean;` declarations each sit on separate
lines with their doc comments immediately above them and then terminate the
interface with a standalone `}`.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6700746 and 3002601.

📒 Files selected for processing (2)
  • code/addons/docs/src/blocks/components/Preview.tsx
  • code/addons/docs/src/blocks/components/Source.tsx

@Sidnioulz Sidnioulz force-pushed the sidnioulz/issue-23642 branch from 64a1f83 to b0f94e7 Compare February 26, 2026 16:25
Copy link
Copy Markdown
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.

♻️ Duplicate comments (1)
code/addons/docs/src/blocks/components/Preview.tsx (1)

257-264: ⚠️ Potential issue | 🟡 Minor

Add ariaLabel on mapped Button actions for consistency.

This callsite should explicitly pass ariaLabel={false} (when visible children provide the name), matching the repo’s Button usage convention.

♿ Suggested patch
           {additionalActionItems.map(({ title, className, onClick, disabled }, index: number) => (
             <Button
               key={index}
+              ariaLabel={false}
               className={className}
               onClick={onClick}
               disabled={!!disabled}
               variant="ghost"
             >
               {title}
             </Button>
           ))}

Based on learnings: "Convention: ariaLabel is mandatory on all Button usages — provide a descriptive string for icon-only buttons; set ariaLabel=false when the button’s children already serve as the accessible name."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/addons/docs/src/blocks/components/Preview.tsx` around lines 257 - 264,
The mapped Button instances in the additionalActionItems render (inside the
additionalActionItems.map block) are missing the explicit ariaLabel prop; follow
the repo convention by adding ariaLabel={false} to the Button call so the
children continue to serve as the accessible name. Locate the mapping over
additionalActionItems in Preview.tsx (the JSX that returns <Button key={index}
... />) and add ariaLabel={false} to the Button props for consistency and
accessibility.
🧹 Nitpick comments (1)
code/addons/docs/src/blocks/components/Preview.stories.tsx (1)

54-65: Nice repro story; consider adding an assertion-backed regression test.

This story is a solid manual repro. A follow-up visual/interaction assertion for the wrapped action row would better protect against overlap regressions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/addons/docs/src/blocks/components/Preview.stories.tsx` around lines 54 -
65, Add an automated assertion-backed regression test for the ActionBarWrapping
story to lock in the wrapped action row behavior: create a test (either a
Storybook play test or a Jest/React Testing Library test) that mounts the
Preview with the same props used in ActionBarWrapping (Preview with inline,
isExpanded, withSource set to sourceStories.JSX.args and the Button child) and
asserts the visual/DOM outcome you care about (e.g., that the action row wraps
and buttons do not overlap or that the Button is visible and positioned on a new
line). Reference the existing story name ActionBarWrapping, the Preview
component, and the Button element to mirror the repro exactly and include clear
assertions for the wrap/overlap condition so future regressions are caught.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@code/addons/docs/src/blocks/components/Preview.tsx`:
- Around line 257-264: The mapped Button instances in the additionalActionItems
render (inside the additionalActionItems.map block) are missing the explicit
ariaLabel prop; follow the repo convention by adding ariaLabel={false} to the
Button call so the children continue to serve as the accessible name. Locate the
mapping over additionalActionItems in Preview.tsx (the JSX that returns <Button
key={index} ... />) and add ariaLabel={false} to the Button props for
consistency and accessibility.

---

Nitpick comments:
In `@code/addons/docs/src/blocks/components/Preview.stories.tsx`:
- Around line 54-65: Add an automated assertion-backed regression test for the
ActionBarWrapping story to lock in the wrapped action row behavior: create a
test (either a Storybook play test or a Jest/React Testing Library test) that
mounts the Preview with the same props used in ActionBarWrapping (Preview with
inline, isExpanded, withSource set to sourceStories.JSX.args and the Button
child) and asserts the visual/DOM outcome you care about (e.g., that the action
row wraps and buttons do not overlap or that the Button is visible and
positioned on a new line). Reference the existing story name ActionBarWrapping,
the Preview component, and the Button element to mirror the repro exactly and
include clear assertions for the wrap/overlap condition so future regressions
are caught.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3002601 and f8dabbf.

📒 Files selected for processing (5)
  • code/addons/docs/src/blocks/components/Preview.stories.tsx
  • code/addons/docs/src/blocks/components/Preview.tsx
  • code/addons/docs/src/blocks/components/Source.tsx
  • code/core/src/components/components/ActionBar/ActionBar.tsx
  • code/core/src/components/components/syntaxhighlighter/syntaxhighlighter.tsx

@Sidnioulz Sidnioulz requested a review from kylegach as a code owner February 27, 2026 11:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

3 participants