Skip to content

Core: Use canonical links in sidebar and menu#33400

Merged
ghengeveld merged 9 commits into
nextfrom
sidnioulz/exp-use-links-to-navigate
Jan 9, 2026
Merged

Core: Use canonical links in sidebar and menu#33400
ghengeveld merged 9 commits into
nextfrom
sidnioulz/exp-use-links-to-navigate

Conversation

@Sidnioulz
Copy link
Copy Markdown
Member

@Sidnioulz Sidnioulz commented Dec 19, 2025

What I did

  • Transforms buttons in menu and sidebar into a when relevant
  • Adds rel="canonical" attr to help crawlers index our content

Checklist for Contributors

Testing

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

ø

Manual testing

  1. Run this SB instance
  2. Check DOM content of menu entries for shortcuts, what's new and about
  3. Check DOM content for docs sidebar entries, and story sidebar entries (compare stories with test children and without; I kept stories with child tests as buttons to avoid disrupting changes made by @ghengeveld, but we could move these back to be links when we rework the sidebar for consistency)

Documentation

  • Add or update documentation reflecting your changes
  • If you are deprecating/removing a feature, make sure to update
    MIGRATION.MD

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

Release Notes

  • Improvements

    • Enhanced menu navigation with improved internal link handling and routing behavior.
  • Style

    • Refined modal and mobile drawer styling with improved border radius and theme-aware background rendering for better visual consistency across components.

✏️ Tip: You can customize this high-level summary in your review settings.

@Sidnioulz Sidnioulz added maintenance User-facing maintenance tasks ci:normal experiment This issue/PR is an experiment; it doesn't always follow production standards but will be monitored. labels Dec 19, 2025
@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Dec 19, 2025

View your CI Pipeline Execution ↗ for commit 6d9ccc2

Command Status Duration Result
nx run-many -t compile,check,knip,test,pretty-d... ✅ Succeeded 10m 46s View ↗

☁️ Nx Cloud last updated this comment at 2026-01-09 08:43:58 UTC

@Sidnioulz
Copy link
Copy Markdown
Member Author

Note: a shared util was started but discarded as Gert is also writing one. I had added a new manager error type:

export class EntryHasNoPathError extends StorybookError {
  constructor(
    public data: {
      entry: API_HashEntry;
    }
  ) {
    super({
      name: 'EntryHasNoPathError',
      category: Category.MANAGER_UTILS,
      code: 1,
      message: `Entry of type "${data.entry.type}" cannot be converted to a URL path. Only 'story' and 'docs' entries have paths. Full entry: ${JSON.stringify(
        data.entry,
        null,
        2
      )}`,
    });
  }
}

And I had edited three code paths:

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 19, 2025

📝 Walkthrough

Walkthrough

This PR refactors sidebar navigation components by introducing href-based link routing with internal/external navigation handling in Menu, splitting the StoryNode component into specialized StoryBranchNode and StoryLeafNode variants, and applying theme-based styling updates to modal and mobile drawer components. Updates accompanying story files and mocks accordingly.

Changes

Cohort / File(s) Summary
Sidebar Navigation - Link & Routing
code/core/src/manager/components/sidebar/Menu.tsx
Replaced direct ActionList.Item usage with ActionList.Action wrapper and composed anchor elements. Added href-based internal vs external link routing with preventDefault for internal links and target="_blank" for external ones. Consolidated click handling to guard disabled state first, then route appropriately, then invoke link-specific onClick and menu close logic.
Sidebar Navigation - Component Refactoring
code/core/src/manager/components/sidebar/TreeNode.tsx
Split exported StoryNode into two new public components: StoryBranchNode (with collapse indicator and branch handling) and StoryLeafNode (leaf-only with canonical rel). Added rel="canonical" to DocumentNode and TestNode. Public API surface restructured for story rendering.
Sidebar Navigation - Component Updates
code/core/src/manager/components/sidebar/Tree.tsx, code/core/src/manager/components/sidebar/TreeNode.stories.tsx
Updated to import and use new StoryBranchNode and StoryLeafNode components. Tree.tsx now conditionally renders StoryBranchNode for branch nodes and StoryLeafNode for leaf nodes. Story definitions updated to use appropriate new components replacing StoryNode.
Sidebar Navigation - Menu Configuration
code/core/src/manager/container/Menu.tsx, code/core/src/manager/components/sidebar/Menu.stories.tsx
Added optional internal?: boolean field to MenuItem type. Three menu items (about, guide, shortcuts) now include href properties and internal: true flag. Menu.stories.tsx consolidated common API mocks into getAPIMocks helper function.
Modal & Mobile UI Styling
code/core/src/components/components/Modal/Modal.styled.tsx, code/core/src/manager/components/mobile/about/MobileAbout.tsx
Modal.styled.tsx now receives theme parameter and applies theme-based background and rounded corners (10px 10px 0 0) to bottom-drawer variant. MobileAbout.tsx adds matching borderRadius.
Mobile Drawers - Styling Cleanup
code/core/src/manager/components/mobile/navigation/MobileAddonsDrawer.tsx, code/core/src/manager/components/mobile/navigation/MobileMenuDrawer.tsx
Removed StyledModal wrappers and custom styling; replaced with direct Modal component usage. MobileMenuDrawer adds explicit height="80vh" prop. Styling now inherited from Modal component.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

✨ Finishing touches
  • 📝 Generate docstrings

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

🧹 Nitpick comments (6)
code/core/src/manager/components/sidebar/Menu.tsx (1)

97-102: Consider adding rel="noopener noreferrer" for external links and reviewing rel="canonical" usage.

Two concerns:

  1. External links with target="_blank" should include rel="noopener noreferrer" to prevent the new page from accessing window.opener (security best practice).

  2. rel="canonical" is typically used to tell search engines which URL is the "preferred" version when duplicate content exists. Using it on all internal navigation links may not achieve the intended SEO benefit and could confuse crawlers.

🔎 Suggested fix
 {...(link.href && {
   as: 'a',
   href: link.href,
   target: link.internal ? undefined : '_blank',
-  rel: link.internal ? 'canonical' : undefined,
+  rel: link.internal ? undefined : 'noopener noreferrer',
 })}
code/core/src/manager-api/modules/stories.ts (3)

63-64: Duplicate DEFAULT_REF_ID constant.

This constant already exists in code/core/src/manager/components/sidebar/Sidebar.tsx (line 26). Consider importing from a shared location to avoid duplication and potential drift.


456-488: Duplicated URL construction logic.

The URL template on lines 470 and 487 is identical. Consider extracting to a helper function.

🔎 Suggested refactor
+ const buildEntryUrl = (entry: API_HashEntry, refId: string | undefined): string => {
+   const entryId = !refId || refId === DEFAULT_REF_ID ? entry.id : `${refId}_${entry.id}`;
+   return `./?path=/${entry.type}/${entryId}`;
+ };
+
  getSiblingComponentHref: (direction) => {
    const result = findSiblingComponent(direction);
    if (!result) {
      return null;
    }

    const { filteredIndex, refs, refId } = store.getState();
    const hash = refId ? refs[refId].filteredIndex || {} : filteredIndex;
    const entry = hash?.[result];
    if (!entry || (entry.type !== 'docs' && entry.type !== 'story')) {
      return null;
    }

-   // TODO: port this to getStoryUrls once available.
-   return `./?path=/${entry.type}/${!refId || refId === DEFAULT_REF_ID ? entry.id : `${refId}_${entry.id}`}`;
+   return buildEntryUrl(entry, refId);
  },

472-488: Redundant story data fetch in getSiblingStoryHref.

findSiblingStory already calls api.getData(storyId, refId) internally (line 368). Then getSiblingStoryHref calls it again (line 479). Consider returning the resolved hash from the helper or restructuring to avoid the duplicate lookup.

code/core/src/manager/components/sidebar/TreeNode.tsx (2)

154-175: Inner function name mismatch.

The exported component is StoryBranchNode, but the inner function passed to React.memo is named StoryNode. This causes the component to appear as "StoryNode" in React DevTools and error stack traces, which can be confusing during debugging.

🔎 Suggested fix
 export const StoryBranchNode: FC<ComponentProps<typeof BranchNode>> = React.memo(
-  function StoryNode({
+  function StoryBranchNode({
     theme,
     children,
     isExpandable = false,
     isExpanded = false,
     isSelected,
     ...props
   }) {

177-192: Inner function name mismatch.

Same issue as StoryBranchNode - the inner function is named StoryNode but should be StoryLeafNode for clarity in DevTools and stack traces.

🔎 Suggested fix
-export const StoryLeafNode: FC<ComponentProps<typeof LeafNode>> = React.memo(function StoryNode({
+export const StoryLeafNode: FC<ComponentProps<typeof LeafNode>> = React.memo(function StoryLeafNode({
   theme,
   children,
   ...props
 }) {
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e66b664 and 8d041cf.

📒 Files selected for processing (5)
  • code/core/src/manager-api/modules/stories.ts (4 hunks)
  • code/core/src/manager/components/sidebar/Menu.tsx (1 hunks)
  • code/core/src/manager/components/sidebar/Tree.tsx (4 hunks)
  • code/core/src/manager/components/sidebar/TreeNode.tsx (3 hunks)
  • code/core/src/manager/container/Menu.tsx (8 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use ESLint and Prettier configurations that are enforced in the codebase

Files:

  • code/core/src/manager/container/Menu.tsx
  • code/core/src/manager-api/modules/stories.ts
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/manager/components/sidebar/TreeNode.tsx
  • code/core/src/manager/components/sidebar/Menu.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Enable TypeScript strict mode

Files:

  • code/core/src/manager/container/Menu.tsx
  • code/core/src/manager-api/modules/stories.ts
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/manager/components/sidebar/TreeNode.tsx
  • code/core/src/manager/components/sidebar/Menu.tsx
code/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

code/**/*.{ts,tsx,js,jsx,mjs}: Use server-side logger from 'storybook/internal/node-logger' for Node.js code
Use client-side logger from 'storybook/internal/client-logger' for browser code
Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/core/src/manager/container/Menu.tsx
  • code/core/src/manager-api/modules/stories.ts
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/manager/components/sidebar/TreeNode.tsx
  • code/core/src/manager/components/sidebar/Menu.tsx
code/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Export functions that need to be tested from their modules

Files:

  • code/core/src/manager/container/Menu.tsx
  • code/core/src/manager-api/modules/stories.ts
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/manager/components/sidebar/TreeNode.tsx
  • code/core/src/manager/components/sidebar/Menu.tsx
code/**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

code/**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier with --write flag to format code before committing
Run ESLint with yarn lint:js:cmd to check for linting issues and fix errors before committing

Files:

  • code/core/src/manager/container/Menu.tsx
  • code/core/src/manager-api/modules/stories.ts
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/manager/components/sidebar/TreeNode.tsx
  • code/core/src/manager/components/sidebar/Menu.tsx
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.889Z
Learning: Follow existing patterns and conventions in the Storybook codebase
📚 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-api/modules/stories.ts
  • code/core/src/manager/components/sidebar/TreeNode.tsx
📚 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/Tree.tsx
  • code/core/src/manager/components/sidebar/TreeNode.tsx
📚 Learning: 2025-11-28T14:50:24.889Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.889Z
Learning: Follow existing patterns and conventions in the Storybook codebase

Applied to files:

  • code/core/src/manager/components/sidebar/TreeNode.tsx
📚 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/TreeNode.tsx
🧬 Code graph analysis (3)
code/core/src/manager-api/modules/stories.ts (2)
code/core/src/manager/components/sidebar/Sidebar.tsx (1)
  • DEFAULT_REF_ID (27-27)
code/core/src/csf/csf-factories.ts (1)
  • story (114-118)
code/core/src/manager/components/sidebar/Tree.tsx (1)
code/core/src/manager/components/sidebar/TreeNode.tsx (6)
  • DocumentNode (140-152)
  • StoryLeafNode (177-192)
  • ComponentNode (117-138)
  • GroupNode (96-115)
  • StoryBranchNode (154-175)
  • TestNode (194-209)
code/core/src/manager/components/sidebar/Menu.tsx (1)
code/lib/cli-storybook/src/link.ts (1)
  • link (15-103)
⏰ 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: Core Unit Tests, windows-latest
🔇 Additional comments (8)
code/core/src/manager/container/Menu.tsx (3)

24-27: LGTM!

The internal flag extension to MenuItem is clean and integrates well with the existing type structure.


81-92: LGTM!

The about menu item correctly adds href for SEO/UX purposes while retaining the onClick handler for router-based navigation.


164-175: No changes needed. The api parameter is already included in the dependency array on line 174: [api, enableShortcuts, shortcutKeys]. All similar menu items (down, prev, next) follow the same correct pattern.

Likely an incorrect or invalid review comment.

code/core/src/manager/components/sidebar/Menu.tsx (1)

106-127: LGTM!

The click handler correctly prevents default navigation for both disabled links and internal links that should use router-based navigation while passing the link metadata to the onClick callback.

code/core/src/manager/components/sidebar/Tree.tsx (4)

50-58: LGTM!

Import changes correctly align with the new StoryBranchNode and StoryLeafNode exports from TreeNode.tsx.


265-265: LGTM!

Correct use of StoryLeafNode for docs/story leaf nodes that are not expandable.


389-391: LGTM!

The branch node mapping correctly uses StoryBranchNode for story items that have children, maintaining consistency with the new node type structure.


470-470: LGTM!

Correct use of StoryLeafNode as the fallback for non-test story/docs items.

Comment thread code/core/src/manager-api/modules/stories.ts Outdated
Comment thread code/core/src/manager/components/sidebar/TreeNode.tsx
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: 0

♻️ Duplicate comments (1)
code/core/src/manager-api/modules/stories.ts (1)

341-381: Inconsistent fallback handling between findSiblingComponent and findSiblingStory.

Line 353 uses refs[refId].filteredIndex || {} while line 374 uses refs[refId].filteredIndex without the fallback. This inconsistency is also repeated in getSiblingComponentHref (line 463) and getSiblingStoryHref (line 479).

Both approaches eventually return undefined/null when the index is unavailable, but the inconsistent pattern could cause confusion or subtle bugs. Consider standardizing the approach across all four locations.

🔎 Suggested fix for consistency

Apply the same fallback pattern to both helpers and their corresponding href methods:

  const findSiblingStory = (direction: Direction): StoryId | undefined => {
    const { filteredIndex, storyId, refs, refId } = store.getState();
    const story = api.getData(storyId, refId);

    if (!story) {
      return undefined;
    }

-   const hash = refId ? refs[refId].filteredIndex : filteredIndex;
+   const hash = refId ? refs[refId].filteredIndex || {} : filteredIndex;

    if (!hash) {
      return undefined;
    }

    return api.findSiblingStoryId(storyId, hash, direction, false);
  };

And similarly for getSiblingStoryHref at line 479.

🧹 Nitpick comments (2)
code/core/src/manager/components/sidebar/TreeNode.stories.tsx (1)

91-171: Consider adding StoryLeafNode examples for completeness.

The Nested and NestedLongName stories demonstrate how various node types render at different depths, but only include StoryBranchNode for story nodes. Adding StoryLeafNode examples at some depths would provide more complete coverage of how leaf nodes render in hierarchical contexts.

Example: Add a StoryLeafNode to the Nested story
     <StoryBranchNode depth={2}>Two</StoryBranchNode>
+    <StoryLeafNode depth={2}>Two (leaf)</StoryLeafNode>
     <TestNode depth={3}>Three</TestNode>
code/core/src/manager-api/modules/stories.ts (1)

456-487: URL construction pattern differs from gotoStory and duplicates logic.

The URL construction in these methods uses refId from state:

!refId || refId === DEFAULT_REF_ID ? entry.id : `${refId}_${entry.id}`

However, gotoStory (line 509) uses entry.refId from the entry itself:

entry.refId ? `${entry.refId}_${entry.id}` : entry.id

Using entry.refId would be more consistent and reliable, since the entry already has refId populated when retrieved from the index. Consider aligning with the gotoStory pattern.

Additionally, the URL construction logic is duplicated between getSiblingComponentHref and getSiblingStoryHref. As noted in the TODO comments, this should be extracted to a shared utility (e.g., getStoryUrls) once available.

🔎 Proposed fix for URL construction consistency
  getSiblingComponentHref: (direction) => {
    const result = findSiblingComponent(direction);
    if (!result) {
      return null;
    }

    const { filteredIndex, refs, refId } = store.getState();
    const hash = refId ? refs[refId].filteredIndex || {} : filteredIndex;
    const entry = hash?.[result];
    if (!entry || (entry.type !== 'docs' && entry.type !== 'story')) {
      return null;
    }

    // TODO: port this to getStoryUrls once available.
-   return `./?path=/${entry.type}/${!refId || refId === DEFAULT_REF_ID ? entry.id : `${refId}_${entry.id}`}`;
+   return `./?path=/${entry.type}/${entry.refId ? `${entry.refId}_${entry.id}` : entry.id}`;
  },

Apply the same change to getSiblingStoryHref at line 486.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8d041cf and 6d65c94.

📒 Files selected for processing (2)
  • code/core/src/manager-api/modules/stories.ts
  • code/core/src/manager/components/sidebar/TreeNode.stories.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use ESLint and Prettier configurations that are enforced in the codebase

Files:

  • code/core/src/manager/components/sidebar/TreeNode.stories.tsx
  • code/core/src/manager-api/modules/stories.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Enable TypeScript strict mode

Files:

  • code/core/src/manager/components/sidebar/TreeNode.stories.tsx
  • code/core/src/manager-api/modules/stories.ts
code/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

code/**/*.{ts,tsx,js,jsx,mjs}: Use server-side logger from 'storybook/internal/node-logger' for Node.js code
Use client-side logger from 'storybook/internal/client-logger' for browser code
Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/core/src/manager/components/sidebar/TreeNode.stories.tsx
  • code/core/src/manager-api/modules/stories.ts
code/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Export functions that need to be tested from their modules

Files:

  • code/core/src/manager/components/sidebar/TreeNode.stories.tsx
  • code/core/src/manager-api/modules/stories.ts
code/**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

code/**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier with --write flag to format code before committing
Run ESLint with yarn lint:js:cmd to check for linting issues and fix errors before committing

Files:

  • code/core/src/manager/components/sidebar/TreeNode.stories.tsx
  • code/core/src/manager-api/modules/stories.ts
🧠 Learnings (4)
📚 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/TreeNode.stories.tsx
📚 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/TreeNode.stories.tsx
  • code/core/src/manager-api/modules/stories.ts
📚 Learning: 2025-11-28T14:50:24.889Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.889Z
Learning: Follow existing patterns and conventions in the Storybook codebase

Applied to files:

  • code/core/src/manager/components/sidebar/TreeNode.stories.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/TreeNode.stories.tsx
  • code/core/src/manager-api/modules/stories.ts
🧬 Code graph analysis (1)
code/core/src/manager/components/sidebar/TreeNode.stories.tsx (1)
code/core/src/manager/components/sidebar/TreeNode.tsx (4)
  • StoryLeafNode (177-192)
  • StoryBranchNode (154-175)
  • TestNode (194-209)
  • ComponentNode (117-138)
⏰ 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). (3)
  • GitHub Check: normal
  • GitHub Check: Core Unit Tests, windows-latest
  • GitHub Check: nx
🔇 Additional comments (6)
code/core/src/manager/components/sidebar/TreeNode.stories.tsx (4)

6-13: LGTM: Import changes correctly reflect the component split.

The imports now include both StoryBranchNode and StoryLeafNode, aligning with the refactoring that splits the previous StoryNode into separate branch and leaf components.


19-19: LGTM: Appropriate default component selection.

Setting StoryLeafNode as the default component is a sensible choice, as leaf nodes represent the simpler, more common case.


34-35: LGTM: Clear demonstration of branch vs. leaf distinction.

The Types story effectively showcases both node variants with descriptive labels that clarify their semantic differences.


173-351: LGTM: Consistent use of leaf nodes for selection testing.

The Selection and SelectionWithLongName stories appropriately use StoryLeafNode throughout, correctly testing selection and highlighting states on leaf nodes.

code/core/src/manager-api/modules/stories.ts (2)

442-455: Clean refactoring with shared helper functions.

Extracting the sibling-finding logic into findSiblingComponent and findSiblingStory reduces duplication and enables reuse by the new getSibling*Href methods. The navigation logic remains unchanged.


63-64: The constant value 'storybook_internal' is confirmed correct and consistently used throughout the codebase to determine when to omit the ref prefix from generated URLs.

@storybook-app-bot
Copy link
Copy Markdown

storybook-app-bot Bot commented Dec 22, 2025

Package Benchmarks

Commit: 6d9ccc2, ran on 9 January 2026 at 08:41:38 UTC

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

@storybook/nextjs

Before After Difference
Dependency count 534 538 🚨 +4 🚨
Self size 646 KB 646 KB 🚨 +64 B 🚨
Dependency size 59.16 MB 59.19 MB 🚨 +27 KB 🚨
Bundle Size Analyzer Link Link

@storybook/react-webpack5

Before After Difference
Dependency count 274 278 🚨 +4 🚨
Self size 24 KB 24 KB 0 B
Dependency size 44.09 MB 44.12 MB 🚨 +27 KB 🚨
Bundle Size Analyzer Link Link

@storybook/preset-create-react-app

Before After Difference
Dependency count 64 68 🚨 +4 🚨
Self size 32 KB 32 KB 🚨 +30 B 🚨
Dependency size 5.95 MB 5.98 MB 🚨 +28 KB 🚨
Bundle Size Analyzer Link Link

@storybook/preset-react-webpack

Before After Difference
Dependency count 166 170 🚨 +4 🚨
Self size 18 KB 18 KB 🚨 +52 B 🚨
Dependency size 31.23 MB 31.26 MB 🚨 +28 KB 🚨
Bundle Size Analyzer Link Link

@Sidnioulz Sidnioulz requested a review from ndelangen December 22, 2025 14:45
@Sidnioulz Sidnioulz force-pushed the sidnioulz/exp-use-links-to-navigate branch from 6d65c94 to 63b3420 Compare December 22, 2025 17:55
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: 0

🧹 Nitpick comments (4)
code/core/src/manager/components/sidebar/Menu.stories.tsx (4)

19-43: Good consolidation of API mocks.

The new getAPIMocks() helper centralizes common mock definitions and includes the new sibling navigation methods (getSiblingComponentHref, getSiblingStoryHref) mentioned in the PR objectives.

Consider making this helper accept parameters for commonly-overridden values (e.g., isWhatsNewUnread, custom shortcuts) to eliminate the override pattern seen in the stories below.

Optional: Parameterized mock factory
-const getAPIMocks = () => ({
+const getAPIMocks = (overrides: Partial<ReturnType<typeof getAPIMocks>> = {}) => ({
   getShortcutKeys: fn()
     .mockName('api::getShortcutKeys')
     .mockImplementation(() => ({})),
   getAddonsShortcuts: fn()
     .mockName('api::getAddonsShortcuts')
     .mockImplementation(() => ({})),
   versionUpdateAvailable: fn()
     .mockName('api::versionUpdateAvailable')
     .mockImplementation(() => false),
   isWhatsNewUnread: fn()
     .mockName('api::isWhatsNewUnread')
     .mockImplementation(() => false),
   getDocsUrl: fn()
     .mockName('api::getDocsUrl')
     .mockImplementation(() => 'https://storybook.js.org/docs/'),
   toggleNav: fn().mockName('api::toggleNav'),
   toggleToolbar: fn().mockName('api::toggleToolbar'),
   togglePanel: fn().mockName('api::togglePanel'),
   jumpToComponent: fn().mockName('api::jumpToComponent'),
   getSiblingComponentHref: fn().mockName('api::getSiblingComponentHref'),
   jumpToStory: fn().mockName('api::jumpToStory'),
   getSiblingStoryHref: fn().mockName('api::getSiblingStoryHref'),
   emit: fn().mockName('api::emit'),
+  ...overrides,
 });

This would allow stories to call getAPIMocks({ isWhatsNewUnread: fn().mockImplementation(() => true) }) instead of spreading and overriding.


119-126: Remove redundant mock overrides.

Lines 121-125 override methods with implementations identical to the getAPIMocks() defaults. These overrides can be safely removed since spreading getAPIMocks() already provides the same values.

Simplified code
 const menu = useMenu({
   api: {
     ...getAPIMocks(),
-    // @ts-expect-error (Converted from ts-ignore)
-    getShortcutKeys: () => ({}),
-    getAddonsShortcuts: () => ({}),
-    versionUpdateAvailable: () => false,
-    isWhatsNewUnread: () => false,
-    getDocsUrl: () => 'https://storybook.js.org/docs/',
   },
   showToolbar: false,

170-190: Remove redundant mock overrides.

Only getShortcutKeys (lines 172-185) needs to override the default from getAPIMocks(). The other overrides on lines 186-189 are redundant.

Simplified code
 const menu = useMenu({
   api: {
     ...getAPIMocks(),
     // @ts-expect-error (invalid)
     getShortcutKeys: () => ({
       shortcutsPage: ['⌘', '⇧​', ','],
       toggleNav: ['⌥', 'S'],
       togglePanel: ['⌥', 'A'],
       toolbar: ['⌥', 'T'],
       panelPosition: ['⌥', 'D'],
       fullScreen: ['⌥', 'F'],
       search: ['⌥', 'K'],
       prevComponent: ['⌥', '↑'],
       nextComponent: ['⌥', '↓'],
       prevStory: ['⌥', '←'],
       nextStory: ['⌥', '→'],
       collapseAll: ['⌥', '⇧', '↑'],
     }),
-    getAddonsShortcuts: () => ({}),
-    versionUpdateAvailable: () => false,
-    isWhatsNewUnread: () => false,
-    getDocsUrl: () => 'https://storybook.js.org/docs/',
   },
   showToolbar: false,

223-230: Remove redundant mock overrides.

Only isWhatsNewUnread (line 228) needs to override the default from getAPIMocks() to return true. The other overrides on lines 225-227 and 229 are redundant.

Simplified code
 const menu = useMenu({
   api: {
     ...getAPIMocks(),
-    // @ts-expect-error (invalid)
-    getShortcutKeys: () => ({}),
-    getAddonsShortcuts: () => ({}),
-    versionUpdateAvailable: () => false,
     isWhatsNewUnread: () => true,
-    getDocsUrl: () => 'https://storybook.js.org/docs/',
   },
   showToolbar: false,
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 63b3420 and c2f574b.

📒 Files selected for processing (1)
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use ESLint and Prettier configurations that are enforced in the codebase

Files:

  • code/core/src/manager/components/sidebar/Menu.stories.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Enable TypeScript strict mode

Files:

  • code/core/src/manager/components/sidebar/Menu.stories.tsx
code/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

code/**/*.{ts,tsx,js,jsx,mjs}: Use server-side logger from 'storybook/internal/node-logger' for Node.js code
Use client-side logger from 'storybook/internal/client-logger' for browser code
Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/core/src/manager/components/sidebar/Menu.stories.tsx
code/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Export functions that need to be tested from their modules

Files:

  • code/core/src/manager/components/sidebar/Menu.stories.tsx
code/**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

code/**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier with --write flag to format code before committing
Run ESLint with yarn lint:js:cmd to check for linting issues and fix errors before committing

Files:

  • code/core/src/manager/components/sidebar/Menu.stories.tsx
🧠 Learnings (6)
📓 Common learnings
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.
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.
📚 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/Menu.stories.tsx
📚 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/Menu.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 implementations should match the expected return type of the original function

Applied to files:

  • code/core/src/manager/components/sidebar/Menu.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/Menu.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 properties and methods that the test subject uses in Vitest tests

Applied to files:

  • code/core/src/manager/components/sidebar/Menu.stories.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). (3)
  • GitHub Check: normal
  • GitHub Check: Core Unit Tests, windows-latest
  • GitHub Check: nx

@Sidnioulz
Copy link
Copy Markdown
Member Author

@ghengeveld @ndelangen could either of you please review this PR? I still need to merge it to get indexing results from Google Search Console for the About page project. Thanks!

Comment thread code/core/src/manager/components/sidebar/Menu.tsx
Comment thread code/core/src/manager/container/Menu.tsx Outdated
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: 0

🧹 Nitpick comments (1)
code/core/src/manager-api/modules/stories.ts (1)

399-399: Redundant state read.

Line 399 retrieves refId from state, but it was already retrieved on line 382 and nothing modifies the store between these lines. The second read shadows the outer variable and adds unnecessary overhead.

🔎 Suggested simplification
  const result = api.findSiblingStoryId(storyId, hash, direction, true);

  if (result) {
-   const { refId } = store.getState();
    api.selectStory(result, undefined, { ref: refId });
  }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c2f574b and 3df2150.

📒 Files selected for processing (3)
  • code/core/src/manager-api/modules/stories.ts
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/container/Menu.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • code/core/src/manager/container/Menu.tsx
  • code/core/src/manager/components/sidebar/Menu.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-api/modules/stories.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 command cross-env NODE_ENV=production eslint --cache --cache-location=../.cache/eslint --ext .js,.jsx,.json,.html,.ts,.tsx,.mjs --report-unused-disable-directives to fix linting errors before committing

Files:

  • code/core/src/manager-api/modules/stories.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Enable TypeScript strict mode across all packages

Files:

  • code/core/src/manager-api/modules/stories.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 use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/core/src/manager-api/modules/stories.ts
code/{core,lib,addons,builders,frameworks,presets}/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use logger from storybook/internal/node-logger for server-side logging in Node.js code

Files:

  • code/core/src/manager-api/modules/stories.ts
🧠 Learnings (3)
📓 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/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.
📚 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-api/modules/stories.ts
📚 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-api/modules/stories.ts
⏰ 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). (3)
  • GitHub Check: normal
  • GitHub Check: Core Unit Tests, windows-latest
  • GitHub Check: nx

@Sidnioulz Sidnioulz force-pushed the sidnioulz/exp-use-links-to-navigate branch from 3df2150 to 82a081c Compare January 5, 2026 13:50
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: 0

🧹 Nitpick comments (2)
code/core/src/manager/components/sidebar/TreeNode.tsx (2)

155-176: Fix the function name to match the export.

The component is exported as StoryBranchNode but the internal function is named StoryNode (line 156). This mismatch can cause confusion in React DevTools and stack traces.

🔎 Proposed fix
 export const StoryBranchNode: FC<ComponentProps<typeof BranchNode>> = React.memo(
-  function StoryNode({
+  function StoryBranchNode({
     theme,
     children,
     isExpandable = false,
     isExpanded = false,
     isSelected,
     ...props
   }) {

178-193: Fix the function name to match the export.

The component is exported as StoryLeafNode but the internal function is named StoryNode (line 178). This mismatch can cause confusion in React DevTools and stack traces.

🔎 Proposed fix
-export const StoryLeafNode: FC<ComponentProps<typeof LeafNode>> = React.memo(function StoryNode({
+export const StoryLeafNode: FC<ComponentProps<typeof LeafNode>> = React.memo(function StoryLeafNode({
   theme,
   children,
   ...props
 }) {
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3df2150 and 82a081c.

📒 Files selected for processing (7)
  • code/core/src/manager-api/modules/stories.ts
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.tsx
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/manager/components/sidebar/TreeNode.stories.tsx
  • code/core/src/manager/components/sidebar/TreeNode.tsx
  • code/core/src/manager/container/Menu.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • code/core/src/manager/container/Menu.tsx
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/manager-api/modules/stories.ts
  • code/core/src/manager/components/sidebar/Menu.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/TreeNode.tsx
  • code/core/src/manager/components/sidebar/TreeNode.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.tsx
**/*.{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 command cross-env NODE_ENV=production eslint --cache --cache-location=../.cache/eslint --ext .js,.jsx,.json,.html,.ts,.tsx,.mjs --report-unused-disable-directives to fix linting errors before committing

Files:

  • code/core/src/manager/components/sidebar/TreeNode.tsx
  • code/core/src/manager/components/sidebar/TreeNode.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Enable TypeScript strict mode across all packages

Files:

  • code/core/src/manager/components/sidebar/TreeNode.tsx
  • code/core/src/manager/components/sidebar/TreeNode.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.tsx
**/*.{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 use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/core/src/manager/components/sidebar/TreeNode.tsx
  • code/core/src/manager/components/sidebar/TreeNode.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.tsx
code/{core,lib,addons,builders,frameworks,presets}/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use logger from storybook/internal/node-logger for server-side logging in Node.js code

Files:

  • code/core/src/manager/components/sidebar/TreeNode.tsx
  • code/core/src/manager/components/sidebar/TreeNode.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.tsx
🧠 Learnings (5)
📓 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/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.
📚 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/TreeNode.tsx
  • code/core/src/manager/components/sidebar/TreeNode.stories.tsx
📚 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/TreeNode.tsx
  • code/core/src/manager/components/sidebar/TreeNode.stories.tsx
📚 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/TreeNode.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/TreeNode.stories.tsx
🧬 Code graph analysis (1)
code/core/src/manager/components/sidebar/TreeNode.stories.tsx (1)
code/core/src/manager/components/sidebar/TreeNode.tsx (2)
  • StoryLeafNode (178-193)
  • StoryBranchNode (155-176)
⏰ 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). (3)
  • GitHub Check: normal
  • GitHub Check: nx
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (5)
code/core/src/manager/components/sidebar/Menu.tsx (1)

97-127: LGTM! Clean implementation of internal vs external link handling.

The conditional rendering logic correctly:

  • Converts menu items with href to anchor elements
  • Opens external links in new tabs while keeping internal links in the same context
  • Applies rel="canonical" to internal navigation links for SEO
  • Prevents browser navigation for internal links while preserving href for accessibility and UX

The click handler appropriately guards against disabled state and prevents default navigation for internal links.

code/core/src/manager/components/sidebar/TreeNode.tsx (1)

155-193: Clean refactoring to separate branch and leaf story nodes.

The split of the previous StoryNode into StoryBranchNode (for expandable stories with tests) and StoryLeafNode (for leaf stories) provides clearer semantics and better aligns with the component hierarchy structure.

code/core/src/manager/components/sidebar/TreeNode.stories.tsx (3)

6-13: LGTM! Imports correctly updated.

The imports properly reflect the new StoryBranchNode and StoryLeafNode components, replacing the previous StoryNode export.


30-38: Nice addition showing both branch and leaf variants.

The Types story effectively demonstrates the distinction between StoryBranchNode (for stories with tests) and StoryLeafNode (for leaf node stories), making the component API clear.


91-110: Correct usage of StoryBranchNode and StoryLeafNode throughout stories.

The stories appropriately use:

  • StoryBranchNode for nested/hierarchical examples where stories may have children
  • StoryLeafNode for selection examples where stories are terminal nodes

This aligns well with the semantic distinction introduced in TreeNode.tsx.

Also applies to: 173-211

Comment thread code/core/src/manager/components/sidebar/Menu.tsx Outdated
Comment thread code/core/src/manager-api/modules/stories.ts Outdated
Comment thread code/core/src/manager/components/sidebar/Menu.tsx Outdated
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

🤖 Fix all issues with AI agents
In @code/core/src/manager/components/sidebar/Menu.tsx:
- Line 141: The anchor rendering in Menu.tsx incorrectly sets rel={link.internal
? 'canonical' : 'noreferrer'}; change it to omit or neutralize the rel for
internal links (e.g., set rel={link.internal ? undefined : 'noreferrer'} or
remove the rel prop when link.internal is true) and apply the same fix to the
mobile About button rendering; update the conditional that produces 'canonical'
so internal anchors do not use the canonical rel value.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 82a081c and 6d9ccc2.

📒 Files selected for processing (5)
  • code/core/src/components/components/Modal/Modal.styled.tsx
  • code/core/src/manager/components/mobile/about/MobileAbout.tsx
  • code/core/src/manager/components/mobile/navigation/MobileAddonsDrawer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileMenuDrawer.tsx
  • code/core/src/manager/components/sidebar/Menu.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/Menu.tsx
  • code/core/src/manager/components/mobile/navigation/MobileAddonsDrawer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileMenuDrawer.tsx
  • code/core/src/manager/components/mobile/about/MobileAbout.tsx
  • code/core/src/components/components/Modal/Modal.styled.tsx
**/*.{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 command cross-env NODE_ENV=production eslint --cache --cache-location=../.cache/eslint --ext .js,.jsx,.json,.html,.ts,.tsx,.mjs --report-unused-disable-directives to fix linting errors before committing

Files:

  • code/core/src/manager/components/sidebar/Menu.tsx
  • code/core/src/manager/components/mobile/navigation/MobileAddonsDrawer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileMenuDrawer.tsx
  • code/core/src/manager/components/mobile/about/MobileAbout.tsx
  • code/core/src/components/components/Modal/Modal.styled.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Enable TypeScript strict mode across all packages

Files:

  • code/core/src/manager/components/sidebar/Menu.tsx
  • code/core/src/manager/components/mobile/navigation/MobileAddonsDrawer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileMenuDrawer.tsx
  • code/core/src/manager/components/mobile/about/MobileAbout.tsx
  • code/core/src/components/components/Modal/Modal.styled.tsx
**/*.{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 use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/core/src/manager/components/sidebar/Menu.tsx
  • code/core/src/manager/components/mobile/navigation/MobileAddonsDrawer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileMenuDrawer.tsx
  • code/core/src/manager/components/mobile/about/MobileAbout.tsx
  • code/core/src/components/components/Modal/Modal.styled.tsx
code/{core,lib,addons,builders,frameworks,presets}/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use logger from storybook/internal/node-logger for server-side logging in Node.js code

Files:

  • code/core/src/manager/components/sidebar/Menu.tsx
  • code/core/src/manager/components/mobile/navigation/MobileAddonsDrawer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileMenuDrawer.tsx
  • code/core/src/manager/components/mobile/about/MobileAbout.tsx
  • code/core/src/components/components/Modal/Modal.styled.tsx
🧠 Learnings (3)
📚 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/Menu.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/Menu.tsx
📚 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/Menu.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). (3)
  • GitHub Check: normal
  • GitHub Check: Core Unit Tests, windows-latest
  • GitHub Check: nx
🔇 Additional comments (8)
code/core/src/components/components/Modal/Modal.styled.tsx (2)

115-115: LGTM: Theme-based styling for bottom-drawer variant.

The addition of the theme parameter and application of borderRadius, along with using theme.background.content, properly aligns the bottom-drawer styling with the theme system and creates visual consistency across mobile components.

Also applies to: 141-141, 146-146


141-142: No action required. The overflow: 'hidden' on the bottom-drawer variant is intentional and follows an established design pattern. Mobile drawer consumers (MobileMenuDrawer, MobileAddonsDrawer) properly handle scrolling through child components like ScrollArea, which manage their own scroll behavior independently. This prevents double scrollbars and gives full control over scroll implementation, consistent with similar container patterns in the codebase.

code/core/src/manager/components/mobile/about/MobileAbout.tsx (1)

122-122: LGTM: Consistent border radius styling.

The borderRadius: '10px 10px 0 0' addition rounds the top corners and aligns with the visual pattern established in Modal.styled.tsx for bottom-drawer variants.

code/core/src/manager/components/mobile/navigation/MobileAddonsDrawer.tsx (1)

22-32: LGTM: Simplified drawer implementation.

Removing the StyledModal wrapper and using Modal directly with variant="bottom-drawer" centralizes styling logic and reduces redundancy. The explicit height="42vh" prop appropriately sizes the addon drawer.

code/core/src/manager/components/mobile/navigation/MobileMenuDrawer.tsx (1)

23-34: LGTM: Consistent drawer refactoring.

The removal of StyledModal in favor of direct Modal usage mirrors the MobileAddonsDrawer pattern and centralizes styling. The height="80vh" appropriately accommodates the menu navigation and nested MobileAbout component.

code/core/src/manager/components/sidebar/Menu.tsx (3)

114-135: Well-structured onClick handler flow.

The onClick logic properly handles:

  • Disabled state guard (prevents any interaction)
  • Internal link navigation (prevents browser navigation while preserving href)
  • Link-specific onClick callback with complete metadata
  • Menu closing behavior via closeOnClick flag

122-124: Internal link routing is correctly implemented.

The link.internal flag is properly set for menu items (about, guide, and shortcuts all have internal: true), and the link.onClick callbacks properly invoke navigation through API methods (api.changeSettingsTab(), api.toggleNav(), api.jumpToComponent(), etc.). The preventDefault pattern correctly prevents browser navigation while ensuring the appropriate API method is called to handle routing. No action needed.


175-178: The mobile About button navigation works correctly—no fix needed.

The onClick handler properly invokes setMobileAboutOpen to toggle the modal state. The preventDefault() is intentional because navigation is handled via React state, not URL navigation. The MobileAbout component is rendered in MobileMenuDrawer and responds to state changes, displaying the about panel when opened.

Likely an incorrect or invalid review comment.

<a
href={link.href}
target={link.internal ? undefined : '_blank'}
rel={link.internal ? 'canonical' : 'noreferrer'}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: rel="canonical" is incorrect for anchor tags.

The rel="canonical" attribute is semantically invalid on <a> elements. The canonical link relation is intended for <link> elements in the document <head> to indicate the preferred URL for search engine indexing, not for navigation anchors.

For internal navigation links:

  • Omit the rel attribute entirely, OR
  • Use a descriptive value like rel="internal" if needed for styling/scripting

This same issue appears on line 182 for the mobile About button.

🔧 Suggested fix
                  {link.href ? (
                    <a
                      href={link.href}
                      target={link.internal ? undefined : '_blank'}
-                     rel={link.internal ? 'canonical' : 'noreferrer'}
+                     rel={link.internal ? undefined : 'noreferrer'}
                    >
                      {linkContent}
                    </a>

And for the mobile button (line 182):

-          <a href="./?path=/settings/about" rel="canonical">
+          <a href="./?path=/settings/about">
            <CogIcon />
          </a>
📝 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.

Suggested change
rel={link.internal ? 'canonical' : 'noreferrer'}
rel={link.internal ? undefined : 'noreferrer'}
🤖 Prompt for AI Agents
In @code/core/src/manager/components/sidebar/Menu.tsx at line 141, The anchor
rendering in Menu.tsx incorrectly sets rel={link.internal ? 'canonical' :
'noreferrer'}; change it to omit or neutralize the rel for internal links (e.g.,
set rel={link.internal ? undefined : 'noreferrer'} or remove the rel prop when
link.internal is true) and apply the same fix to the mobile About button
rendering; update the conditional that produces 'canonical' so internal anchors
do not use the canonical rel value.

@ghengeveld ghengeveld merged commit 293bfec into next Jan 9, 2026
69 checks passed
@ghengeveld ghengeveld deleted the sidnioulz/exp-use-links-to-navigate branch January 9, 2026 09:14
@github-actions github-actions Bot mentioned this pull request Jan 9, 2026
19 tasks
@ghengeveld ghengeveld added needs qa Indicates that this needs manual QA during the upcoming minor/major release and removed needs triage labels Jan 14, 2026
@ndelangen ndelangen removed needs qa Indicates that this needs manual QA during the upcoming minor/major release experiment This issue/PR is an experiment; it doesn't always follow production standards but will be monitored. labels Jan 16, 2026
@Sidnioulz Sidnioulz added the experiment This issue/PR is an experiment; it doesn't always follow production standards but will be monitored. label Jan 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:normal experiment This issue/PR is an experiment; it doesn't always follow production standards but will be monitored. maintenance User-facing maintenance tasks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants