-
-
Notifications
You must be signed in to change notification settings - Fork 9.8k
UI: Improve landmark navigation #33457
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
base: next
Are you sure you want to change the base?
Conversation
|
View your CI Pipeline Execution ↗ for commit 7feac0b
☁️ Nx Cloud last updated this comment at |
3b6c464 to
aaf875d
Compare
aaf875d to
e37e586
Compare
📝 WalkthroughWalkthroughAdds accessibility landmark navigation via new Changes
Sequence DiagramsequenceDiagram
participant User as User
participant KeyListener as Keyboard Listener<br/>(useLandmarkIndicator)
participant DOM as DOM Tree
participant Animation as Web Animations API
participant Theme as Theme System
User->>KeyListener: Press F6
KeyListener->>KeyListener: Capture keydown event
KeyListener->>DOM: Search up from activeElement<br/>for [data-sb-landmark]
DOM-->>KeyListener: Return parent landmark element
alt Landmark Found
KeyListener->>Animation: Cancel existing animation<br/>on landmark
KeyListener->>Theme: Fetch theme.color.primary
Theme-->>KeyListener: Return primary color
KeyListener->>Animation: Start border highlight animation<br/>(2px solid, 1.5s duration)<br/>on ::after pseudo-element
Animation->>DOM: Render border highlight<br/>on landmark
Animation->>Animation: Wait 1.5s
Animation->>DOM: Remove animation, cleanup state
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Possibly related PRs
✨ Finishing touches
Comment |
There was a problem hiding this 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
🤖 Fix all issues with AI agents
In @code/core/src/manager/components/sidebar/Explorer.tsx:
- Line 27: Update the ref type for containerRef to match the rendered element:
change useRef<HTMLDivElement>(null) to useRef<HTMLElement | null>(null)
(referencing the containerRef variable in Explorer.tsx) so the ref can point to
the <nav> element and satisfy type safety wherever containerRef.current is used.
In @code/core/src/manager/components/sidebar/TestingWidget.tsx:
- Around line 257-264: The hooks cardRef = useRef<HTMLDivElement>(null) and
const { landmarkProps } = useLandmark(...) are being invoked after an early
return, violating the Rules of Hooks; move those hook calls (cardRef and
useLandmark invocation that provides landmarkProps) to execute unconditionally
before any early return or conditional logic in the TestingWidget component so
they run in the same order on every render, then use landmarkProps and cardRef
where needed after the early-return check.
🧹 Nitpick comments (5)
code/core/src/manager/components/sidebar/Heading.stories.tsx (1)
237-240: Text update correctly reflects the new skip link label.The change from "Skip to canvas" to "Skip to content" aligns with the PR's accessibility improvements. The play function will correctly focus the updated skip link elements for Chromatic testing.
Optional: Consider renaming the story export for consistency.
The story is named
SkipToCanvasLinkFocused(line 229) but now targets "Skip to content" text. For clarity, you might rename it toSkipToContentLinkFocused, though story names primarily serve as identifiers and the current name is still functional.code/core/src/components/components/Card/Card.tsx (1)
100-107: Consider placing ref after outlineProps spread.While
DOMAttributesdoesn't includeref(TypeScript prevents passing it), React convention placesreflast to prevent accidental override and provide defense in depth.♻️ Suggested prop order
- <CardOutline animation={outlineAnimation} color={outlineColor} ref={ref} {...outlineProps}> + <CardOutline animation={outlineAnimation} color={outlineColor} {...outlineProps} ref={ref}>code/core/src/manager/components/preview/FramesRenderer.tsx (1)
99-103: Removing the anchortitlehere is fine; consider dropping redundanttabIndex={0}.Since the
<a>has anhref, it’s already tabbable; keepingtabIndex={0}is harmless but unnecessary.code/core/src/manager/components/layout/useLandmarkIndicator.ts (1)
37-43: Consider browser compatibility for the Animation API.The Web Animations API is well-supported in modern browsers but may fail in older environments. Consider adding a try-catch or feature detection for maximum robustness.
♻️ Optional defensive coding enhancement
- const animation = landmarkElement.animate( - [{ border: `2px solid ${theme.color.primary}` }, { border: `2px solid transparent` }], - { - duration: 1500, - pseudoElement: '::after', - } - ); + try { + const animation = landmarkElement.animate( + [{ border: `2px solid ${theme.color.primary}` }, { border: `2px solid transparent` }], + { + duration: 1500, + pseudoElement: '::after', + } + ); - currentAnimationRef.current = animation; + currentAnimationRef.current = animation; - animation.onfinish = () => { - currentAnimationRef.current = null; - }; + animation.onfinish = () => { + currentAnimationRef.current = null; + }; + } catch (e) { + // Animation API not supported or failed, silently continue + }code/core/src/manager/components/sidebar/Search.tsx (1)
316-322: Consider adding an explicit label to the search landmark.The search landmark lacks an
aria-labeloraria-labelledbyattribute. While the input field has an associated label, the landmark region itself would benefit from explicit labeling for better accessibility.💡 Suggested enhancement
const searchLandmarkRef = useRef<HTMLDivElement>(null); const { landmarkProps } = useLandmark( { + 'aria-label': 'Search components', role: 'search', }, searchLandmarkRef );
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (22)
code/core/package.jsoncode/core/src/components/components/Card/Card.tsxcode/core/src/manager/components/layout/Layout.tsxcode/core/src/manager/components/layout/useLandmarkIndicator.tscode/core/src/manager/components/mobile/navigation/MobileNavigation.tsxcode/core/src/manager/components/panel/Panel.tsxcode/core/src/manager/components/preview/FramesRenderer.tsxcode/core/src/manager/components/preview/Preview.tsxcode/core/src/manager/components/preview/Toolbar.tsxcode/core/src/manager/components/preview/utils/components.tscode/core/src/manager/components/sidebar/Explorer.tsxcode/core/src/manager/components/sidebar/Heading.stories.tsxcode/core/src/manager/components/sidebar/Heading.tsxcode/core/src/manager/components/sidebar/Search.tsxcode/core/src/manager/components/sidebar/Sidebar.stories.tsxcode/core/src/manager/components/sidebar/Sidebar.tsxcode/core/src/manager/components/sidebar/TestingWidget.tsxcode/core/src/manager/components/sidebar/Tree.stories.tsxcode/core/src/manager/components/sidebar/Tree.tsxcode/core/src/manager/components/sidebar/types.tscode/core/src/manager/hooks/useLandmark.tscode/core/src/theming/global.ts
💤 Files with no reviewable changes (1)
- code/core/src/manager/components/sidebar/Sidebar.stories.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,ts,tsx,json,md,html,css,scss}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Format code using Prettier with
yarn prettier --write <file>
Files:
code/core/src/manager/components/sidebar/TestingWidget.tsxcode/core/src/manager/components/sidebar/Explorer.tsxcode/core/src/manager/components/sidebar/Heading.stories.tsxcode/core/src/manager/hooks/useLandmark.tscode/core/src/components/components/Card/Card.tsxcode/core/src/manager/components/preview/utils/components.tscode/core/src/manager/components/sidebar/Sidebar.tsxcode/core/src/manager/components/sidebar/Heading.tsxcode/core/src/manager/components/mobile/navigation/MobileNavigation.tsxcode/core/src/manager/components/sidebar/Tree.stories.tsxcode/core/src/manager/components/sidebar/Tree.tsxcode/core/src/manager/components/preview/Toolbar.tsxcode/core/src/manager/components/sidebar/types.tscode/core/src/manager/components/preview/FramesRenderer.tsxcode/core/src/manager/components/sidebar/Search.tsxcode/core/src/manager/components/preview/Preview.tsxcode/core/src/manager/components/panel/Panel.tsxcode/core/package.jsoncode/core/src/manager/components/layout/useLandmarkIndicator.tscode/core/src/manager/components/layout/Layout.tsxcode/core/src/theming/global.ts
**/*.{js,jsx,json,html,ts,tsx,mjs}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Run ESLint checks using
yarn lint:js:cmd <file>or the full commandcross-env NODE_ENV=production eslint --cache --cache-location=../.cache/eslint --ext .js,.jsx,.json,.html,.ts,.tsx,.mjs --report-unused-disable-directivesto fix linting errors before committing
Files:
code/core/src/manager/components/sidebar/TestingWidget.tsxcode/core/src/manager/components/sidebar/Explorer.tsxcode/core/src/manager/components/sidebar/Heading.stories.tsxcode/core/src/manager/hooks/useLandmark.tscode/core/src/components/components/Card/Card.tsxcode/core/src/manager/components/preview/utils/components.tscode/core/src/manager/components/sidebar/Sidebar.tsxcode/core/src/manager/components/sidebar/Heading.tsxcode/core/src/manager/components/mobile/navigation/MobileNavigation.tsxcode/core/src/manager/components/sidebar/Tree.stories.tsxcode/core/src/manager/components/sidebar/Tree.tsxcode/core/src/manager/components/preview/Toolbar.tsxcode/core/src/manager/components/sidebar/types.tscode/core/src/manager/components/preview/FramesRenderer.tsxcode/core/src/manager/components/sidebar/Search.tsxcode/core/src/manager/components/preview/Preview.tsxcode/core/src/manager/components/panel/Panel.tsxcode/core/package.jsoncode/core/src/manager/components/layout/useLandmarkIndicator.tscode/core/src/manager/components/layout/Layout.tsxcode/core/src/theming/global.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Enable TypeScript strict mode across all packages
Files:
code/core/src/manager/components/sidebar/TestingWidget.tsxcode/core/src/manager/components/sidebar/Explorer.tsxcode/core/src/manager/components/sidebar/Heading.stories.tsxcode/core/src/manager/hooks/useLandmark.tscode/core/src/components/components/Card/Card.tsxcode/core/src/manager/components/preview/utils/components.tscode/core/src/manager/components/sidebar/Sidebar.tsxcode/core/src/manager/components/sidebar/Heading.tsxcode/core/src/manager/components/mobile/navigation/MobileNavigation.tsxcode/core/src/manager/components/sidebar/Tree.stories.tsxcode/core/src/manager/components/sidebar/Tree.tsxcode/core/src/manager/components/preview/Toolbar.tsxcode/core/src/manager/components/sidebar/types.tscode/core/src/manager/components/preview/FramesRenderer.tsxcode/core/src/manager/components/sidebar/Search.tsxcode/core/src/manager/components/preview/Preview.tsxcode/core/src/manager/components/panel/Panel.tsxcode/core/src/manager/components/layout/useLandmarkIndicator.tscode/core/src/manager/components/layout/Layout.tsxcode/core/src/theming/global.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{ts,tsx,js,jsx}: Export functions from modules if they need to be tested
Do not useconsole.log,console.warn, orconsole.errordirectly unless in isolated files where importing loggers would significantly increase bundle size
Files:
code/core/src/manager/components/sidebar/TestingWidget.tsxcode/core/src/manager/components/sidebar/Explorer.tsxcode/core/src/manager/components/sidebar/Heading.stories.tsxcode/core/src/manager/hooks/useLandmark.tscode/core/src/components/components/Card/Card.tsxcode/core/src/manager/components/preview/utils/components.tscode/core/src/manager/components/sidebar/Sidebar.tsxcode/core/src/manager/components/sidebar/Heading.tsxcode/core/src/manager/components/mobile/navigation/MobileNavigation.tsxcode/core/src/manager/components/sidebar/Tree.stories.tsxcode/core/src/manager/components/sidebar/Tree.tsxcode/core/src/manager/components/preview/Toolbar.tsxcode/core/src/manager/components/sidebar/types.tscode/core/src/manager/components/preview/FramesRenderer.tsxcode/core/src/manager/components/sidebar/Search.tsxcode/core/src/manager/components/preview/Preview.tsxcode/core/src/manager/components/panel/Panel.tsxcode/core/src/manager/components/layout/useLandmarkIndicator.tscode/core/src/manager/components/layout/Layout.tsxcode/core/src/theming/global.ts
code/{core,lib,addons,builders,frameworks,presets}/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use
loggerfromstorybook/internal/node-loggerfor server-side logging in Node.js code
Files:
code/core/src/manager/components/sidebar/TestingWidget.tsxcode/core/src/manager/components/sidebar/Explorer.tsxcode/core/src/manager/components/sidebar/Heading.stories.tsxcode/core/src/manager/hooks/useLandmark.tscode/core/src/components/components/Card/Card.tsxcode/core/src/manager/components/preview/utils/components.tscode/core/src/manager/components/sidebar/Sidebar.tsxcode/core/src/manager/components/sidebar/Heading.tsxcode/core/src/manager/components/mobile/navigation/MobileNavigation.tsxcode/core/src/manager/components/sidebar/Tree.stories.tsxcode/core/src/manager/components/sidebar/Tree.tsxcode/core/src/manager/components/preview/Toolbar.tsxcode/core/src/manager/components/sidebar/types.tscode/core/src/manager/components/preview/FramesRenderer.tsxcode/core/src/manager/components/sidebar/Search.tsxcode/core/src/manager/components/preview/Preview.tsxcode/core/src/manager/components/panel/Panel.tsxcode/core/src/manager/components/layout/useLandmarkIndicator.tscode/core/src/manager/components/layout/Layout.tsxcode/core/src/theming/global.ts
🧠 Learnings (13)
📓 Common learnings
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Tabs/Tabs.stories.tsx:222-227
Timestamp: 2025-11-05T09:36:55.944Z
Learning: Repo: storybookjs/storybook PR: 32458 — In code/core/src/components/components/Button/Button.tsx (React/TypeScript), ButtonProps includes ariaLabel?: string | false and the component maps it to the DOM aria-label. 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. Do not suggest using a raw aria-label prop on Button call sites.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/manager/components/preview/Toolbar.tsx:102-105
Timestamp: 2025-10-03T07:55:42.639Z
Learning: In code/core/src/manager/components/preview/Toolbar.tsx, we intentionally do not add aria-label/aria-labelledby to StyledToolbar (AbstractToolbar) because the enclosing section is already labeled via an sr-only heading and the toolbar is the only content. Revisit only if real user testing indicates a need.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32594
File: code/core/src/components/components/Popover/WithPopover.tsx:7-9
Timestamp: 2025-10-01T15:24:01.060Z
Learning: In the Storybook repository, "react-aria-components/patched-dist/*" (e.g., "react-aria-components/patched-dist/Dialog", "react-aria-components/patched-dist/Popover", "react-aria-components/patched-dist/Tooltip") are valid import paths created by a patch applied to the react-aria-components package. These imports should not be flagged as broken or invalid until a maintainer explicitly states they are no longer acceptable.
📚 Learning: 2025-11-05T09:36:55.944Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Tabs/Tabs.stories.tsx:222-227
Timestamp: 2025-11-05T09:36:55.944Z
Learning: Repo: storybookjs/storybook PR: 32458 — In code/core/src/components/components/Button/Button.tsx (React/TypeScript), ButtonProps includes ariaLabel?: string | false and the component maps it to the DOM aria-label. 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. Do not suggest using a raw aria-label prop on Button call sites.
Applied to files:
code/core/src/manager/components/sidebar/TestingWidget.tsxcode/core/src/manager/components/sidebar/Explorer.tsxcode/core/src/manager/components/sidebar/Heading.stories.tsxcode/core/src/manager/hooks/useLandmark.tscode/core/src/components/components/Card/Card.tsxcode/core/src/manager/components/sidebar/Sidebar.tsxcode/core/src/manager/components/sidebar/Heading.tsxcode/core/src/manager/components/mobile/navigation/MobileNavigation.tsxcode/core/src/manager/components/sidebar/Tree.stories.tsxcode/core/src/manager/components/sidebar/Tree.tsxcode/core/src/manager/components/preview/Toolbar.tsxcode/core/src/manager/components/preview/FramesRenderer.tsxcode/core/src/manager/components/sidebar/Search.tsxcode/core/src/manager/components/preview/Preview.tsxcode/core/package.jsoncode/core/src/theming/global.ts
📚 Learning: 2025-11-05T09:37:25.920Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/tooltip/WithTooltip.tsx:54-96
Timestamp: 2025-11-05T09:37:25.920Z
Learning: Repo: storybookjs/storybook — In code/core/src/components/components/tooltip/WithTooltip.tsx, the legacy WithTooltip implementation is intentionally reintroduced for backward compatibility and is deprecated; maintainers (per Sidnioulz) do not want maintenance or improvements on it. Prefer WithTooltipNew/Popover; avoid suggesting changes to WithTooltip.* going forward.
Applied to files:
code/core/src/manager/components/sidebar/TestingWidget.tsxcode/core/src/manager/components/sidebar/Heading.stories.tsxcode/core/src/components/components/Card/Card.tsxcode/core/src/manager/components/sidebar/Sidebar.tsxcode/core/src/manager/components/mobile/navigation/MobileNavigation.tsxcode/core/src/manager/components/sidebar/Tree.stories.tsxcode/core/src/manager/components/preview/Toolbar.tsxcode/core/src/manager/components/preview/FramesRenderer.tsxcode/core/src/manager/components/sidebar/Search.tsxcode/core/src/manager/components/preview/Preview.tsx
📚 Learning: 2025-10-03T07:55:42.639Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/manager/components/preview/Toolbar.tsx:102-105
Timestamp: 2025-10-03T07:55:42.639Z
Learning: In code/core/src/manager/components/preview/Toolbar.tsx, we intentionally do not add aria-label/aria-labelledby to StyledToolbar (AbstractToolbar) because the enclosing section is already labeled via an sr-only heading and the toolbar is the only content. Revisit only if real user testing indicates a need.
Applied to files:
code/core/src/manager/components/sidebar/TestingWidget.tsxcode/core/src/manager/components/sidebar/Explorer.tsxcode/core/src/manager/components/sidebar/Heading.stories.tsxcode/core/src/manager/components/preview/utils/components.tscode/core/src/manager/components/sidebar/Sidebar.tsxcode/core/src/manager/components/sidebar/Heading.tsxcode/core/src/manager/components/mobile/navigation/MobileNavigation.tsxcode/core/src/manager/components/sidebar/Tree.stories.tsxcode/core/src/manager/components/sidebar/Tree.tsxcode/core/src/manager/components/preview/Toolbar.tsxcode/core/src/manager/components/preview/FramesRenderer.tsxcode/core/src/manager/components/preview/Preview.tsxcode/core/src/manager/components/panel/Panel.tsxcode/core/src/theming/global.ts
📚 Learning: 2025-11-05T09:38:47.712Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.
Applied to files:
code/core/src/manager/components/sidebar/TestingWidget.tsxcode/core/src/manager/components/sidebar/Explorer.tsxcode/core/src/manager/components/sidebar/Heading.stories.tsxcode/core/src/components/components/Card/Card.tsxcode/core/src/manager/components/sidebar/Sidebar.tsxcode/core/src/manager/components/mobile/navigation/MobileNavigation.tsxcode/core/src/manager/components/sidebar/Tree.stories.tsxcode/core/src/manager/components/preview/Toolbar.tsxcode/core/src/manager/components/preview/FramesRenderer.tsxcode/core/src/manager/components/sidebar/Search.tsxcode/core/src/manager/components/preview/Preview.tsxcode/core/src/manager/components/panel/Panel.tsxcode/core/src/manager/components/layout/useLandmarkIndicator.ts
📚 Learning: 2025-09-18T20:51:06.618Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: In viewport tool code, when using the `useGlobals` hook from storybook/manager-api, the third returned value `storyGlobals` is guaranteed by TypeScript to be defined (not undefined/null), making the `in` operator safe to use without additional null checks.
Applied to files:
code/core/src/manager/components/sidebar/TestingWidget.tsxcode/core/src/manager/components/mobile/navigation/MobileNavigation.tsxcode/core/src/manager/components/sidebar/Search.tsxcode/core/src/manager/components/preview/Preview.tsxcode/core/src/manager/components/panel/Panel.tsx
📚 Learning: 2025-09-18T20:51:06.618Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: The useGlobals hook from storybook/manager-api returns a tuple where the third element (storyGlobals) is typed as Globals, not Globals | undefined. This means TypeScript guarantees it's always defined, making the `in` operator safe to use without additional null checks.
Applied to files:
code/core/src/manager/components/sidebar/TestingWidget.tsxcode/core/src/manager/components/mobile/navigation/MobileNavigation.tsxcode/core/src/manager/components/sidebar/Search.tsxcode/core/src/manager/components/panel/Panel.tsx
📚 Learning: 2025-09-24T09:39:39.233Z
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32507
File: code/core/src/manager/globals/globals-module-info.ts:25-33
Timestamp: 2025-09-24T09:39:39.233Z
Learning: In Storybook, storybook/actions/decorator is a preview-only entrypoint and should not be included in manager globals configuration. The duplicatedKeys array in code/core/src/manager/globals/globals-module-info.ts is specifically for manager-side externalization, not preview entrypoints.
Applied to files:
code/core/src/manager/components/sidebar/Heading.stories.tsxcode/core/src/manager/components/mobile/navigation/MobileNavigation.tsxcode/core/src/manager/components/sidebar/Tree.stories.tsxcode/core/src/manager/components/preview/Toolbar.tsxcode/core/src/manager/components/preview/FramesRenderer.tsxcode/core/src/manager/components/sidebar/Search.tsx
📚 Learning: 2025-10-01T15:24:01.060Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32594
File: code/core/src/components/components/Popover/WithPopover.tsx:7-9
Timestamp: 2025-10-01T15:24:01.060Z
Learning: In the Storybook repository, "react-aria-components/patched-dist/*" (e.g., "react-aria-components/patched-dist/Dialog", "react-aria-components/patched-dist/Popover", "react-aria-components/patched-dist/Tooltip") are valid import paths created by a patch applied to the react-aria-components package. These imports should not be flagged as broken or invalid until a maintainer explicitly states they are no longer acceptable.
Applied to files:
code/core/src/manager/components/sidebar/Tree.stories.tsxcode/core/package.json
📚 Learning: 2025-12-22T22:03:40.123Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-22T22:03:40.123Z
Learning: Applies to code/{renderers}/**/*.{ts,tsx,js,jsx} : Use `logger` from `storybook/internal/client-logger` for client-side logging in browser code
Applied to files:
code/core/src/manager/components/sidebar/Tree.stories.tsx
📚 Learning: 2025-12-22T22:03:40.123Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-22T22:03:40.123Z
Learning: Applies to code/{core,lib,addons,builders,frameworks,presets}/**/*.{ts,tsx,js,jsx} : Use `logger` from `storybook/internal/node-logger` for server-side logging in Node.js code
Applied to files:
code/core/src/manager/components/sidebar/Tree.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock all required dependencies that the test subject uses
Applied to files:
code/core/src/manager/components/sidebar/Tree.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Document complex mock behaviors in Vitest tests
Applied to files:
code/core/src/manager/components/sidebar/Tree.stories.tsx
🧬 Code graph analysis (13)
code/core/src/manager/components/sidebar/TestingWidget.tsx (2)
code/core/src/preview-api/index.ts (1)
useRef(13-13)code/core/src/manager/hooks/useLandmark.ts (1)
useLandmark(10-22)
code/core/src/manager/components/sidebar/Explorer.tsx (1)
code/core/src/manager/hooks/useLandmark.ts (1)
useLandmark(10-22)
code/core/src/components/components/Card/Card.tsx (1)
code/core/src/components/index.ts (1)
Card(46-46)
code/core/src/manager/components/sidebar/Sidebar.tsx (4)
code/core/src/preview-api/index.ts (1)
useRef(13-13)code/core/src/manager/hooks/useLandmark.ts (1)
useLandmark(10-22)code/core/src/manager/components/sidebar/Explorer.tsx (1)
Explorer(19-72)code/core/src/manager/components/sidebar/SearchResults.tsx (1)
SearchResults(224-345)
code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx (2)
code/core/src/preview-api/index.ts (1)
useRef(13-13)code/core/src/manager/hooks/useLandmark.ts (1)
useLandmark(10-22)
code/core/src/manager/components/sidebar/Tree.stories.tsx (1)
code/core/src/manager-api/root.tsx (1)
ManagerContext(79-79)
code/core/src/manager/components/preview/Toolbar.tsx (2)
code/core/src/preview-api/index.ts (1)
useRef(13-13)code/core/src/manager/hooks/useLandmark.ts (1)
useLandmark(10-22)
code/core/src/manager/components/sidebar/Search.tsx (1)
code/core/src/manager/hooks/useLandmark.ts (1)
useLandmark(10-22)
code/core/src/manager/components/preview/Preview.tsx (1)
code/core/src/preview-api/index.ts (1)
useRef(13-13)
code/core/src/manager/components/panel/Panel.tsx (2)
code/core/src/preview-api/index.ts (1)
useRef(13-13)code/core/src/manager/hooks/useLandmark.ts (1)
useLandmark(10-22)
code/core/src/manager/components/layout/useLandmarkIndicator.ts (2)
code/core/src/theming/index.ts (1)
useTheme(18-18)code/core/src/preview-api/index.ts (2)
useRef(13-13)useEffect(8-8)
code/core/src/manager/components/layout/Layout.tsx (1)
code/core/src/manager/components/layout/useLandmarkIndicator.ts (1)
useLandmarkIndicator(9-59)
code/core/src/theming/global.ts (1)
code/core/src/theming/base.ts (1)
color(1-42)
🪛 Biome (2.1.2)
code/core/src/manager/components/sidebar/TestingWidget.tsx
[error] 257-257: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
[error] 258-258: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
⏰ 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). (4)
- GitHub Check: normal
- GitHub Check: nx
- GitHub Check: nx
- GitHub Check: nx
🔇 Additional comments (28)
code/core/src/components/components/Card/Card.tsx (2)
1-1: LGTM: Import addition is appropriate.The
DOMAttributesimport is necessary for typing the newoutlinePropsand is correctly imported from React.
95-95: LGTM: Type definition is appropriate.The
outlinePropstype correctly usesDOMAttributes<HTMLDivElement>to accept standard DOM attributes, ARIA attributes, and event handlers for the outline element.code/core/src/manager/components/sidebar/Heading.tsx (1)
90-96: Skip link text change looks good and consistent with the new landmark framing.
ariaLabel={false}is the right choice here since the anchor text provides the accessible name (as per learnings).code/core/src/manager/components/sidebar/Tree.stories.tsx (2)
15-17: Story decorator update to includeIconSymbolsmatches the component-level change.This keeps the Tree stories self-contained now that
Treeno longer injects the SVG symbols.Also applies to: 79-85
277-285: Updated play assertion for “Skip to content” matches the new label.code/core/src/manager/components/sidebar/Tree.tsx (2)
304-306: “Skip to content” label updates are consistent across node types.Also: using
ariaLabel={false}is correct since the link text is the accessible name (as per learnings).Also applies to: 445-447, 503-505
753-755: No action needed —IconSymbolsis properly mounted in the parentSidebarcomponent.The verification confirms that
<IconSymbols />is rendered at line 156 ofSidebar.tsx, which is the parent component ofTree. This global mount ensures that symbol-based icons (via<UseSymbol />) remain functional across all child components, including those that depend on it likeTreeNode,ContextMenu, andSearchResults. The refactoring correctly moved the mount to the appropriate parent level.code/core/package.json (1)
228-235: No action needed.@react-aria/landmarkis correctly placed indevDependenciesbecause it is bundled into the manager output, following Storybook's established pattern for bundled UI component libraries (similar toopenandfast-printf). All@react-aria/*packages are grouped indevDependenciesand are not listed in the esbuild global externals configuration, confirming they are bundled rather than externalized.code/core/src/theming/global.ts (1)
137-155: LGTM! Landmark focus styling implementation is sound.The landmark focus indication using
::afterpseudo-element is well-executed:
- Avoids layout shift by using an overlay approach
- Properly isolates pointer events
- Uses theme primary color for brand consistency
Per the PR objectives, ensure this styling has been tested in high-contrast mode to verify the focus outline remains visible.
code/core/src/manager/components/sidebar/types.ts (1)
54-66: LGTM! Improved state granularity for accessibility.The replacement of
isBrowsingwith three distinct flags provides clearer semantics and enables better accessibility control. The inline documentation effectively explains each flag's purpose and the distinction between visibility and DOM presence is crucial for maintaining keyboard navigation (per PR objectives).code/core/src/manager/components/layout/Layout.tsx (1)
162-163: LGTM! Landmark indicator integration is correctly placed.The
useLandmarkIndicator()hook is appropriately called in the rootLayoutcomponent, which serves as the parent container for all landmarks. The hook's implementation (per code snippets) properly manages the F6 keydown listener lifecycle with cleanup, and the animation mechanism prevents concurrent highlights.The PR acknowledges the known limitation where the preview iframe can intercept F6 events, preventing navigation from within rendered stories—an acceptable trade-off given iframe constraints.
code/core/src/manager/components/preview/utils/components.ts (1)
5-5: LGTM! Landmark element restructuring is semantically correct.Moving the
mainlandmark fromPreviewContainertoFrameWrapproperly aligns semantic roles with actual content:
PreviewContainerasdiv: appropriate for layout wrapperFrameWrapasmain: correct for the primary preview content regionThis change aligns with the PR objective of marking Preview as the main landmark. The
useLandmarkhook properly integrates the landmark with React Aria, registering the region with appropriate ARIA attributes (aria-labelledbyandrole='main').Verification confirms only one
<main>landmark exists in the manager components (the FrameWrap in Preview), with no conflicting landmarks across the codebase.code/core/src/manager/hooks/useLandmark.ts (1)
1-22: LGTM! Clean wrapper implementation.The hook properly wraps React Aria's
useLandmarkand augments it with the Storybook-specificdata-sb-landmarkattribute that enables the focus styling defined inglobal.ts. The implementation is minimal, type-safe, and actively used across eight manager components (Preview, Toolbar, Panel, Sidebar, Search, Explorer, TestingWidget, and MobileNavigation) with consistent patterns.code/core/src/manager/components/preview/Preview.tsx (2)
115-119: LGTM! Landmark integration correctly implemented.The landmark hook is properly configured with role='main' and references the visually-hidden heading, aligning with the PR objectives to mark the Preview as the main landmark region.
138-138: LGTM! Landmark props correctly applied.The ref and landmarkProps are properly spread onto the FrameWrap element, enabling both landmark navigation and visual highlighting.
code/core/src/manager/components/preview/Toolbar.tsx (2)
87-94: LGTM! Landmark integration correctly implemented.The landmark hook properly configures the toolbar as a region with appropriate labeling via the visually-hidden heading.
101-102: LGTM! Landmark props correctly applied.The ref and landmarkProps are properly spread onto the StyledSection, enabling landmark navigation and visual highlighting.
code/core/src/manager/components/panel/Panel.tsx (2)
156-160: Verify ARIA role matches semantic intent.The
<aside>element has an implicit ARIA role ofcomplementary, but the code explicitly setsrole='region', overriding this. The PR objectives specify marking the "Addon panel as aside", suggesting the complementary role should be used.Consider using
role='complementary'to align with the semantic<aside>element, or omit the role attribute entirely to use the implicit role:♻️ Proposed fix
const { landmarkProps } = useLandmark( - { 'aria-labelledby': 'storybook-panel-heading', role: 'region' }, + { 'aria-labelledby': 'storybook-panel-heading', role: 'complementary' }, asideRef );Alternatively, if the React Aria landmark hook properly handles implicit roles:
const { landmarkProps } = useLandmark( - { 'aria-labelledby': 'storybook-panel-heading', role: 'region' }, + { 'aria-labelledby': 'storybook-panel-heading' }, asideRef );
163-163: LGTM! Landmark props correctly applied.The ref and landmarkProps are properly spread onto the Aside element, assuming the role mismatch is addressed.
code/core/src/manager/components/layout/useLandmarkIndicator.ts (1)
9-58: LGTM! Landmark indicator implementation is sound.The F6 keydown handler correctly traverses the DOM to locate landmark regions and applies a visual border animation. The cleanup and dependency on
theme.color.primaryare appropriate.Note: The PR objectives mention a known limitation where the preview iframe intercepts F6 events, preventing landmark navigation from within the iframe. This is an expected constraint.
code/core/src/manager/components/sidebar/TestingWidget.tsx (1)
274-279: LGTM! Landmark props correctly applied.The ref and
outlinePropswith landmarkProps are properly applied to the HoverCard, and the visually-hidden heading provides appropriate labeling. This will work correctly once the hook placement issue is resolved.code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx (2)
81-88: LGTM: Landmark integration correctly implemented.The banner landmark is properly configured with
aria-labelledbyreferencing the "Navigation controls" heading. The ref type matches the header element.
145-174: LGTM: Semantic element updates align with landmark strategy.Changing
Containertostyled.sectionandMobileBottomBartostyled.headerprovides the appropriate semantic structure for the banner landmark.code/core/src/manager/components/sidebar/Explorer.tsx (1)
37-56: LGTM: Navigation landmark properly configured.The landmark is correctly labeled via the "Stories" heading, and the semantic
navelement withrole="navigation"provides appropriate structure for assistive technologies.code/core/src/manager/components/sidebar/Search.tsx (1)
445-447: LGTM: Render props API updated to support landmark-aware navigation.The new
isNavVisible,isNavRendered, andisSearchResultRenderedflags provide clearer semantics for controlling visibility and rendering of navigation elements, aligning with the broader landmark navigation strategy.code/core/src/manager/components/sidebar/Sidebar.tsx (3)
142-156: LGTM: Banner landmark and document structure correctly implemented.The addition of a visually-hidden
h1for "Storybook" and the banner landmark on the header element provides proper document structure and navigation semantics. TheIconSymbolscomponent placement is appropriate.
213-222: Verify screen reader behavior with conditionally hidden Explorer.When
isNavVisibleis false, the Explorer remains in the DOM withsb-sr-onlyclass applied. Confirm that this approach doesn't cause screen readers to announce hidden navigation content while users are interacting with search results.This pattern is likely intentional to preserve keyboard navigation state, but validation with actual screen reader testing would be beneficial to ensure it doesn't create confusion for AT users.
31-47: LGTM: Semantic container updated to header for banner landmark.The change from
styled.divtostyled.headerprovides the appropriate semantic foundation for the banner landmark while preserving all styling.
| selected, | ||
| ...restProps | ||
| }) { | ||
| const containerRef = useRef<HTMLDivElement>(null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix type mismatch for containerRef.
The ref is typed as HTMLDivElement but Line 46 shows the element is now a nav. Update to HTMLElement for type safety.
🔧 Proposed fix
- const containerRef = useRef<HTMLDivElement>(null);
+ const containerRef = useRef<HTMLElement>(null);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const containerRef = useRef<HTMLDivElement>(null); | |
| const containerRef = useRef<HTMLElement>(null); |
🤖 Prompt for AI Agents
In @code/core/src/manager/components/sidebar/Explorer.tsx at line 27, Update the
ref type for containerRef to match the rendered element: change
useRef<HTMLDivElement>(null) to useRef<HTMLElement | null>(null) (referencing
the containerRef variable in Explorer.tsx) so the ref can point to the <nav>
element and satisfy type safety wherever containerRef.current is used.
| const cardRef = useRef<HTMLDivElement>(null); | ||
| const { landmarkProps } = useLandmark( | ||
| { | ||
| 'aria-labelledby': 'storybook-testing-widget-heading', | ||
| role: 'region', | ||
| }, | ||
| cardRef | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Hooks called conditionally after early return.
Lines 257-264 call useRef and useLandmark hooks after an early return on line 253. This violates the Rules of Hooks, which require hooks to be called unconditionally and in the same order on every render. This can cause React state corruption and runtime errors.
🐛 Proposed fix: Move hooks before early return
+ const cardRef = useRef<HTMLDivElement>(null);
+ const { landmarkProps } = useLandmark(
+ {
+ 'aria-labelledby': 'storybook-testing-widget-heading',
+ role: 'region',
+ },
+ cardRef
+ );
+
if (!hasTestProviders && !errorCount && !warningCount) {
return null;
}
- const cardRef = useRef<HTMLDivElement>(null);
- const { landmarkProps } = useLandmark(
- {
- 'aria-labelledby': 'storybook-testing-widget-heading',
- role: 'region',
- },
- cardRef
- );
-
return (📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const cardRef = useRef<HTMLDivElement>(null); | |
| const { landmarkProps } = useLandmark( | |
| { | |
| 'aria-labelledby': 'storybook-testing-widget-heading', | |
| role: 'region', | |
| }, | |
| cardRef | |
| ); | |
| const cardRef = useRef<HTMLDivElement>(null); | |
| const { landmarkProps } = useLandmark( | |
| { | |
| 'aria-labelledby': 'storybook-testing-widget-heading', | |
| role: 'region', | |
| }, | |
| cardRef | |
| ); | |
| if (!hasTestProviders && !errorCount && !warningCount) { | |
| return null; | |
| } |
🧰 Tools
🪛 Biome (2.1.2)
[error] 257-257: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
[error] 258-258: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
🤖 Prompt for AI Agents
In @code/core/src/manager/components/sidebar/TestingWidget.tsx around lines 257
- 264, The hooks cardRef = useRef<HTMLDivElement>(null) and const {
landmarkProps } = useLandmark(...) are being invoked after an early return,
violating the Rules of Hooks; move those hook calls (cardRef and useLandmark
invocation that provides landmarkProps) to execute unconditionally before any
early return or conditional logic in the TestingWidget component so they run in
the same order on every render, then use landmarkProps and cardRef where needed
after the early-return check.
Closes #32271
What I did
Peek.2026-01-09.19-27.webm
headerelement,bannerrole, was previously a nav)searchrole)navrole)mainrole)regionrole)asiderole)header/bannerto the MobileNavigation bottom barFix preview iframe eating up F6 key events(can't find a way)Still gotta handle a few focus / keyboard handling edge cases.
Checklist for Contributors
Testing
Not tested
Manual testing
With screen reader
Without screen reader
Limitations
Documentation
@kylegach @jonniebigodes @MichaelArestad I'd like to get your opinions on how to make this feature discoverable to users. Should we add it in the shortcuts page, even though it can't be edited, just for visibility? Is there a best page to document it in the docs website?
Checklist for Maintainers
When this PR is ready for testing, make sure to add
ci:normal,ci:mergedorci:dailyGH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found incode/lib/cli-storybook/src/sandbox-templates.tsMake 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/coreteam 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
Bug Fixes
Style
✏️ Tip: You can customize this high-level summary in your review settings.