Skip to content

Conversation

@ghengeveld
Copy link
Member

@ghengeveld ghengeveld commented Dec 23, 2025

What I did

  • Added getStoryHrefs method to urls manager API module
  • Added keyboard shortcut for "open in isolation mode" (Option-Shift-I / Alt-Shift-I), using api.getStoryHrefs
  • Updated preview iframe URL to use api.getStoryHrefs
  • Updated ShareMenu component to use api.getStoryHrefs for all URLs, including QR code
  • Deprecated old getStoryHref util in manager components
  • Copied old getStoryHref into addon-docs source, because that's the only place it's still used and cannot be replaced with api.getStoryHrefs due to being in the preview. Yet another reason to not render docs pages in the preview.

Checklist for Contributors

Testing

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

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

Manual testing

  1. Open a story, select a few globals (e.g. Grid and Viewport tools), and change some Controls to get args in the URL. Verify that the preview behaves accordingly.
  2. Press Option-Shift-I (or Alt-Shift-I). This should open a new browser tab with the story preview (iframe.html). Verify that both globals and args are still applied.
  3. Use the Share tool (top-right) to check the story link, isolation mode link and QR code to all link to the story correctly.
  4. Navigate to a composed Storybook's story (setup composition if you have to) and check the same things. Globals should not be carried over in isolation mode.

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

  • New Features

    • Added getStoryHrefs API method for retrieving manager and preview URLs with support for custom query parameters, ref linking, and view modes.
    • Added "Open story in isolation" shortcut (Alt+Shift+I).
    • Added STORYBOOK_NETWORK_ADDRESS and PREVIEW_URL global configuration options.
  • Bug Fixes

    • Fixed "Open in editor" context menu option appearing when no file path is available.
  • Documentation

    • Added comprehensive addon API documentation and code examples for getStoryHrefs, openInEditor, toggleFullscreen, togglePanel, addNotification, and getCurrentStoryData.

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

@nx-cloud
Copy link

nx-cloud bot commented Dec 23, 2025

View your CI Pipeline Execution ↗ for commit e518754

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

☁️ Nx Cloud last updated this comment at 2026-01-05 14:28:23 UTC

@nx-cloud
Copy link

nx-cloud bot commented Dec 23, 2025

View your CI Pipeline Execution ↗ for commit 84c18fa


☁️ Nx Cloud last updated this comment at 2025-12-23 12:01:37 UTC

@coderabbitai

This comment was marked as spam.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
code/core/src/manager-api/modules/url.ts (2)

34-48: Consider adding input validation to parseSerializedParam.

The parseSerializedParam function splits on semicolons and colons without validating the input format. Malformed input (e.g., "a:b:c", "key:", ":value") could produce unexpected entries in the returned object.

🔎 Suggested improvement with validation
 const parseSerializedParam = (param: string) =>
   Object.fromEntries(
     param
       .split(';')
       .map((pair) => pair.split(':'))
-      .filter(([key, value]) => key && value)
+      .filter(([key, value]) => key && value !== undefined && pair.split(':').length === 2)
   );

Note: The current filter already checks key && value, which should handle most edge cases. Consider if additional validation is needed based on expected usage patterns.


227-272: Document the asymmetric globals handling for refs.

Line 270 conditionally excludes globals from the preview URL when a refId is present (${refId ? '' : globalsParam}). This asymmetry between manager and preview URLs for refs could be confusing to consumers.

Consider adding a comment explaining why globals are dropped for ref previews:

       return {
         managerHref: `${managerBase}?path=/${viewMode}/${refId ? `${refId}_` : ''}${storyId}${argsParam}${globalsParam}${customParams}`,
+        // Globals are omitted from ref preview URLs as they are managed by the external Storybook instance
         previewHref: `${previewBase}?id=${storyId}&viewMode=${viewMode}${argsParam}${refId ? '' : globalsParam}${customParams}`,
       };
code/addons/docs/src/blocks/getStoryHref.ts (1)

15-24: Consider edge case handling in parseQuery.

The parseQuery function doesn't handle edge cases like:

  • Missing values (e.g., key= or just key)
  • Multiple = signs in a pair (e.g., key=value=extra)
  • Empty pairs from consecutive & characters

While these might be unlikely in practice, they could cause subtle bugs.

🔎 More robust parsing
 function parseQuery(queryString: string) {
   const query: Record<string, string> = {};
   const pairs = queryString.split('&');

   for (let i = 0; i < pairs.length; i++) {
+    if (!pairs[i]) continue;
     const pair = pairs[i].split('=');
-    query[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || '');
+    const key = decodeURIComponent(pair[0]);
+    const value = decodeURIComponent(pair.slice(1).join('=') || '');
+    if (key) query[key] = value;
   }
   return query;
 }

This handles empty pairs, missing values, and multiple = signs more gracefully.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 821b832 and 84c18fa.

📒 Files selected for processing (10)
  • code/addons/docs/src/blocks/components/Preview.tsx
  • code/addons/docs/src/blocks/components/Story.tsx
  • code/addons/docs/src/blocks/components/Toolbar.tsx
  • code/addons/docs/src/blocks/getStoryHref.ts
  • code/core/src/components/components/utils/getStoryHref.ts
  • code/core/src/manager-api/modules/url.ts
  • code/core/src/manager-api/tests/url.test.js
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/preview/Preview.tsx
  • code/core/src/manager/components/preview/utils/types.tsx
💤 Files with no reviewable changes (1)
  • code/addons/docs/src/blocks/components/Preview.tsx
🧰 Additional context used
📓 Path-based instructions (7)
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/spy-mocking.mdc)

**/*.test.{ts,tsx,js,jsx}: Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests
Place all mocks at the top of the test file before any test cases
Use vi.mocked() to type and access the mocked functions in Vitest tests
Implement mock behaviors in beforeEach blocks in Vitest tests
Mock all required dependencies that the test subject uses
Each mock implementation should return a Promise for async functions in Vitest
Mock implementations should match the expected return type of the original function
Mock all required properties and methods that the test subject uses in Vitest tests
Avoid direct function mocking without vi.mocked() in Vitest tests
Avoid mock implementations outside of beforeEach blocks in Vitest tests
Avoid mocking without the spy: true option in Vitest tests
Avoid inline mock implementations within test cases in Vitest tests
Avoid mocking only a subset of required dependencies in Vitest tests
Mock at the highest level of abstraction needed in Vitest tests
Keep mock implementations simple and focused in Vitest tests
Use type-safe mocking with vi.mocked() in Vitest tests
Document complex mock behaviors in Vitest tests
Group related mocks together in Vitest tests

Files:

  • code/core/src/manager-api/tests/url.test.js
**/*.{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/tests/url.test.js
  • code/core/src/manager/components/preview/utils/types.tsx
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager-api/modules/url.ts
  • code/addons/docs/src/blocks/components/Toolbar.tsx
  • code/addons/docs/src/blocks/getStoryHref.ts
  • code/core/src/manager/components/preview/Preview.tsx
  • code/core/src/components/components/utils/getStoryHref.ts
  • code/addons/docs/src/blocks/components/Story.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-api/tests/url.test.js
  • code/core/src/manager/components/preview/utils/types.tsx
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager-api/modules/url.ts
  • code/addons/docs/src/blocks/components/Toolbar.tsx
  • code/addons/docs/src/blocks/getStoryHref.ts
  • code/core/src/manager/components/preview/Preview.tsx
  • code/core/src/components/components/utils/getStoryHref.ts
  • code/addons/docs/src/blocks/components/Story.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-api/tests/url.test.js
  • code/core/src/manager/components/preview/utils/types.tsx
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager-api/modules/url.ts
  • code/addons/docs/src/blocks/components/Toolbar.tsx
  • code/addons/docs/src/blocks/getStoryHref.ts
  • code/core/src/manager/components/preview/Preview.tsx
  • code/core/src/components/components/utils/getStoryHref.ts
  • code/addons/docs/src/blocks/components/Story.tsx
**/*.{test,spec}.{ts,tsx,js,jsx}

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

**/*.{test,spec}.{ts,tsx,js,jsx}: Write meaningful unit tests that actually import and call the functions being tested, not just verify syntax patterns
Achieve high test coverage of business logic, aiming for 75%+ coverage of statements/lines
Cover all branches, conditions, edge cases, error paths, and different input variations in unit tests
Use vi.mock() to mock file system, loggers, and other external dependencies in tests

Files:

  • code/core/src/manager-api/tests/url.test.js
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/tests/url.test.js
  • code/core/src/manager/components/preview/utils/types.tsx
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager-api/modules/url.ts
  • code/addons/docs/src/blocks/components/Toolbar.tsx
  • code/addons/docs/src/blocks/getStoryHref.ts
  • code/core/src/manager/components/preview/Preview.tsx
  • code/core/src/components/components/utils/getStoryHref.ts
  • code/addons/docs/src/blocks/components/Story.tsx
**/*.{ts,tsx}

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

Enable TypeScript strict mode across all packages

Files:

  • code/core/src/manager/components/preview/utils/types.tsx
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager-api/modules/url.ts
  • code/addons/docs/src/blocks/components/Toolbar.tsx
  • code/addons/docs/src/blocks/getStoryHref.ts
  • code/core/src/manager/components/preview/Preview.tsx
  • code/core/src/components/components/utils/getStoryHref.ts
  • code/addons/docs/src/blocks/components/Story.tsx
🧠 Learnings (7)
📚 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/tests/url.test.js
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager-api/modules/url.ts
  • code/addons/docs/src/blocks/components/Story.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-api/tests/url.test.js
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager-api/modules/url.ts
  • code/addons/docs/src/blocks/components/Toolbar.tsx
  • code/core/src/components/components/utils/getStoryHref.ts
  • code/addons/docs/src/blocks/components/Story.tsx
📚 Learning: 2025-12-22T22:03:40.109Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-22T22:03:40.109Z
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-api/tests/url.test.js
  • code/core/src/components/components/utils/getStoryHref.ts
  • code/addons/docs/src/blocks/components/Story.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/preview/FramesRenderer.tsx
  • code/addons/docs/src/blocks/components/Toolbar.tsx
  • code/core/src/components/components/utils/getStoryHref.ts
  • code/addons/docs/src/blocks/components/Story.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/preview/FramesRenderer.tsx
  • code/addons/docs/src/blocks/components/Toolbar.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/addons/docs/src/blocks/components/Toolbar.tsx
📚 Learning: 2025-12-22T22:03:40.109Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-22T22:03:40.109Z
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/components/components/utils/getStoryHref.ts
🧬 Code graph analysis (6)
code/core/src/manager/components/preview/FramesRenderer.tsx (1)
code/core/src/manager-api/version.ts (1)
  • version (1-1)
code/core/src/manager-api/modules/url.ts (1)
code/core/src/manager-api/tests/url.test.js (2)
  • store (155-163)
  • store (254-259)
code/addons/docs/src/blocks/components/Toolbar.tsx (2)
code/addons/docs/src/blocks/getStoryHref.ts (1)
  • getStoryHref (4-13)
code/core/src/components/components/utils/getStoryHref.ts (1)
  • getStoryHref (15-37)
code/addons/docs/src/blocks/getStoryHref.ts (1)
code/core/src/components/components/utils/getStoryHref.ts (1)
  • getStoryHref (15-37)
code/core/src/components/components/utils/getStoryHref.ts (2)
code/addons/docs/src/blocks/getStoryHref.ts (1)
  • getStoryHref (4-13)
code/core/src/node-logger/index.ts (1)
  • deprecate (95-95)
code/addons/docs/src/blocks/components/Story.tsx (2)
code/addons/docs/src/blocks/getStoryHref.ts (1)
  • getStoryHref (4-13)
code/core/src/components/components/utils/getStoryHref.ts (1)
  • getStoryHref (15-37)
⏰ 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). (5)
  • GitHub Check: normal
  • GitHub Check: nx
  • GitHub Check: Core Unit Tests, windows-latest
  • GitHub Check: nx
  • GitHub Check: nx
🔇 Additional comments (10)
code/addons/docs/src/blocks/components/Story.tsx (1)

5-5: LGTM! Clean migration to local getStoryHref utility.

The changes correctly update the import path and simplify the function call signature by removing the baseUrl parameter, aligning with the new URL generation pattern.

Also applies to: 10-10, 99-99

code/core/src/components/components/utils/getStoryHref.ts (1)

1-1: LGTM! Proper deprecation pattern implemented.

The deprecation follows best practices with both JSDoc annotation and runtime warning, providing a clear migration path to api.getStoryHrefs. The existing implementation is preserved for backward compatibility.

Also applies to: 14-22

code/core/src/manager-api/modules/url.ts (1)

343-353: LGTM! Consistent use of global.window.

The changes to use global.window instead of a standalone window reference are consistent with the global imports at the top of the file.

code/core/src/manager-api/tests/url.test.js (1)

9-27: LGTM! Comprehensive test coverage for getStoryHrefs.

The test suite thoroughly covers the new API including:

  • Basic URL generation
  • Args and globals inheritance and merging
  • Base URL options (origin/network)
  • Ref handling
  • PREVIEW_URL overrides

The mock setup for @storybook/global is appropriate for testing absolute URL generation.

Also applies to: 252-408

code/core/src/manager/components/preview/utils/types.tsx (1)

52-52: LGTM! Type updated to include the API.

The addition of api: API to FramesRendererProps correctly reflects the new prop being passed from Preview.tsx to FramesRenderer.

code/addons/docs/src/blocks/components/Toolbar.tsx (1)

4-4: LGTM! Clean migration to local getStoryHref utility.

The changes consistently remove the baseUrl dependency and switch to the local getStoryHref utility, simplifying the component's props and call signatures.

Also applies to: 10-10, 56-56, 117-117

code/core/src/manager/components/preview/Preview.tsx (1)

22-33: LGTM! API correctly passed through to FramesRenderer.

The changes properly thread the api through the component tree via canvasMapper and the Canvas consumer, enabling FramesRenderer to use the new api.getStoryHrefs method for URL generation.

Also applies to: 156-166, 206-216

code/core/src/manager/components/preview/FramesRenderer.tsx (3)

4-4: Import cleanup looks good.

The removal of getStoryHref from imports aligns with the deprecation strategy outlined in the PR.


56-56: Refactoring to use the new API is implemented correctly.

The transition from getStoryHref to api.getStoryHrefs(...).preview is clean and maintains the same functionality. The API correctly receives all necessary parameters (storyId, queryParams with version, refId, viewMode), and the .preview accessor appropriately extracts the preview URL from the returned object.

Also applies to: 77-81


76-91: Appropriate handling of main vs. external ref iframes.

The implementation correctly distinguishes between the main preview iframe (which uses the new API) and external ref iframes (which require manual URL construction with their own base URLs). This architectural decision is sound given that external refs point to separate Storybook instances.

@storybook-app-bot
Copy link

storybook-app-bot bot commented Dec 23, 2025

Package Benchmarks

Commit: e518754, ran on 5 January 2026 at 14:27:49 UTC

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

storybook

Before After Difference
Dependency count 49 49 0
Self size 20.19 MB 20.18 MB 🎉 -14 KB 🎉
Dependency size 16.52 MB 16.52 MB 0 B
Bundle Size Analyzer Link Link

@storybook/cli

Before After Difference
Dependency count 183 183 0
Self size 775 KB 775 KB 🎉 -84 B 🎉
Dependency size 67.24 MB 67.23 MB 🎉 -14 KB 🎉
Bundle Size Analyzer Link Link

@storybook/codemod

Before After Difference
Dependency count 176 176 0
Self size 30 KB 30 KB 🎉 -36 B 🎉
Dependency size 65.82 MB 65.80 MB 🎉 -14 KB 🎉
Bundle Size Analyzer Link Link

create-storybook

Before After Difference
Dependency count 50 50 0
Self size 999 KB 999 KB 🎉 -42 B 🎉
Dependency size 36.71 MB 36.69 MB 🎉 -14 KB 🎉
Bundle Size Analyzer node node
coderabbitai[bot]

This comment was marked as spam.

coderabbitai[bot]

This comment was marked as spam.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (3)
code/core/src/manager/components/preview/tools/share.stories.tsx (1)

18-21: Fix shortcut key case sensitivity in mock: use uppercase letters.

The mock shortcut keys use lowercase letters ('l' and 'i'), but the actual implementation in shortcuts.ts uses uppercase ('L' and 'I'). Update lines 18-21 to:

getShortcutKeys: () => ({
  copyStoryLink: ['alt', 'shift', 'L'],
  openInIsolation: ['alt', 'shift', 'I'],
}),
code/core/src/manager-api/modules/url.ts (2)

35-43: Handle colons in parameter values.

The split(':') on line 39 splits on every colon. If a value contains colons (e.g., "theme:dark:blue"), only the first two segments are captured by the destructuring [key, value], discarding the rest (e.g., ":blue"). This causes data loss when args or globals contain colon-separated values.

🔎 Proposed fix using split with limit
 const parseSerializedParam = (param: string) =>
   Object.fromEntries(
     param
       .split(';')
-      .map((pair) => pair.split(':'))
+      .map((pair) => pair.split(':', 2))
       // Encoding values ensures we don't break already encoded args/globals but also don't encode our own special characters like ; and :.
       .map(([key, value]) => [key, encodeURIComponent(value)])
       .filter(([key, value]) => key && value)
   );

This limits the split to at most 2 parts, preserving colons in the value.


269-275: URL-encode path segments and query parameters.

The serialized argsParam and globalsParam strings (containing ; and : delimiters) are concatenated directly into query strings without URL encoding the entire serialized string. While individual values within these strings are already encoded by parseSerializedParam, the structural characters could still conflict with the & delimiter in the query string. Additionally, refId and storyId in the managerHref path (line 274) and storyId in the previewHref query (line 275) are not URL-encoded.

🔎 Proposed fix to add URL encoding
-  argsParam = argsParam && `&args=${argsParam}`;
-  globalsParam = globalsParam && `&globals=${globalsParam}`;
+  argsParam = argsParam && `&args=${encodeURIComponent(argsParam)}`;
+  globalsParam = globalsParam && `&globals=${encodeURIComponent(globalsParam)}`;
   customParams = customParams && `&${customParams}`;

   return {
-    managerHref: `${managerBase}?path=/${viewMode}/${refId ? `${refId}_` : ''}${storyId}${argsParam}${globalsParam}${customParams}`,
-    previewHref: `${previewBase}?id=${storyId}&viewMode=${viewMode}${refParam}${argsParam}${refId ? '' : globalsParam}${customParams}`,
+    managerHref: `${managerBase}?path=/${viewMode}/${refId ? `${encodeURIComponent(refId)}_` : ''}${encodeURIComponent(storyId)}${argsParam}${globalsParam}${customParams}`,
+    previewHref: `${previewBase}?id=${encodeURIComponent(storyId)}&viewMode=${viewMode}${refParam}${argsParam}${refId ? '' : globalsParam}${customParams}`,
   };

Note: Verify that stringify() from picoquery already handles encoding for customParams.

🧹 Nitpick comments (1)
code/core/src/manager-api/tests/url.test.js (1)

16-27: Consider using the spy: true option for consistency.

The coding guidelines recommend using vi.mock() with the spy: true option for all package mocks. While this mock only provides static values rather than functions to spy on, adding spy: true would maintain consistency with the testing guidelines.

🔎 Proposed change
-vi.mock('@storybook/global', () => ({
+vi.mock('@storybook/global', { spy: true }, () => ({
   global: {
     window: {
       location: {
         hash: '',
         href: 'http://localhost:6006',
         origin: 'http://localhost:6006',
       },
     },
     STORYBOOK_NETWORK_ADDRESS: 'http://192.168.1.1:6006/',
   },
 }));

Note: If spy: true causes issues with this static value mock, the current approach is acceptable.

Based on coding guidelines.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1e780c5 and 68aae62.

📒 Files selected for processing (4)
  • code/addons/docs/src/blocks/getStoryHref.ts
  • code/core/src/manager-api/modules/url.ts
  • code/core/src/manager-api/tests/url.test.js
  • code/core/src/manager/components/preview/tools/share.stories.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • code/addons/docs/src/blocks/getStoryHref.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{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/preview/tools/share.stories.tsx
  • code/core/src/manager-api/tests/url.test.js
  • code/core/src/manager-api/modules/url.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/components/preview/tools/share.stories.tsx
  • code/core/src/manager-api/tests/url.test.js
  • code/core/src/manager-api/modules/url.ts
**/*.{ts,tsx}

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

Enable TypeScript strict mode across all packages

Files:

  • code/core/src/manager/components/preview/tools/share.stories.tsx
  • code/core/src/manager-api/modules/url.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/components/preview/tools/share.stories.tsx
  • code/core/src/manager-api/tests/url.test.js
  • code/core/src/manager-api/modules/url.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/components/preview/tools/share.stories.tsx
  • code/core/src/manager-api/tests/url.test.js
  • code/core/src/manager-api/modules/url.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/spy-mocking.mdc)

**/*.test.{ts,tsx,js,jsx}: Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests
Place all mocks at the top of the test file before any test cases
Use vi.mocked() to type and access the mocked functions in Vitest tests
Implement mock behaviors in beforeEach blocks in Vitest tests
Mock all required dependencies that the test subject uses
Each mock implementation should return a Promise for async functions in Vitest
Mock implementations should match the expected return type of the original function
Mock all required properties and methods that the test subject uses in Vitest tests
Avoid direct function mocking without vi.mocked() in Vitest tests
Avoid mock implementations outside of beforeEach blocks in Vitest tests
Avoid mocking without the spy: true option in Vitest tests
Avoid inline mock implementations within test cases in Vitest tests
Avoid mocking only a subset of required dependencies in Vitest tests
Mock at the highest level of abstraction needed in Vitest tests
Keep mock implementations simple and focused in Vitest tests
Use type-safe mocking with vi.mocked() in Vitest tests
Document complex mock behaviors in Vitest tests
Group related mocks together in Vitest tests

Files:

  • code/core/src/manager-api/tests/url.test.js
**/*.{test,spec}.{ts,tsx,js,jsx}

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

**/*.{test,spec}.{ts,tsx,js,jsx}: Write meaningful unit tests that actually import and call the functions being tested, not just verify syntax patterns
Achieve high test coverage of business logic, aiming for 75%+ coverage of statements/lines
Cover all branches, conditions, edge cases, error paths, and different input variations in unit tests
Use vi.mock() to mock file system, loggers, and other external dependencies in tests

Files:

  • code/core/src/manager-api/tests/url.test.js
🧠 Learnings (7)
📚 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/preview/tools/share.stories.tsx
  • code/core/src/manager-api/tests/url.test.js
📚 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/preview/tools/share.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/preview/tools/share.stories.tsx
  • code/core/src/manager-api/tests/url.test.js
  • code/core/src/manager-api/modules/url.ts
📚 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/preview/tools/share.stories.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: 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/preview/tools/share.stories.tsx
  • code/core/src/manager-api/tests/url.test.js
📚 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-api/tests/url.test.js
📚 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-api/tests/url.test.js
🧬 Code graph analysis (1)
code/core/src/manager-api/modules/url.ts (1)
code/core/src/manager-api/tests/url.test.js (2)
  • store (155-163)
  • store (254-259)
⏰ 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/preview/tools/share.stories.tsx (1)

22-25: Mock correctly implements the API property names.

The getStoryHrefs mock now correctly returns { managerHref, previewHref }, addressing the issue flagged in a previous review. The property names match the actual API implementation.

code/core/src/manager-api/modules/url.ts (3)

45-51: LGTM!

The merge logic correctly parses both parameter strings, merges them with proper precedence (extra params override existing), and re-serializes back to the expected format.


141-167: LGTM!

The JSDoc documentation is comprehensive and clearly describes the method signature, all options, and return value.


348-352: LGTM!

The change from globalWindow to global.window is consistent with the pattern used elsewhere in this file and correctly accesses the idle callback APIs.

code/core/src/manager-api/tests/url.test.js (1)

252-457: Excellent test coverage!

The test suite comprehensively covers the getStoryHrefs functionality with well-structured tests for:

  • Basic URL generation
  • Args/globals inheritance and merging behavior
  • Special value preservation (e.g., !null, !hex())
  • Query parameter encoding and preservation
  • Absolute URL generation with base options
  • Ref-based URL generation
  • Custom nested query parameters

The tests are well-organized, have clear naming, and cover both happy paths and edge cases. The test at lines 388-403 specifically addresses the encoding of custom query params, which was mentioned in past review comments.

@ghengeveld ghengeveld mentioned this pull request Dec 24, 2025
8 tasks
@ghengeveld ghengeveld requested a review from yannbf December 24, 2025 15:29
@Sidnioulz Sidnioulz force-pushed the isolation-mode-shortcut branch from 68aae62 to e5d6628 Compare December 29, 2025 08:59
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

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

35-43: Colon handling in serialized values may truncate data.

split(':') at line 39 splits on every colon. If a value contains colons (e.g., a URL like http://example.com), only the first segment after the key is captured, discarding the rest.

The previous review flagged this issue. Consider using split(':', 2) to limit the split:

🔎 Proposed fix
 const parseSerializedParam = (param: string) =>
   Object.fromEntries(
     param
       .split(';')
-      .map((pair) => pair.split(':'))
+      .map((pair) => {
+        const colonIndex = pair.indexOf(':');
+        return colonIndex === -1 ? [pair, ''] : [pair.slice(0, colonIndex), pair.slice(colonIndex + 1)];
+      })
       .map(([key, value]) => [key, encodeURIComponent(value)])
       .filter(([key, value]) => key && value)
   );

269-276: URL encoding for args/globals parameters.

The argsParam and globalsParam values contain serialized data with special characters (;, :) that are concatenated directly into the URL. While the values within are encoded via encodeURIComponent in parseSerializedParam, the overall structure with ; and : delimiters remains unencoded.

This was flagged in a previous review. Verify whether the current encoding strategy is intentional to preserve the custom serialization format, or if full URL encoding is needed.

🧹 Nitpick comments (1)
code/core/src/manager/components/preview/utils/types.tsx (1)

51-60: Remove unused baseUrl from FramesRendererProps.

The baseUrl prop (line 56) is defined in the interface but unused in the FramesRenderer component, which now uses API-based URL generation via api.getStoryHrefs(). Removing it will eliminate dead code and avoid confusion about its purpose.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 68aae62 and e5d6628.

📒 Files selected for processing (17)
  • code/addons/docs/src/blocks/components/Preview.tsx
  • code/addons/docs/src/blocks/components/Story.tsx
  • code/addons/docs/src/blocks/components/Toolbar.tsx
  • code/addons/docs/src/blocks/getStoryHref.ts
  • code/core/src/components/components/utils/getStoryHref.ts
  • code/core/src/manager-api/modules/shortcuts.ts
  • code/core/src/manager-api/modules/url.ts
  • code/core/src/manager-api/tests/url.test.js
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/preview/Preview.tsx
  • code/core/src/manager/components/preview/tools/share.stories.tsx
  • code/core/src/manager/components/preview/tools/share.tsx
  • code/core/src/manager/components/preview/utils/stringifyQueryParams.tsx
  • code/core/src/manager/components/preview/utils/types.tsx
  • code/core/src/manager/settings/defaultShortcuts.tsx
  • code/core/src/manager/settings/shortcuts.tsx
  • code/core/src/typings.d.ts
💤 Files with no reviewable changes (2)
  • code/addons/docs/src/blocks/components/Preview.tsx
  • code/core/src/manager/components/preview/utils/stringifyQueryParams.tsx
🚧 Files skipped from review as they are similar to previous changes (9)
  • code/addons/docs/src/blocks/getStoryHref.ts
  • code/core/src/manager-api/tests/url.test.js
  • code/core/src/manager-api/modules/shortcuts.ts
  • code/core/src/components/components/utils/getStoryHref.ts
  • code/core/src/manager/components/preview/Preview.tsx
  • code/core/src/manager/settings/shortcuts.tsx
  • code/core/src/typings.d.ts
  • code/addons/docs/src/blocks/components/Story.tsx
  • code/core/src/manager/settings/defaultShortcuts.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/preview/tools/share.stories.tsx
  • code/core/src/manager/components/preview/utils/types.tsx
  • code/core/src/manager-api/modules/url.ts
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/preview/tools/share.tsx
  • code/addons/docs/src/blocks/components/Toolbar.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/preview/tools/share.stories.tsx
  • code/core/src/manager/components/preview/utils/types.tsx
  • code/core/src/manager-api/modules/url.ts
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/preview/tools/share.tsx
  • code/addons/docs/src/blocks/components/Toolbar.tsx
**/*.{ts,tsx}

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

Enable TypeScript strict mode across all packages

Files:

  • code/core/src/manager/components/preview/tools/share.stories.tsx
  • code/core/src/manager/components/preview/utils/types.tsx
  • code/core/src/manager-api/modules/url.ts
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/preview/tools/share.tsx
  • code/addons/docs/src/blocks/components/Toolbar.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/preview/tools/share.stories.tsx
  • code/core/src/manager/components/preview/utils/types.tsx
  • code/core/src/manager-api/modules/url.ts
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/preview/tools/share.tsx
  • code/addons/docs/src/blocks/components/Toolbar.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/preview/tools/share.stories.tsx
  • code/core/src/manager/components/preview/utils/types.tsx
  • code/core/src/manager-api/modules/url.ts
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/preview/tools/share.tsx
  • code/addons/docs/src/blocks/components/Toolbar.tsx
🧠 Learnings (9)
📚 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/preview/tools/share.stories.tsx
  • code/core/src/manager-api/modules/url.ts
  • code/core/src/manager/components/preview/tools/share.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/preview/tools/share.stories.tsx
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/preview/tools/share.tsx
  • code/addons/docs/src/blocks/components/Toolbar.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/preview/tools/share.stories.tsx
  • code/core/src/manager-api/modules/url.ts
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/preview/tools/share.tsx
  • code/addons/docs/src/blocks/components/Toolbar.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/preview/tools/share.stories.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: 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/preview/tools/share.stories.tsx
  • code/core/src/manager/components/preview/tools/share.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/preview/FramesRenderer.tsx
  • code/core/src/manager/components/preview/tools/share.tsx
  • code/addons/docs/src/blocks/components/Toolbar.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/preview/FramesRenderer.tsx
  • code/core/src/manager/components/preview/tools/share.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/{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/preview/tools/share.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/addons/docs/src/blocks/components/Toolbar.tsx
🧬 Code graph analysis (4)
code/core/src/manager-api/modules/url.ts (1)
code/core/src/manager-api/tests/url.test.js (2)
  • store (155-163)
  • store (254-259)
code/core/src/manager/components/preview/FramesRenderer.tsx (1)
code/core/src/manager-api/version.ts (1)
  • version (1-1)
code/core/src/manager/components/preview/tools/share.tsx (3)
code/core/src/components/index.ts (3)
  • TooltipLinkList (86-86)
  • PopoverProvider (71-71)
  • Button (56-56)
code/core/src/types/modules/addons.ts (1)
  • Addon_BaseType (329-391)
code/core/src/components/components/Popover/PopoverProvider.tsx (1)
  • PopoverProvider (55-102)
code/addons/docs/src/blocks/components/Toolbar.tsx (2)
code/addons/docs/src/blocks/getStoryHref.ts (1)
  • getStoryHref (3-15)
code/core/src/components/components/utils/getStoryHref.ts (1)
  • getStoryHref (15-37)
⏰ 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 (14)
code/addons/docs/src/blocks/components/Toolbar.tsx (3)

4-10: LGTM! Clean migration to local utility.

The import change correctly switches from the deprecated getStoryHref in internal components to the new local utility that handles URL construction without requiring a baseUrl prop.


56-56: Props cleanup looks correct.

The baseUrl prop removal from the component signature aligns with the interface change in EjectProps (lines 17-19) and the switch to the local getStoryHref utility.


117-117: Verify the local getStoryHref handles edge cases.

The simplified call getStoryHref(storyId) relies on the local utility to derive the base URL from globalThis.PREVIEW_URL || 'iframe.html'. Ensure this works correctly in all docs contexts (standalone docs, embedded docs, etc.).

code/core/src/manager/components/preview/tools/share.stories.tsx (2)

18-25: Mock API correctly updated.

The mock now returns { managerHref, previewHref } matching the actual getStoryHrefs API signature. The getShortcutKeys mock includes the new openInIsolation shortcut.


47-62: Good test coverage for development mode.

The story correctly sets up globals (STORYBOOK_NETWORK_ADDRESS, CONFIG_TYPE) and cleans them up via the beforeEach return function. The play function validates the QR code section renders correctly.

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

230-246: Solid implementation of getStoryHrefs.

The method correctly:

  • Determines if viewing the current story to default inheritArgs
  • Validates refId against available refs
  • Handles origin vs network base URL resolution

The logic flow is clear and well-documented.


348-352: Consistent use of global.window.

The change from globalWindow to global.window aligns with the pattern used elsewhere in this file and improves consistency.

code/core/src/manager/components/preview/FramesRenderer.tsx (3)

54-62: Clean API-based refactor.

The component now accepts api instead of baseUrl, centralizing URL generation logic. The destructuring is clean and the signature change aligns with FramesRendererProps.


71-77: Main iframe URL generation looks correct.

The URL is generated once (guarded by the if (!frames['storybook-preview-iframe']) check) using api.getStoryHrefs with proper parameters including version and viewMode.


79-88: Ref iframe URL invalidation logic.

The check !frames[id]?.startsWith(ref.url) ensures frames are regenerated when the ref URL changes. This is a reasonable cache invalidation strategy.

However, note that this only checks if the cached URL starts with the ref URL—it won't invalidate if queryParams, storyId, or viewMode change. Since the component is typically re-mounted or the entire frames ref is reset on story changes, this should be acceptable.

code/core/src/manager/components/preview/tools/share.tsx (4)

17-20: Clean mapper pattern.

The mapper extracts exactly what ShareMenu needs from the Combo, keeping the component decoupled from the full state shape.


59-67: ShareMenu signature is well-typed.

The component correctly accepts api, storyId, and refId props with appropriate types. Using React.memo is a good optimization for the tooltip content.


74-121: Well-structured links with proper URL sources.

The implementation correctly:

  • Uses originHrefs for copy/open actions (same-origin use case)
  • Uses networkHrefs for QR code (cross-device scanning)
  • Includes the new openInIsolation shortcut
  • Has complete useMemo dependencies

The QR description adapts based on CONFIG_TYPE, providing appropriate user guidance.


131-148: Render function uses Consumer pattern correctly.

The Consumer with mapper pattern cleanly provides the required props to ShareMenu. The conditional rendering (storyId ?) ensures the share button only appears when a story is selected.

@ndelangen
Copy link
Member

Everything looks great to me!

The one thing I noticed that the keyboard shortcut also works for a docs page, should it? Considering the share menu is absent in the UI when viewing docs pages.

@Sidnioulz
Copy link
Member

Everything looks great to me!

The one thing I noticed that the keyboard shortcut also works for a docs page, should it? Considering the share menu is absent in the UI when viewing docs pages.

I don't think it hurts to have it. People might want to embed such a docs page in another website, or debug rare interactions between manager-head scripts and their docs.

@ndelangen
Copy link
Member

Yeah, it doesn't hurt to have it as an undocumented feature.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Fix all issues with AI Agents 🤖
In @code/core/src/manager-api/modules/shortcuts.ts:
- Around line 419-422: The copyStoryLink handler calls
fullAPI.getStoryHrefs(storyId, { refId }) without checking storyId, causing
"undefined" URLs; update the copyStoryLink case to first verify that storyId
exists (and optionally that viewMode === 'story' to match openInIsolation) and
only call getStoryHrefs and copy when the guard passes; if storyId is absent, do
nothing (or handle gracefully) to avoid copying malformed URLs.

In @code/core/src/manager/components/preview/Iframe.stories.tsx:
- Around line 59-60: The parameters assignment uses the wrong story identifier:
change the assignment from WorkingStory.parameters to WorkingDocs.parameters so
the parameters apply to the WorkingDocs export; locate the block where
WorkingStory.parameters = { chromatic: { disableSnapshot: true } } and update
the left-hand identifier to WorkingDocs.parameters to correct the copy-paste
error.

In @code/frameworks/nextjs-vite/src/export-mocks/headers/index.ts:
- Line 1: The static import of draftMode from
'next/dist/server/request/draft-mode.js' will break on Next.js 14; change to a
dynamic import with try/catch like the webpack version: remove the top-level
static import, import headers via 'next/dist/server/request/headers.js' for
fallback, attempt to dynamically import 'next/dist/server/request/draft-mode.js'
and fall back to headers.draftMode if that fails, then wrap the resolved
originalDraftMode with the existing fn(...) mockName('draftMode') export so
draftMode continues to be exported as before.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
code/frameworks/react-vite/package.json (1)

76-76: Invalid git commit hash: contains non-hexadecimal character.

The gitHead value ends with a lowercase l character, which is not valid in a hexadecimal git commit hash. Git hashes should only contain characters 0-9 and a-f.

This appears to be a typo—the last character should likely be 1 instead of l.

🔎 Proposed fix
-  "gitHead": "a8e7fd8a655c69780bc20b9749d2699e45beae1l"
+  "gitHead": "a8e7fd8a655c69780bc20b9749d2699e45beae11"
code/frameworks/react-webpack5/package.json (1)

70-70: Fix invalid git commit hash in gitHead field.

The gitHead value ends with 'l' (lowercase L) and is 41 characters long, but git commit hashes must be exactly 40 hexadecimal characters (0-9, a-f). This appears to be a typo or data corruption issue that could cause problems during package publishing or version resolution.

🔎 Verify and correct the git commit hash

The hash should be truncated to remove the trailing 'l':

-  "gitHead": "a8e7fd8a655c69780bc20b9749d2699e45beae1l"
+  "gitHead": "a8e7fd8a655c69780bc20b9749d2699e45beae1"

However, if this field is auto-generated during the publishing process, you may want to verify whether it should be manually specified at all or if it should be removed and regenerated during publishing.

code/frameworks/svelte-vite/package.json (1)

75-75: Invalid git hash in gitHead field.

The gitHead value ends with "1l", making it 41 characters instead of the expected 40-character SHA-1 hash. The "l" is not a valid hexadecimal character and appears to be a typo.

🔎 Proposed fix
-  "gitHead": "a8e7fd8a655c69780bc20b9749d2699e45beae1l"
+  "gitHead": "a8e7fd8a655c69780bc20b9749d2699e45beae17"

Note: Verify the correct commit hash from your git history.

scripts/build-package.ts (1)

32-59: Fix type inconsistency in the reduce return type.

The return type annotation at line 58 specifies Record<string, { name: string; defaultValue: boolean; suffix: string }>, but this is narrower than the full task type defined at lines 34-40, which includes optional value and location properties. Since value is assigned at line 79 and location is accessed at line 156, the return type should match the full definition.

🔎 Proposed fix for the type definition
       },
-      {} as Record<string, { name: string; defaultValue: boolean; suffix: string }>
+      {} as Record<string, { name: string; defaultValue: boolean; suffix: string; value?: unknown; location?: string }>
     );
♻️ Duplicate comments (4)
code/addons/vitest/template/stories/unhandled-errors.stories.ts (1)

16-16: Chromatic parameter update matches pattern.

Same change as in code/core/template/stories/component-test.unhandled-errors.stories.ts. The migration from disable to disableSnapshot is consistent.

code/core/template/stories/parameters-actions.stories.ts (1)

11-11: Chromatic parameter migration is consistent.

Same parameter update as other story files in this PR. The change from disable to disableSnapshot maintains consistency across the codebase.

code/addons/themes/template/stories/decorators.stories.ts (1)

52-52: Chromatic parameter update aligns with migration.

Consistent with the pattern across the codebase. Note that line 53's themes: { disable: false } is a separate configuration unrelated to the Chromatic change.

code/core/template/stories/destructuring-not-transpiled.stories.ts (1)

13-13: Chromatic configuration property renamed.

This change is consistent with the Chromatic API property rename from disable to disableSnapshot applied across multiple files in this PR. See the verification request in code/addons/docs/template/stories/docs2/multiple-csf-files-b.stories.ts for the API confirmation.

🧹 Nitpick comments (3)
CHANGELOG.prerelease.md (1)

1-15: Changelog section is well‑formed; consider adding an entry for this PR

The new 10.2.0-alpha.10 section matches existing formatting and structure. Since this PR adds a user‑visible manager API (getStoryHrefs) and a new isolation shortcut, consider adding a bullet for it in this section for release traceability (e.g., under “Core” or “UI”), but this is optional if your release tooling documents it elsewhere.

CHANGELOG.md (1)

1-5: Update 10.1.11 entry to mention getStoryHrefs feature and normalize bullet formatting

Right now 10.1.11 only mentions unrelated fixes, but this PR’s main user‑visible change is the new getStoryHrefs manager API and isolation shortcut. I’d add a bullet for that, and optionally fix the extra space before the second - for consistency with nearby entries.

Proposed changelog tweak
 ## 10.1.11

-- React: Fix several CSF factory bugs  - [#33354](https://github.com/storybookjs/storybook/pull/33354), thanks @kasperpeulen!
+- Core: Add `getStoryHrefs` manager API and isolation shortcut - [#33416](https://github.com/storybookjs/storybook/pull/33416), thanks @ghengeveld!
+- React: Fix several CSF factory bugs - [#33354](https://github.com/storybookjs/storybook/pull/33354), thanks @kasperpeulen!
 - UI: Fix React error 300 on some addons - [#33381](https://github.com/storybookjs/storybook/pull/33381), thanks @Sidnioulz!

Adjust wording/PR number as needed to match how you want to present the feature.

code/frameworks/nextjs/src/export-mocks/headers/index.ts (1)

16-26: Consider more specific error handling for module resolution.

The try/catch block uses require() to conditionally load the draftMode module, but the empty catch block silently swallows all errors, not just MODULE_NOT_FOUND. While the webpack alias is mentioned in the comments, consider these improvements:

  1. Check for specific error codes: if (err.code !== 'MODULE_NOT_FOUND') throw err
  2. Add a fallback comment explaining when the catch branch executes
  3. Consider whether the any type cast on line 22 could be replaced with a more specific type
🔎 Suggested improvements
 let originalDraftMode: any;
 try {
   // This will be resolved by webpack alias to the correct location based on Next.js version
   originalDraftMode = require('next/dist/server/request/draft-mode').draftMode;
-} catch {
-  // Fallback to the location in the headers module (Next.js 14)
+} catch (err: any) {
+  // Fallback to the location in the headers module (Next.js 14)
+  // Only catch MODULE_NOT_FOUND; re-throw other errors
+  if (err.code !== 'MODULE_NOT_FOUND') throw err;
   originalDraftMode = (headers as any).draftMode;
 }
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
code/core/src/manager/components/preview/Iframe.stories.tsx (1)

85-113: Consider completing the CSF format migration for consistency.

PreparingStory and PreparingDocs remain as function components while WorkingStory and WorkingDocs were migrated to object-shaped exports. For consistency and to fully align with current CSF 3.0 conventions, consider converting these to the same object format.

🔎 Proposed refactor for PreparingStory
-export const PreparingStory = () => (
-  <IFrame
-    active
-    id="iframe"
-    title="Preparing Story"
-    src="/iframe.html?__SPECIAL_TEST_PARAMETER__=preparing-story"
-    allowFullScreen
-    style={style}
-    scale={1.0}
-  />
-);
-PreparingStory.parameters = {
-  chromatic: { disableSnapshot: true },
+export const PreparingStory = {
+  render: () => (
+    <IFrame
+      active
+      id="iframe"
+      title="Preparing Story"
+      src="/iframe.html?__SPECIAL_TEST_PARAMETER__=preparing-story"
+      allowFullScreen
+      style={style}
+      scale={1.0}
+    />
+  ),
+  parameters: {
+    chromatic: { disableSnapshot: true },
+  },
 };
🔎 Proposed refactor for PreparingDocs
-export const PreparingDocs = () => (
-  <IFrame
-    active
-    id="iframe"
-    title="Preparing Docs"
-    src="/iframe.html?__SPECIAL_TEST_PARAMETER__=preparing-docs"
-    allowFullScreen
-    style={style}
-    scale={1.0}
-  />
-);
-PreparingDocs.parameters = {
-  chromatic: { disableSnapshot: true },
+export const PreparingDocs = {
+  render: () => (
+    <IFrame
+      active
+      id="iframe"
+      title="Preparing Docs"
+      src="/iframe.html?__SPECIAL_TEST_PARAMETER__=preparing-docs"
+      allowFullScreen
+      style={style}
+      scale={1.0}
+    />
+  ),
+  parameters: {
+    chromatic: { disableSnapshot: true },
+  },
 };
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2e9e276 and 6f12897.

📒 Files selected for processing (4)
  • code/addons/docs/src/blocks/getStoryHref.ts
  • code/core/src/components/components/utils/getStoryHref.ts
  • code/core/src/manager-api/modules/shortcuts.ts
  • code/core/src/manager/components/preview/Iframe.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/components/components/utils/getStoryHref.ts
  • code/core/src/manager-api/modules/shortcuts.ts
  • code/core/src/manager/components/preview/Iframe.stories.tsx
  • code/addons/docs/src/blocks/getStoryHref.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/components/components/utils/getStoryHref.ts
  • code/core/src/manager-api/modules/shortcuts.ts
  • code/core/src/manager/components/preview/Iframe.stories.tsx
  • code/addons/docs/src/blocks/getStoryHref.ts
**/*.{ts,tsx}

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

Enable TypeScript strict mode across all packages

Files:

  • code/core/src/components/components/utils/getStoryHref.ts
  • code/core/src/manager-api/modules/shortcuts.ts
  • code/core/src/manager/components/preview/Iframe.stories.tsx
  • code/addons/docs/src/blocks/getStoryHref.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/components/components/utils/getStoryHref.ts
  • code/core/src/manager-api/modules/shortcuts.ts
  • code/core/src/manager/components/preview/Iframe.stories.tsx
  • code/addons/docs/src/blocks/getStoryHref.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/components/components/utils/getStoryHref.ts
  • code/core/src/manager-api/modules/shortcuts.ts
  • code/core/src/manager/components/preview/Iframe.stories.tsx
  • code/addons/docs/src/blocks/getStoryHref.ts
🧠 Learnings (9)
📓 Common learnings
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.
📚 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/components/components/utils/getStoryHref.ts
  • code/core/src/manager-api/modules/shortcuts.ts
  • code/core/src/manager/components/preview/Iframe.stories.tsx
  • code/addons/docs/src/blocks/getStoryHref.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/components/components/utils/getStoryHref.ts
  • code/core/src/manager/components/preview/Iframe.stories.tsx
  • code/addons/docs/src/blocks/getStoryHref.ts
📚 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/components/components/utils/getStoryHref.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/shortcuts.ts
  • code/core/src/manager/components/preview/Iframe.stories.tsx
  • code/addons/docs/src/blocks/getStoryHref.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-api/modules/shortcuts.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: 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-api/modules/shortcuts.ts
📚 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/addons/docs/src/blocks/getStoryHref.ts
📚 Learning: 2025-09-29T13:20:23.346Z
Learnt from: mrginglymus
Repo: storybookjs/storybook PR: 32556
File: code/core/package.json:309-313
Timestamp: 2025-09-29T13:20:23.346Z
Learning: The `fast-printf` dependency in Storybook's core package is bundled into the final distribution during the build process, so it should remain in devDependencies rather than being moved to dependencies, following the same pattern as other bundled dependencies like `open`.

Applied to files:

  • code/addons/docs/src/blocks/getStoryHref.ts
🧬 Code graph analysis (2)
code/core/src/components/components/utils/getStoryHref.ts (2)
code/addons/docs/src/blocks/getStoryHref.ts (1)
  • getStoryHref (5-17)
code/core/src/node-logger/index.ts (1)
  • deprecate (95-95)
code/addons/docs/src/blocks/getStoryHref.ts (1)
code/core/src/components/components/utils/getStoryHref.ts (1)
  • getStoryHref (15-37)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: normal
🔇 Additional comments (6)
code/core/src/manager/components/preview/Iframe.stories.tsx (1)

33-65: Migration to object-shaped CSF exports looks good.

The conversion of WorkingStory and WorkingDocs to object-shaped exports with render functions follows current Storybook best practices. The chromatic configuration update to disableSnapshot is appropriate.

code/core/src/components/components/utils/getStoryHref.ts (2)

1-1: Deprecation properly implemented with appropriate logger.

The deprecation is correctly implemented with both JSDoc annotation and runtime warning. Using deprecate from storybook/internal/client-logger is appropriate for this client-side component code, per the coding guidelines.

Also applies to: 20-22


14-14: JSDoc deprecation annotation is clear and actionable.

The deprecation notice properly directs users to the replacement API method.

code/addons/docs/src/blocks/getStoryHref.ts (2)

1-4: JSDoc clearly documents the rationale for duplication.

The comment properly explains why this utility exists separately from the deprecated core version and clarifies its preview-only scope.


5-17: Clean implementation using URLSearchParams.

The implementation correctly uses URLSearchParams throughout, which properly handles URL encoding/decoding—addressing the past concern about manual encoding. The logic correctly:

  • Derives the base URL from globalThis.PREVIEW_URL with appropriate fallback
  • Merges additional parameters before setting the story ID
  • Ensures the id parameter is always set (line 14)
code/core/src/manager-api/modules/shortcuts.ts (1)

116-116: LGTM! Well-implemented shortcut addition with appropriate guards.

The implementation correctly:

  • Adds the openInIsolation shortcut with proper type definitions and default key binding
  • Guards against undefined storyId in both handlers
  • Restricts openInIsolation to story pages only (viewMode === 'story' check) per PR objectives
  • Allows copyStoryLink to work on any page with a story ID (stories and docs)
  • Uses secure window.open flags (noopener,noreferrer)
  • Refactors copyStoryLink to use the new getStoryHrefs API instead of window.location.href

Previous review concerns about undefined storyId have been properly addressed with guards.

Also applies to: 156-156, 252-253, 404-410, 420-423

@ghengeveld ghengeveld merged commit 426fa73 into next Jan 5, 2026
69 checks passed
@ghengeveld ghengeveld deleted the isolation-mode-shortcut branch January 5, 2026 14:34
@ghengeveld ghengeveld added the needs qa Indicates that this needs manual QA during the upcoming minor/major release label Jan 14, 2026
@ndelangen ndelangen removed the needs qa Indicates that this needs manual QA during the upcoming minor/major release label Jan 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment