Skip to content

Conversation

@DKER2
Copy link

@DKER2 DKER2 commented Oct 11, 2025

Closes #32464

addonsShortcutLabels and addonsShortcut is initilized as empty dict on start of programs, but because of localstorage from previous session, it retrieve the list of shortcuts with previously initialized addon shortcut. This PR will hide any addon-shortcut if it have not been initiliazed in new start

Thank you for contributing to Storybook! Please submit all PRs to the next branch unless they are specific to the current release. Storybook maintainers cherry-pick bug and documentation fixes into the main branch as part of the release process, so you shouldn't need to worry about this. For additional guidance: https://storybook.js.org/docs/contribute

-->

What I did

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

This section is mandatory for all contributions. If you believe no manual test is necessary, please state so explicitly. Thanks!

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

  • Bug Fixes

    • Shortcut settings now hide entries without visible labels, cleaning up the shortcuts list.
  • Refactor

    • Shortcut rendering simplified to only show available labeled shortcuts.
  • Removed

    • Built-in Jest addon and its Test panel have been removed.
  • Documentation

    • Migration guidance updated for ESM and explicit file extensions; changelogs and snippets updated (Next.js Vite plugin, a11y export rename to initialGlobals).
  • Behavior

    • Story navigation now prefers visible children when an index is filtered, improving navigation with active filters.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 11, 2025

📝 Walkthrough

Walkthrough

renderKeyInput in settings/shortcuts now filters shortcutKeys to only entries that have labels in shortcutLabels or addonsShortcutLabels, and renders rows from that filtered list; unlabeled shortcuts are skipped. Also a minor whitespace change in an onBlur handler.

Changes

Cohort / File(s) Summary
Settings — Shortcuts rendering
code/core/src/manager/settings/shortcuts.tsx
In renderKeyInput, compute availableShortcuts by filtering shortcutKeys to keys present in shortcutLabels or addonsShortcutLabels; iterate over availableShortcuts to render Rows. Small whitespace adjustment in an onBlur handler.

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant Settings as Settings/Shortcuts UI
  participant Renderer as renderKeyInput
  participant Labels as shortcutLabels / addonsShortcutLabels

  User->>Settings: open shortcuts UI
  Settings->>Renderer: call renderKeyInput(shortcutKeys)
  Renderer->>Labels: filter keys -> availableShortcuts
  alt key has label
    Renderer-->>Settings: render Row for key
  else no label
    Renderer-->>Settings: skip key (no Row)
  end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
code/core/src/manager/settings/shortcuts.tsx (2)

233-233: Minor formatting change with no behavioral impact.

This empty line addition is a whitespace-only change. Ensure it aligns with the project's ESLint and Prettier configuration.


291-317: Consider using .filter().map() or .flatMap() for cleaner code.

The current implementation uses .map() with a conditional return statement, which creates an array containing undefined elements. While React correctly skips undefined values during rendering, a more idiomatic approach would be to filter first, then map.

Option 1: Use .filter().map()

-    const arr = Object.entries(shortcutKeys).map(([feature, { shortcut }]: [Feature, any]) => {
-      if (shortcutLabels[feature] === undefined && (!addonsShortcutLabels || !addonsShortcutLabels[feature])) {
-        return;
-      }
-      return (
+    const arr = Object.entries(shortcutKeys)
+      .filter(([feature]: [Feature, any]) => {
+        return shortcutLabels[feature] !== undefined || (addonsShortcutLabels && addonsShortcutLabels[feature]);
+      })
+      .map(([feature, { shortcut }]: [Feature, any]) => (
         <Row key={feature}>
           {/* @ts-expect-error (non strict) */}
           <Description>{shortcutLabels[feature] || addonsShortcutLabels[feature]}</Description>
 
           <TextInput
             spellCheck="false"
             valid={this.displayError(feature)}
             className="modalInput"
             onBlur={this.onBlur}
             onFocus={this.onFocus(feature)}
             // @ts-expect-error (Converted from ts-ignore)
             onKeyDown={this.onKeyDown}
             value={shortcut ? shortcutToHumanString(shortcut) : ''}
             placeholder="Type keys"
             readOnly
           />
 
           {/* @ts-expect-error (non strict) */}
           <SuccessIcon valid={this.displaySuccessMessage(feature)} />
         </Row>
-      );
-    });
+      ));

Option 2: Use .flatMap()

-    const arr = Object.entries(shortcutKeys).map(([feature, { shortcut }]: [Feature, any]) => {
+    const arr = Object.entries(shortcutKeys).flatMap(([feature, { shortcut }]: [Feature, any]) => {
       if (shortcutLabels[feature] === undefined && (!addonsShortcutLabels || !addonsShortcutLabels[feature])) {
-        return;
+        return [];
       }
       return (
         <Row key={feature}>
           {/* @ts-expect-error (non strict) */}
           <Description>{shortcutLabels[feature] || addonsShortcutLabels[feature]}</Description>
 
           <TextInput
             spellCheck="false"
             valid={this.displayError(feature)}
             className="modalInput"
             onBlur={this.onBlur}
             onFocus={this.onFocus(feature)}
             // @ts-expect-error (Converted from ts-ignore)
             onKeyDown={this.onKeyDown}
             value={shortcut ? shortcutToHumanString(shortcut) : ''}
             placeholder="Type keys"
             readOnly
           />
 
           {/* @ts-expect-error (non strict) */}
           <SuccessIcon valid={this.displaySuccessMessage(feature)} />
         </Row>
       );
     });

Both options eliminate undefined elements from the array and make the intent clearer. Option 1 (.filter().map()) is generally preferred for readability, while Option 2 (.flatMap()) is more concise if you prefer the early return pattern.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e9a2317 and 0811a45.

📒 Files selected for processing (1)
  • code/core/src/manager/settings/shortcuts.tsx (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

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

Adhere to ESLint and Prettier rules across all JS/TS source files

Files:

  • code/core/src/manager/settings/shortcuts.tsx
**/*.{ts,tsx}

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

Fix type errors and prefer precise typings instead of using any or suppressions, consistent with strict mode

Files:

  • code/core/src/manager/settings/shortcuts.tsx
🧬 Code graph analysis (1)
code/core/src/manager/settings/shortcuts.tsx (1)
code/core/src/manager-api/lib/shortcut.ts (1)
  • shortcutToHumanString (172-174)

@nx-cloud
Copy link

nx-cloud bot commented Oct 11, 2025

View your CI Pipeline Execution ↗ for commit 78ca2f1

Command Status Duration Result
nx run-many -t build --parallel=3 ✅ Succeeded 46s View ↗

☁️ Nx Cloud last updated this comment at 2025-11-03 09:35:53 UTC

Copy link
Member

@Sidnioulz Sidnioulz left a comment

Choose a reason for hiding this comment

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

Thanks for your contribution @DKER2!

I think something is wrong with the applied logic unfortunately. I am consistently missing Toggle Outline on both my existing sets of browsers with expired shortcuts and on a fresh port with the default shortcuts.

I think it would be wiser to figure out what is defined in the code, and filter out the settings in local storage that don't match the list in the code. It might also be useful to enforce the sorting present in the code rather than in local storage, as that can result in a messy ordering.

@DKER2
Copy link
Author

DKER2 commented Oct 15, 2025

Thanks for your contribution @DKER2!

I think something is wrong with the applied logic unfortunately. I am consistently missing Toggle Outline on both my existing sets of browsers with expired shortcuts and on a fresh port with the default shortcuts.

I think it would be wiser to figure out what is defined in the code, and filter out the settings in local storage that don't match the list in the code. It might also be useful to enforce the sorting present in the code rather than in local storage, as that can result in a messy ordering.

Hi @Sidnioulz, based on this code

useEffect(() => {
api.setAddonShortcut(ADDON_ID, {
label: 'Toggle Outline',
defaultShortcut: ['alt', 'O'],
actionName: 'outline',
showInMenu: false,
action: toggleOutline,
});
}, [toggleOutline, api]);
, it is seem that the Toggle Outline is initilized after the webapp start, so when you directly reload on shortcuts page, and this addonShortcut have not been initiliazed yet. So this action is not initiliazed yet for that shortcut

default:
addonsShortcuts[feature].action();
break;

@Sidnioulz
Copy link
Member

, it is seem that the Toggle Outline is initilized after the webapp start, so when you directly reload on shortcuts page, and this addonShortcut have not been initiliazed yet. So this action is not initiliazed yet for that shortcut

Oh wow, good catch. I definitely didn't expect that.

@Sidnioulz Sidnioulz changed the base branch from next to 10.1 October 20, 2025 08:07
@Sidnioulz Sidnioulz changed the title fix: do not display shortcut if it is not initialized Bug: Do not display non-existing shortcuts in the settings [age Oct 20, 2025
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: 6

Caution

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

⚠️ Outside diff range comments (7)
code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts (1)

722-734: Test assertion may not match the test intent.

The test description states "should bail transformation and not warn when file is already transformed," but:

  1. Line 733 checks logger.log instead of logger.warn
  2. An already-transformed file would have transformedStoryExports.size === 0, triggering the bailout at line 244 of the main code, which emits logger.warn
  3. This means the test might pass while still emitting warnings

Consider either:

  • Changing the assertion to check logger.warn to match the test intent, OR
  • Updating the bailout logic to detect already-transformed files and skip the warning (as suggested in the previous file review)
-      expect(logger.log).not.toHaveBeenCalled();
+      expect(logger.warn).not.toHaveBeenCalled();
code/lib/cli-storybook/src/codemod/csf-factories.ts (1)

107-107: Remove unsafe non-null assertions on previewConfigPath.

The non-null assertion operator (!) at lines 107 and 122 masks a real safety issue. The parameter previewConfigPath is typed as optional (string | undefined) in AutofixOptions, but it's passed directly to functions expecting a required string without any validation. Additionally, line 72 uses previewConfigPath in a template literal without assertion, which would expose "undefined" in the user-facing message if the value is actually undefined.

Add a guard clause at the start of the run() function to ensure previewConfigPath is provided:

if (!previewConfigPath) {
  throw new Error('previewConfigPath is required for CSF Factories migration');
}

Then remove the ! operators at lines 107 and 122 once the validation is in place.

code/frameworks/nextjs/src/preset.ts (1)

172-172: Use isNextVersionGte consistently for all version checks.

Line 172 still uses the old pattern with semver.gte directly, while the v16 check at line 54 was migrated to use the new isNextVersionGte utility. The new utility applies semver.coerce to normalize version strings with prerelease tags or metadata, making it more robust. This inconsistency could cause the v14 check to behave differently from the v16 check when Next.js versions have non-standard formatting.

Apply this diff to align with the refactoring:

-  const isNext14orNewer = semver.gte(nextjsVersion, '14.0.0');
+  const isNext14orNewer = isNextVersionGte('14.0.0');

Note: After this change, you can also remove the direct getNextjsVersion() call at line 169 if it's only used for this comparison. The isNextVersionGte utility calls it internally.

code/core/src/common/utils/load-main-config.ts (1)

53-55: Bug: temp filename has a double dot before the extension.

${name}.tmp.${ext} yields main.tmp..ts. Use ${name}.tmp${ext}.

-        const { ext, name, dir } = parse(mainPath);
-        const modifiedMainPath = join(dir, `${name}.tmp.${ext}`);
+        const { ext, name, dir } = parse(mainPath);
+        const modifiedMainPath = join(dir, `${name}.tmp${ext}`);
code/core/src/manager-api/modules/stories.ts (2)

371-387: Avoid bypassing the empty-hash guard in jumpToComponent

Defaulting to {} makes the if (!hash) check ineffective and can pass an empty index to sibling lookup. Remove the fallback and/or guard for emptiness.

Apply one of:

- const hash = refId ? refs[refId].filteredIndex || {} : filteredIndex;
- if (!hash) {
+ const hash = refId ? refs[refId].filteredIndex : filteredIndex;
+ if (!hash || Object.keys(hash).length === 0) {
    return;
  }

Also applies to: 379-383


476-484: findLeafEntry can recurse into missing entries; fix and align types

  • If no child exists in index, recursion uses a non-existent ID and may crash.
  • The docstring says “or null if no leaf entry was found”, but the signature returns only API_LeafEntry; the callers already use optional chaining.

Recommend:

  • Return API_LeafEntry | undefined.
  • Stop recursion when there’s no existing child in index.
- findLeafEntry(index: API_IndexHash, storyId: StoryId): API_LeafEntry;
+ findLeafEntry(index: API_IndexHash, storyId: StoryId): API_LeafEntry | undefined;
@@
   const entry = index[storyId];
-  if (entry.type === 'docs' || entry.type === 'story') {
+  if (!entry) return undefined;
+  if (entry.type === 'docs' || entry.type === 'story') {
     return entry;
   }
-  const childStoryId = entry.children.find((childId) => index[childId]) || entry.children[0];
-  return api.findLeafEntry(index, childStoryId);
+  const existingChildId = entry.children.find((childId) => !!index[childId]);
+  if (!existingChildId) return undefined;
+  return api.findLeafEntry(index, existingChildId);

No other changes needed; call sites already use optional chaining or pass result into a guarded navigator.

code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.tsx (1)

74-87: Fix function signature mismatch for isValidWorkspaceConfigFile

The declared type takes (fileContents: string, babel: any) but the implementation only accepts one arg and uses the imported babel. Align the signature to avoid confusion.

-export const isValidWorkspaceConfigFile: (fileContents: string, babel: any) => boolean = (
-  fileContents
-) => {
+export const isValidWorkspaceConfigFile = (fileContents: string): boolean => {

And update call sites to pass only fileContents.

🧹 Nitpick comments (14)
code/lib/eslint-plugin/package.json (1)

61-61: Consider updating ts-dedent to the latest stable version.

The ts-dedent dependency is currently pinned to ^2.0.0. According to recent information, version 2.2.0 is the latest stable release with improved indentation handling for multiline values. Updating to ^2.2.0 would be optional but would unlock these improvements.

code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts (1)

242-258: Early bailout logic looks correct, but may produce warnings for already-transformed files.

The early bailout when transformedStoryExports.size === 0 appropriately prevents broken indexer scenarios. However, already-transformed files (which contain meta.story() calls instead of object literals) would trigger this bailout and emit a warning, even though they don't need transformation.

Consider detecting already-transformed files before the bailout to avoid unnecessary warnings. For example, check if the source already contains preview.meta( or .story( patterns.

Example detection:

+  // Skip transformation if file is already in CSF4 format
+  if (info.source.includes('.story(') || info.source.includes('preview.meta(')) {
+    return info.source;
+  }
+
   // If no stories were transformed, bail early to avoid having a mixed CSF syntax and therefore a broken indexer.
   if (transformedStoryExports.size === 0) {
code/core/src/node-logger/prompts/prompt-provider-clack.ts (1)

56-59: Redundant property assignment after spread.

The explicit required: options.required is redundant since the spread ...options already includes the required field. This change doesn't alter behavior but adds unnecessary code.

Consider simplifying to:

-    const result = await clack.multiselect<T>({
-      ...options,
-      required: options.required,
-    });
+    const result = await clack.multiselect<T>(options);
code/lib/cli-storybook/src/codemod/csf-factories.ts (1)

70-74: Improve grammar in user-facing message.

The phrase "absolute imports of your...file" is grammatically awkward. Consider revising to "absolute imports to reference your preview config file" or "absolute imports for your preview configuration" for better clarity.

Apply this diff to improve the message:

-        The CSF Factories format can benefit from using absolute imports of your ${picocolors.cyan(previewConfigPath)} file. We can configure that for you, using subpath imports (a node standard), by adjusting the imports property of your package.json.
+        The CSF Factories format can benefit from using absolute imports to reference your ${picocolors.cyan(previewConfigPath)} file. We can configure that for you, using subpath imports (a node standard), by adjusting the imports property of your package.json.
code/core/src/common/utils/load-main-config.ts (4)

14-22: Default and document skipCache param.

Make intent explicit and TS-friendly.

-export async function loadMainConfig({
-  configDir = '.storybook',
-  cwd,
-  skipCache,
-}: {
+export async function loadMainConfig({
+  configDir = '.storybook',
+  cwd,
+  skipCache = false,
+}: {
   configDir: string;
   cwd?: string;
-  skipCache?: boolean;
+  /** Bypass module cache when importing main config */
+  skipCache?: boolean;
 }):

Ensure all call sites either rely on default or pass a boolean.


34-37: Broaden ESM error detection to cover older messages.

Some Node/bundler variants still emit “require is not defined”.

-    if (e.message.includes('not defined in ES module scope')) {
+    if (
+      e.message.includes('not defined in ES module scope') ||
+      e.message.includes('require is not defined')
+    ) {

58-61: Honor skipCache for the temporary import and make cleanup resilient.

  • Pass through skipCache.
  • Use rm(..., { force: true }) to avoid races.
-          out = await importModule(modifiedMainPath);
+          out = await importModule(modifiedMainPath, { skipCache });
         } finally {
-          await rm(modifiedMainPath);
+          await rm(modifiedMainPath, { force: true });
         }

35-37: Consider elevating to warn level.

This path indicates config incompatibility; logger.warn may be more appropriate than info.

CHANGELOG.prerelease.md (1)

3-5: Fix grammar in beta.13 entries ("it's" → "its").

User-visible docs polish.

-- Move: Addon jest into it's own repository - [#32646](https://github.com/storybookjs/storybook/pull/32646), thanks @ndelangen!
+- Move: Addon jest into its own repository - [#32646](https://github.com/storybookjs/storybook/pull/32646), thanks @ndelangen!
code/core/src/manager-api/modules/stories.ts (1)

441-444: Requiring filteredHash may block navigation early

selectStory now returns if either hash or filteredHash is falsy. During initial load (or for refs before filters are applied), filteredHash can be absent. Consider falling back to hash for leaf resolution.

- const hash = ref ? refs[ref].index : index;
- const filteredHash = ref ? refs[ref].filteredIndex : filteredIndex;
- if (!hash || !filteredHash) {
+ const hash = ref ? refs[ref].index : index;
+ const filteredHash = ref ? refs[ref].filteredIndex : filteredIndex;
+ if (!hash) {
    return;
  }
+ const leafIndex = filteredHash || hash;

And below:

- gotoStory(api.findLeafEntry(filteredHash, entry.id));
+ gotoStory(api.findLeafEntry(leafIndex, entry.id));

Please confirm there’s no regression when reloading directly on the shortcuts/settings routes before filters are computed. Based on learnings.

code/core/src/manager-api/tests/stories.test.ts (1)

79-98: Add edge-case tests: no visible children and missing filteredIndex

To guard regressions:

  • When a component exists in filteredIndex but all of its children are filtered out, selectStory('componentId') should no-op (no navigate, no crash).
  • When filteredIndex is undefined (early load), selectStory('componentId') should still work by falling back to index.

I can provide test snippets if helpful.

Also applies to: 607-707, 1360-1386

code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.tsx (1)

61-73: Broaden isSafeToExtendWorkspace param type to match call sites

You call isSafeToExtendWorkspace(arg as CallExpression) but the guard inside already checks isCallExpression. Type it as Declaration to reflect usage.

-const isSafeToExtendWorkspace = (path: CallExpression) =>
+const isSafeToExtendWorkspace = (path: Declaration) =>
  isCallExpression(path) &&
  path.arguments.length > 0 &&
  isObjectExpression(path.arguments[0]) &&
  ...
code/addons/vitest/src/updateVitestFile.ts (1)

82-139: Confirm minimum Node runtime for findLastIndex

This relies on Array.prototype.findLastIndex. Ensure our supported Node version in this package/CLI is >=18 (ideally >=20 per Vitest 3.x). If not guaranteed, replace with a backward-compatible scan.

code/addons/vitest/src/updateVitestFile.test.ts (1)

18-20: Adopt Vitest mocking conventions (spy: true, vi.mocked) per repo guidelines

Per guidelines, prefer vi.mock(..., { spy: true }) and vi.mocked() for typed access. Update the mocks (logger, module resolver, events, global) to use spy mode; you’re already using vi.mocked in places—make it consistent.

Based on coding guidelines.

Also applies to: 24-31, 46-56, 120-131, 195-203, 227-238, 302-313, 375-386, 447-458, 498-509, 581-592, 651-662, 675-686, 745-756, 807-818

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c1d8f6 and 21e9058.

⛔ Files ignored due to path filters (2)
  • code/addons/jest/docs/storybook-addon-jest.gif is excluded by !**/*.gif
  • code/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (106)
  • CHANGELOG.md (1 hunks)
  • CHANGELOG.prerelease.md (1 hunks)
  • MIGRATION.md (3 hunks)
  • code/addons/a11y/package.json (1 hunks)
  • code/addons/docs/package.json (1 hunks)
  • code/addons/jest/README.md (0 hunks)
  • code/addons/jest/build-config.ts (0 hunks)
  • code/addons/jest/manager.js (0 hunks)
  • code/addons/jest/package.json (0 hunks)
  • code/addons/jest/project.json (0 hunks)
  • code/addons/jest/src/components/Message.tsx (0 hunks)
  • code/addons/jest/src/components/Panel.tsx (0 hunks)
  • code/addons/jest/src/components/Result.tsx (0 hunks)
  • code/addons/jest/src/hoc/provideJestResult.tsx (0 hunks)
  • code/addons/jest/src/index.ts (0 hunks)
  • code/addons/jest/src/manager.tsx (0 hunks)
  • code/addons/jest/src/shared.test.ts (0 hunks)
  • code/addons/jest/src/shared.ts (0 hunks)
  • code/addons/jest/src/types.ts (0 hunks)
  • code/addons/jest/tsconfig.json (0 hunks)
  • code/addons/jest/vitest.config.ts (0 hunks)
  • code/addons/links/package.json (1 hunks)
  • code/addons/onboarding/package.json (1 hunks)
  • code/addons/pseudo-states/package.json (1 hunks)
  • code/addons/themes/package.json (1 hunks)
  • code/addons/vitest/package.json (1 hunks)
  • code/addons/vitest/src/updateVitestFile.test.ts (11 hunks)
  • code/addons/vitest/src/updateVitestFile.ts (2 hunks)
  • code/builders/builder-vite/package.json (1 hunks)
  • code/builders/builder-webpack5/package.json (1 hunks)
  • code/core/package.json (1 hunks)
  • code/core/src/cli/detect.ts (1 hunks)
  • code/core/src/common/utils/get-addon-annotations.ts (2 hunks)
  • code/core/src/common/utils/load-main-config.ts (1 hunks)
  • code/core/src/common/versions.ts (1 hunks)
  • code/core/src/core-server/__for-testing__/main.ts (0 hunks)
  • code/core/src/manager-api/modules/stories.ts (4 hunks)
  • code/core/src/manager-api/tests/stories.test.ts (1 hunks)
  • code/core/src/manager-api/version.ts (1 hunks)
  • code/core/src/manager/components/sidebar/TagsFilterPanel.tsx (1 hunks)
  • code/core/src/node-logger/prompts/prompt-provider-clack.ts (1 hunks)
  • code/core/src/shared/utils/module.ts (2 hunks)
  • code/frameworks/angular/package.json (1 hunks)
  • code/frameworks/ember/package.json (1 hunks)
  • code/frameworks/html-vite/package.json (1 hunks)
  • code/frameworks/nextjs-vite/package.json (1 hunks)
  • code/frameworks/nextjs/package.json (1 hunks)
  • code/frameworks/nextjs/src/config/webpack.ts (1 hunks)
  • code/frameworks/nextjs/src/preset.ts (2 hunks)
  • code/frameworks/nextjs/src/utils.ts (2 hunks)
  • code/frameworks/preact-vite/package.json (1 hunks)
  • code/frameworks/react-native-web-vite/package.json (1 hunks)
  • code/frameworks/react-vite/package.json (1 hunks)
  • code/frameworks/react-webpack5/package.json (1 hunks)
  • code/frameworks/server-webpack5/package.json (1 hunks)
  • code/frameworks/svelte-vite/package.json (1 hunks)
  • code/frameworks/sveltekit/package.json (1 hunks)
  • code/frameworks/vue3-vite/package.json (1 hunks)
  • code/frameworks/web-components-vite/package.json (1 hunks)
  • code/lib/cli-sb/package.json (1 hunks)
  • code/lib/cli-storybook/package.json (1 hunks)
  • code/lib/cli-storybook/src/add.ts (2 hunks)
  • code/lib/cli-storybook/src/automigrate/fixes/fix-faux-esm-require.test.ts (6 hunks)
  • code/lib/cli-storybook/src/automigrate/fixes/fix-faux-esm-require.ts (2 hunks)
  • code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.test.ts (2 hunks)
  • code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.ts (1 hunks)
  • code/lib/cli-storybook/src/automigrate/multi-project.test.ts (2 hunks)
  • code/lib/cli-storybook/src/automigrate/multi-project.ts (1 hunks)
  • code/lib/cli-storybook/src/codemod/csf-factories.ts (2 hunks)
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts (9 hunks)
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts (1 hunks)
  • code/lib/codemod/package.json (1 hunks)
  • code/lib/core-webpack/package.json (1 hunks)
  • code/lib/create-storybook/package.json (1 hunks)
  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts (2 hunks)
  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.tsx (2 hunks)
  • code/lib/csf-plugin/package.json (1 hunks)
  • code/lib/eslint-plugin/package.json (1 hunks)
  • code/lib/react-dom-shim/package.json (1 hunks)
  • code/package.json (1 hunks)
  • code/presets/create-react-app/package.json (1 hunks)
  • code/presets/react-webpack/package.json (1 hunks)
  • code/presets/server-webpack/package.json (1 hunks)
  • code/renderers/html/package.json (1 hunks)
  • code/renderers/preact/package.json (1 hunks)
  • code/renderers/react/package.json (1 hunks)
  • code/renderers/server/package.json (1 hunks)
  • code/renderers/svelte/package.json (1 hunks)
  • code/renderers/vue3/package.json (1 hunks)
  • code/renderers/web-components/package.json (1 hunks)
  • docs/_snippets/addon-a11y-config-in-preview.md (4 hunks)
  • docs/_snippets/vite-includeStorybookNextjsPlugin.md (1 hunks)
  • docs/releases/migration-guide.mdx (1 hunks)
  • docs/versions/next.json (1 hunks)
  • docs/writing-tests/integrations/stories-in-unit-tests.mdx (1 hunks)
  • scripts/.eslintignore (0 hunks)
  • scripts/build/entry-configs.ts (0 hunks)
  • test-storybooks/ember-cli/package.json (0 hunks)
  • test-storybooks/external-docs/package.json (0 hunks)
  • test-storybooks/portable-stories-kitchen-sink/nextjs/package.json (0 hunks)
  • test-storybooks/portable-stories-kitchen-sink/react/package.json (0 hunks)
  • test-storybooks/portable-stories-kitchen-sink/svelte/package.json (0 hunks)
  • test-storybooks/portable-stories-kitchen-sink/vue3/package.json (1 hunks)
  • test-storybooks/server-kitchen-sink/package.json (0 hunks)
  • test-storybooks/standalone-preview/package.json (0 hunks)
  • test-storybooks/yarn-pnp/package.json (0 hunks)
💤 Files with no reviewable changes (27)
  • code/addons/jest/src/types.ts
  • code/core/src/core-server/for-testing/main.ts
  • code/addons/jest/src/components/Result.tsx
  • code/addons/jest/src/shared.test.ts
  • code/addons/jest/vitest.config.ts
  • test-storybooks/portable-stories-kitchen-sink/nextjs/package.json
  • code/addons/jest/package.json
  • test-storybooks/portable-stories-kitchen-sink/react/package.json
  • test-storybooks/server-kitchen-sink/package.json
  • code/addons/jest/src/index.ts
  • code/addons/jest/src/hoc/provideJestResult.tsx
  • scripts/build/entry-configs.ts
  • test-storybooks/standalone-preview/package.json
  • test-storybooks/ember-cli/package.json
  • code/addons/jest/project.json
  • test-storybooks/yarn-pnp/package.json
  • code/addons/jest/tsconfig.json
  • test-storybooks/external-docs/package.json
  • code/addons/jest/src/manager.tsx
  • code/addons/jest/README.md
  • test-storybooks/portable-stories-kitchen-sink/svelte/package.json
  • code/addons/jest/src/components/Message.tsx
  • code/addons/jest/manager.js
  • code/addons/jest/src/shared.ts
  • scripts/.eslintignore
  • code/addons/jest/build-config.ts
  • code/addons/jest/src/components/Panel.tsx
✅ Files skipped from review due to trivial changes (35)
  • code/frameworks/nextjs-vite/package.json
  • code/frameworks/react-native-web-vite/package.json
  • code/frameworks/svelte-vite/package.json
  • code/core/package.json
  • code/lib/react-dom-shim/package.json
  • code/frameworks/vue3-vite/package.json
  • code/frameworks/react-vite/package.json
  • code/addons/docs/package.json
  • code/renderers/server/package.json
  • code/lib/csf-plugin/package.json
  • code/presets/react-webpack/package.json
  • code/addons/themes/package.json
  • code/lib/codemod/package.json
  • code/renderers/html/package.json
  • code/frameworks/web-components-vite/package.json
  • code/renderers/web-components/package.json
  • code/addons/vitest/package.json
  • code/core/src/manager/components/sidebar/TagsFilterPanel.tsx
  • code/renderers/react/package.json
  • code/builders/builder-vite/package.json
  • code/frameworks/angular/package.json
  • code/addons/links/package.json
  • code/builders/builder-webpack5/package.json
  • code/lib/cli-sb/package.json
  • code/renderers/svelte/package.json
  • code/lib/cli-storybook/package.json
  • code/presets/server-webpack/package.json
  • code/renderers/preact/package.json
  • code/frameworks/react-webpack5/package.json
  • code/addons/a11y/package.json
  • code/frameworks/nextjs/package.json
  • code/core/src/common/utils/get-addon-annotations.ts
  • CHANGELOG.md
  • code/lib/create-storybook/package.json
  • code/addons/pseudo-states/package.json
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{js,jsx,json,html,ts,tsx,mjs}

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

**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier formatting on changed files before committing
Run ESLint on changed files and fix all errors/warnings before committing (use yarn lint:js:cmd <file>)

Files:

  • code/frameworks/ember/package.json
  • code/frameworks/nextjs/src/preset.ts
  • code/package.json
  • code/lib/core-webpack/package.json
  • code/core/src/common/versions.ts
  • code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.test.ts
  • code/core/src/manager-api/tests/stories.test.ts
  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.tsx
  • code/core/src/cli/detect.ts
  • code/frameworks/html-vite/package.json
  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
  • code/lib/cli-storybook/src/automigrate/multi-project.ts
  • code/addons/vitest/src/updateVitestFile.ts
  • code/addons/onboarding/package.json
  • code/core/src/manager-api/version.ts
  • code/lib/cli-storybook/src/automigrate/fixes/fix-faux-esm-require.test.ts
  • code/presets/create-react-app/package.json
  • code/renderers/vue3/package.json
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts
  • code/frameworks/server-webpack5/package.json
  • test-storybooks/portable-stories-kitchen-sink/vue3/package.json
  • code/lib/cli-storybook/src/automigrate/multi-project.test.ts
  • code/frameworks/nextjs/src/utils.ts
  • code/core/src/common/utils/load-main-config.ts
  • code/core/src/node-logger/prompts/prompt-provider-clack.ts
  • docs/versions/next.json
  • code/addons/vitest/src/updateVitestFile.test.ts
  • code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.ts
  • code/lib/eslint-plugin/package.json
  • code/frameworks/sveltekit/package.json
  • code/frameworks/preact-vite/package.json
  • code/lib/cli-storybook/src/automigrate/fixes/fix-faux-esm-require.ts
  • code/core/src/shared/utils/module.ts
  • code/lib/cli-storybook/src/add.ts
  • code/lib/cli-storybook/src/codemod/csf-factories.ts
  • code/core/src/manager-api/modules/stories.ts
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
  • code/frameworks/nextjs/src/config/webpack.ts
**/*.{ts,tsx,js,jsx,mjs}

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

Export functions from modules when they need to be unit-tested

Files:

  • code/frameworks/nextjs/src/preset.ts
  • code/core/src/common/versions.ts
  • code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.test.ts
  • code/core/src/manager-api/tests/stories.test.ts
  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.tsx
  • code/core/src/cli/detect.ts
  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
  • code/lib/cli-storybook/src/automigrate/multi-project.ts
  • code/addons/vitest/src/updateVitestFile.ts
  • code/core/src/manager-api/version.ts
  • code/lib/cli-storybook/src/automigrate/fixes/fix-faux-esm-require.test.ts
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts
  • code/lib/cli-storybook/src/automigrate/multi-project.test.ts
  • code/frameworks/nextjs/src/utils.ts
  • code/core/src/common/utils/load-main-config.ts
  • code/core/src/node-logger/prompts/prompt-provider-clack.ts
  • code/addons/vitest/src/updateVitestFile.test.ts
  • code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.ts
  • code/lib/cli-storybook/src/automigrate/fixes/fix-faux-esm-require.ts
  • code/core/src/shared/utils/module.ts
  • code/lib/cli-storybook/src/add.ts
  • code/lib/cli-storybook/src/codemod/csf-factories.ts
  • code/core/src/manager-api/modules/stories.ts
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
  • code/frameworks/nextjs/src/config/webpack.ts
code/**/*.{ts,tsx,js,jsx,mjs}

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

In application code, use Storybook loggers instead of console.* (client code: storybook/internal/client-logger; server code: storybook/internal/node-logger)

Files:

  • code/frameworks/nextjs/src/preset.ts
  • code/core/src/common/versions.ts
  • code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.test.ts
  • code/core/src/manager-api/tests/stories.test.ts
  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.tsx
  • code/core/src/cli/detect.ts
  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
  • code/lib/cli-storybook/src/automigrate/multi-project.ts
  • code/addons/vitest/src/updateVitestFile.ts
  • code/core/src/manager-api/version.ts
  • code/lib/cli-storybook/src/automigrate/fixes/fix-faux-esm-require.test.ts
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts
  • code/lib/cli-storybook/src/automigrate/multi-project.test.ts
  • code/frameworks/nextjs/src/utils.ts
  • code/core/src/common/utils/load-main-config.ts
  • code/core/src/node-logger/prompts/prompt-provider-clack.ts
  • code/addons/vitest/src/updateVitestFile.test.ts
  • code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.ts
  • code/lib/cli-storybook/src/automigrate/fixes/fix-faux-esm-require.ts
  • code/core/src/shared/utils/module.ts
  • code/lib/cli-storybook/src/add.ts
  • code/lib/cli-storybook/src/codemod/csf-factories.ts
  • code/core/src/manager-api/modules/stories.ts
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
  • code/frameworks/nextjs/src/config/webpack.ts
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

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

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/frameworks/nextjs/src/preset.ts
  • code/core/src/common/versions.ts
  • code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.test.ts
  • code/core/src/manager-api/tests/stories.test.ts
  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.tsx
  • code/core/src/cli/detect.ts
  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
  • code/lib/cli-storybook/src/automigrate/multi-project.ts
  • code/addons/vitest/src/updateVitestFile.ts
  • code/core/src/manager-api/version.ts
  • code/lib/cli-storybook/src/automigrate/fixes/fix-faux-esm-require.test.ts
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.ts
  • code/lib/cli-storybook/src/automigrate/multi-project.test.ts
  • code/frameworks/nextjs/src/utils.ts
  • code/core/src/common/utils/load-main-config.ts
  • code/core/src/node-logger/prompts/prompt-provider-clack.ts
  • code/addons/vitest/src/updateVitestFile.test.ts
  • code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.ts
  • code/lib/cli-storybook/src/automigrate/fixes/fix-faux-esm-require.ts
  • code/core/src/shared/utils/module.ts
  • code/lib/cli-storybook/src/add.ts
  • code/lib/cli-storybook/src/codemod/csf-factories.ts
  • code/core/src/manager-api/modules/stories.ts
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
  • code/frameworks/nextjs/src/config/webpack.ts
docs/**

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

Update documentation under docs/ for significant changes, including migration guides for breaking changes

Files:

  • docs/writing-tests/integrations/stories-in-unit-tests.mdx
  • docs/releases/migration-guide.mdx
  • docs/versions/next.json
  • docs/_snippets/vite-includeStorybookNextjsPlugin.md
  • docs/_snippets/addon-a11y-config-in-preview.md
code/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

code/**/*.{test,spec}.{ts,tsx}: Place all test files under the code/ directory
Name test files as *.test.ts, *.test.tsx, *.spec.ts, or *.spec.tsx

Files:

  • code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.test.ts
  • code/core/src/manager-api/tests/stories.test.ts
  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
  • code/lib/cli-storybook/src/automigrate/fixes/fix-faux-esm-require.test.ts
  • code/lib/cli-storybook/src/automigrate/multi-project.test.ts
  • code/addons/vitest/src/updateVitestFile.test.ts
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.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 mocked functions
Implement mock behaviors in beforeEach blocks
Mock all required dependencies that the test subject uses
Mock implementations should be placed in beforeEach blocks
Each mock implementation should return a Promise for async functions
Mock implementations should match the expected return type of the original function
Use vi.mocked() to access and implement mock behaviors
Mock all required properties and methods that the test subject uses
Avoid direct function mocking without vi.mocked()
Avoid mock implementations outside of beforeEach blocks
Avoid mocking without the spy: true option
Avoid inline mock implementations within test cases
Avoid mocking only a subset of required dependencies
Mock at the highest level of abstraction needed
Keep mock implementations simple and focused
Use type-safe mocking with vi.mocked()
Document complex mock behaviors
Group related mocks together

Files:

  • code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.test.ts
  • code/core/src/manager-api/tests/stories.test.ts
  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
  • code/lib/cli-storybook/src/automigrate/fixes/fix-faux-esm-require.test.ts
  • code/lib/cli-storybook/src/automigrate/multi-project.test.ts
  • code/addons/vitest/src/updateVitestFile.test.ts
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
**/*.@(test|spec).{ts,tsx,js,jsx}

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

**/*.@(test|spec).{ts,tsx,js,jsx}: Unit tests should import and execute the functions under test rather than only asserting on syntax patterns
Mock external dependencies in tests using vi.mock() (e.g., filesystem, loggers)

Files:

  • code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.test.ts
  • code/core/src/manager-api/tests/stories.test.ts
  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
  • code/lib/cli-storybook/src/automigrate/fixes/fix-faux-esm-require.test.ts
  • code/lib/cli-storybook/src/automigrate/multi-project.test.ts
  • code/addons/vitest/src/updateVitestFile.test.ts
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
test-storybooks/**

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

Maintain test Storybook configurations under test-storybooks/ for E2E and visual testing scenarios

Files:

  • test-storybooks/portable-stories-kitchen-sink/vue3/package.json
🧠 Learnings (7)
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests

Applied to files:

  • code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts
  • code/lib/cli-storybook/src/automigrate/fixes/fix-faux-esm-require.test.ts
  • code/addons/vitest/src/updateVitestFile.test.ts
📚 Learning: 2025-10-13T13:33:14.659Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-13T13:33:14.659Z
Learning: Applies to **/*.@(test|spec).{ts,tsx,js,jsx} : Mock external dependencies in tests using `vi.mock()` (e.g., filesystem, loggers)

Applied to files:

  • code/lib/cli-storybook/src/automigrate/fixes/fix-faux-esm-require.test.ts
  • code/addons/vitest/src/updateVitestFile.test.ts
  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
📚 Learning: 2025-10-13T13:33:14.659Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-13T13:33:14.659Z
Learning: Applies to test-storybooks/** : Maintain test Storybook configurations under `test-storybooks/` for E2E and visual testing scenarios

Applied to files:

  • test-storybooks/portable-stories-kitchen-sink/vue3/package.json
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use vi.mocked() to access and implement mock behaviors

Applied to files:

  • code/addons/vitest/src/updateVitestFile.test.ts
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use vi.mocked() to type and access mocked functions

Applied to files:

  • code/addons/vitest/src/updateVitestFile.test.ts
📚 Learning: 2025-10-13T13:33:14.659Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-13T13:33:14.659Z
Learning: Applies to scripts/**/*.{ts,js,mjs} : In Node.js scripts, use `storybook/internal/node-logger` instead of `console.*`

Applied to files:

  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
📚 Learning: 2025-10-13T13:33:14.659Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-13T13:33:14.659Z
Learning: Applies to code/**/*.{ts,tsx,js,jsx,mjs} : In application code, use Storybook loggers instead of `console.*` (client code: `storybook/internal/client-logger`; server code: `storybook/internal/node-logger`)

Applied to files:

  • code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts
🧬 Code graph analysis (13)
code/frameworks/nextjs/src/preset.ts (1)
code/frameworks/nextjs/src/utils.ts (1)
  • isNextVersionGte (28-32)
code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.test.ts (1)
code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.ts (3)
  • containsPatternUsage (259-271)
  • containsDirnameUsage (274-276)
  • hasDirnameDefined (283-287)
code/core/src/manager-api/tests/stories.test.ts (2)
code/core/src/manager-api/tests/mockStoriesEntries.ts (1)
  • navigationEntries (71-120)
code/core/src/manager-api/tests/url.test.js (1)
  • store (143-151)
code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts (1)
code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.tsx (1)
  • vitestConfigFiles (95-146)
code/lib/cli-storybook/src/automigrate/fixes/fix-faux-esm-require.test.ts (2)
code/lib/cli-storybook/src/automigrate/fixes/fix-faux-esm-require.ts (1)
  • fixFauxEsmRequire (67-216)
code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.ts (1)
  • containsDirnameUsage (274-276)
code/lib/cli-storybook/src/automigrate/multi-project.test.ts (1)
code/lib/cli-storybook/src/automigrate/multi-project.ts (1)
  • promptForAutomigrations (197-250)
code/core/src/common/utils/load-main-config.ts (1)
code/core/src/shared/utils/module.ts (1)
  • importModule (54-89)
code/addons/vitest/src/updateVitestFile.test.ts (1)
code/addons/vitest/src/updateVitestFile.ts (3)
  • updateConfigFile (79-301)
  • loadTemplate (9-16)
  • updateWorkspaceFile (303-364)
code/lib/cli-storybook/src/automigrate/fixes/fix-faux-esm-require.ts (2)
code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.ts (6)
  • containsRequireUsage (251-256)
  • containsFilenameUsage (278-280)
  • containsDirnameUsage (274-276)
  • updateMainConfig (207-230)
  • bannerComment (298-299)
  • BannerConfig (292-296)
code/lib/cli-storybook/src/automigrate/types.ts (1)
  • Fix (52-60)
code/lib/cli-storybook/src/add.ts (2)
code/core/src/common/utils/load-main-config.ts (1)
  • loadMainConfig (14-71)
code/core/src/common/utils/sync-main-preview-addons.ts (1)
  • syncStorybookAddons (16-25)
code/lib/cli-storybook/src/codemod/csf-factories.ts (1)
scripts/utils/tools.ts (1)
  • picocolors (117-117)
code/core/src/manager-api/modules/stories.ts (2)
code/core/src/manager-api/root.tsx (1)
  • API_HashEntry (308-308)
code/core/src/csf/index.ts (2)
  • sanitize (8-16)
  • toId (27-28)
code/frameworks/nextjs/src/config/webpack.ts (1)
code/frameworks/nextjs/src/utils.ts (1)
  • isNextVersionGte (28-32)
🪛 LanguageTool
docs/_snippets/vite-includeStorybookNextjsPlugin.md

[grammar] ~1-~1: Hier könnte ein Fehler sein.
Context: ts filename="vitest.config.ts" renderer="react" language="ts" import { defineConfig } from "vite"; import { storybookNextJsPlugin } from '@storybook/nextjs-vite/vite-plugin' export default defineConfig({ // only necessary when not using @storybook/addon-vitest, otherwise the plugin is loaded automatically plugins: [storybookNextJsPlugin()], });

(QB_NEW_DE)

🔇 Additional comments (46)
code/lib/core-webpack/package.json (1)

3-3: Version bump is well-formed and consistent.

The version update from 10.0.0-beta.10 to 10.0.0-beta.13 follows valid semver conventions. Dependencies (notably ts-dedent at ^2.0.0) remain stable and correctly specified. No functional or API changes detected.

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

3-3: Version bump to 10.0.0-beta.13 looks good.

Straightforward version update aligned with repository-wide release. No functional changes and the version string is properly formatted.

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

3-3: Version bump to 10.0.0-beta.13 looks good.

Straightforward version update consistent with html-vite and aligned with repository-wide release. No functional changes, dependencies, or structural modifications.

code/frameworks/sveltekit/package.json (1)

3-3: Version bump is a straightforward patch update to align with coordinated release.

The version bump from 10.0.0-beta.10 to 10.0.0-beta.13 is consistent with the monorepo's beta release cycle. Since this is part of a larger coordinated version bump across multiple packages (as noted in the PR summary), no concerns here.

code/frameworks/ember/package.json (1)

3-3: Version bump is appropriate.

The version bump from 10.0.0-beta.10 to 10.0.0-beta.13 is consistent with the broader version refresh mentioned in the PR context across Storybook packages. The change is isolated and follows semantic versioning for beta releases.

code/presets/create-react-app/package.json (1)

3-3: Version bump is consistent with coordinated release updates.

The version update to 10.0.0-beta.13 aligns with the broader set of version updates across Storybook packages mentioned in the PR context. The preset's version alignment with the core storybook package (specified as workspace:^ in peerDependencies) is appropriate.

test-storybooks/portable-stories-kitchen-sink/vue3/package.json (1)

17-53: LGTM!

The removal of the "@storybook/addon-jest" resolution is consistent with the PR's objective to remove addon-jest across test-storybooks, and the file structure remains valid. The trailing newline (line 72) aligns with Prettier formatting conventions.

code/lib/eslint-plugin/package.json (1)

3-3: Version bump is straightforward.

The package version has been updated from 10.0.0-beta.10 to 10.0.0-beta.13, which is consistent with a beta release cycle. No functional changes to the package.

code/addons/onboarding/package.json (1)

3-3: Version bump looks good; routine monorepo version sync.

The version has been updated from "10.0.0-beta.10" to "10.0.0-beta.13", consistent with the PR's broader version bump across packages. JSON formatting is clean and matches Prettier standards.

docs/_snippets/addon-a11y-config-in-preview.md (1)

25-25: Update the old globals API reference in accessibility testing documentation.

The property rename from globals to initialGlobals is confirmed as a breaking change with an automigration system in place. However, the documentation update in docs/_snippets/addon-a11y-config-in-preview.md is inconsistent with docs/writing-tests/accessibility-testing.mdx, which still references the old globals.a11y.manual property at line 56.

Update the accessibility testing doc to use the new initialGlobals naming and consider adding a deprecation notice or migration section to docs/writing-tests/accessibility-testing.mdx explaining that globals has been replaced with initialGlobals. The automigration system will handle code updates, but explicit documentation guidance helps users understand the change.

code/renderers/vue3/package.json (2)

3-3: Mismatch between PR objectives and file contents.

The PR objectives describe changes to keyboard shortcuts filtering in renderKeyInput within the settings module, but the file provided only shows a version bump. The keyboard shortcuts bug fix code is not included in this review.

Verify that the actual keyboard shortcuts fix (described in the PR title and objectives) has been provided for review in a separate file.


3-3: Version bump is valid and consistently applied across the repository.

Verification confirms the update to 10.0.0-beta.13 is part of a coordinated repository-wide version bump. All 43 packages across renderers, addons, frameworks, and related modules have been consistently updated to the same version with no inconsistencies.

code/frameworks/server-webpack5/package.json (1)

3-3: Version bump is consistent across the monorepo—approved.

Verification confirms 43 active packages (frameworks, core, addons, builders, presets) are all correctly synchronized at 10.0.0-beta.13. Test fixtures use 0.0.0 (expected), and utility packages (scripts/ tools) maintain independent versions by design. No issues found.

code/lib/cli-storybook/src/codemod/helpers/story-to-csf-factory.test.ts (2)

11-16: LGTM! Mock setup follows coding guidelines.

The mock correctly includes both log and warn methods with vi.fn(), and uses the spy: true pattern as required by the coding guidelines.

Based on coding guidelines.


681-697: Good test coverage for the new bailout scenario.

The test appropriately verifies that files with no transformable stories are skipped and emit the expected warning message.

code/lib/cli-storybook/src/automigrate/multi-project.ts (1)

246-246: LGTM: Makes automigration selection optional.

Setting required: false allows users to skip all automigrations if desired, which is the correct behavior for an optional selection prompt. The test coverage confirms this behavior.

code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.test.ts (1)

223-332: LGTM: Comprehensive test coverage for pattern detection.

The test suite thoroughly covers the new helper functions with edge cases including:

  • Detection in actual code vs. strings/comments
  • All quote types (double, single, backtick)
  • Escaped quotes and mixed contexts
  • Different variable declaration keywords (const, let, var)
code/lib/cli-storybook/src/automigrate/fixes/fix-faux-esm-require.test.ts (3)

79-143: LGTM: Tests updated to match BannerConfig return type.

The test expectations correctly verify the new return type with hasRequireUsage, hasUnderscoreDirname, and hasUnderscoreFilename fields instead of a simple boolean.


146-228: LGTM: Thorough edge-case coverage for dirname detection.

The test suite comprehensively validates the containsDirnameUsage function across various contexts:

  • Strings (all quote types)
  • Comments (single-line and multi-line)
  • Escaped quotes
  • Mixed contexts

259-434: LGTM: Run tests validate AST transformations correctly.

The tests verify:

  • Correct import and declaration injection
  • Proper ordering of declarations
  • Avoidance of duplicates
  • Inline snapshots ensure exact output matches expectations
code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.ts (2)

258-287: LGTM: Robust pattern detection with proper string/comment stripping.

The containsPatternUsage implementation correctly:

  • Strips strings using a comprehensive regex that handles all quote types and escape sequences
  • Removes block and line comments
  • Tests the pattern only on actual code

The approach prevents false positives from strings and comments.


289-303: LGTM: Clean API for banner configuration.

The BannerConfig interface and related utilities provide a clear contract for tracking what ESM compatibility features are needed. The bannerComment and hasRequireBanner are straightforward and well-defined.

code/lib/cli-storybook/src/automigrate/fixes/fix-faux-esm-require.ts (4)

19-65: LGTM: Comprehensive declaration detection.

The hasExistingDeclaration function correctly checks for existing declarations across:

  • Variable declarations (const/let/var)
  • Function declarations
  • Exported versions of both

This prevents duplicate declarations from being injected.


92-106: LGTM: Improved return type provides granular control.

Returning a BannerConfig object instead of a boolean allows the run method to selectively inject only the needed compatibility features, avoiding unnecessary imports and declarations.


120-210: LGTM: Careful AST manipulation with correct ordering.

The implementation correctly:

  • Adds only necessary imports
  • Inserts declarations after imports
  • Ensures __filename is declared before __dirname (since dirname() depends on it)
  • Calculates require insertion position accounting for previously added declarations
  • Avoids duplicates via hasExistingDeclaration checks

The insertion logic is well-thought-out and correct.


212-214: Banner comment placement is confirmed as intentional.

The code deliberately prepends the banner comment before all file content, including imports. The flow is: (1) AST modifications are written via updateMainConfig, (2) the modified file is read, (3) the banner is prepended using [bannerComment, content].join('\n'), and (4) the result is written to disk. Since the banner is a comment line, appearing before imports is a valid design choice for a migration notice.

code/lib/cli-storybook/src/codemod/csf-factories.ts (1)

80-84: Helpful option labels with clear examples.

The updated option labels with inline import examples make it very clear to users what each choice means. Good UX improvement.

code/frameworks/nextjs/src/utils.ts (1)

12-12: LGTM!

The semver import is correctly added to support the new version comparison utility.

code/frameworks/nextjs/src/config/webpack.ts (1)

6-8: LGTM!

The refactor correctly replaces the local version check with the new centralized isNextVersionGte utility, improving code maintainability and consistency.

code/frameworks/nextjs/src/preset.ts (1)

18-18: LGTM!

The import and usage of isNextVersionGte correctly centralizes the version comparison logic for the Next.js 16 check.

Also applies to: 54-54

code/package.json (1)

3-3: LGTM: Version bump to beta.13.

The version increment is consistent with the coordinated beta.13 release across the monorepo.

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

1-1: LGTM: Version constant updated.

The version export correctly reflects the beta.13 release.

docs/releases/migration-guide.mdx (1)

32-32: LGTM: Improved documentation clarity.

The updated wording accurately clarifies that both the main configuration and other presets must be valid ESM, not just the main file alone.

docs/writing-tests/integrations/stories-in-unit-tests.mdx (1)

83-91: LGTM: Helpful troubleshooting guidance added.

The new troubleshooting section provides clear guidance for Next.js Vite users encountering module resolution errors.

code/core/src/common/versions.ts (1)

3-44: LGTM: Coordinated version updates.

All package versions correctly updated to 10.0.0-beta.13 in this auto-generated file.

code/lib/cli-storybook/src/add.ts (1)

183-184: LGTM: Proper config reloading after addon addition.

Loading a fresh main config with skipCache: true ensures that syncStorybookAddons operates on the most recent configuration after the addon has been added to the file (line 178). This prevents stale config issues.

docs/_snippets/vite-includeStorybookNextjsPlugin.md (1)

1-9: LGTM: Clear configuration example.

The snippet provides a concise example for configuring the Storybook Next.js plugin in Vite, with a helpful comment explaining when it's needed.

code/lib/create-storybook/src/ink/steps/checks/vitestConfigFiles.test.ts (2)

16-42: LGTM: Mock configs for mergeConfig scenarios.

The new mock configurations appropriately test both simple mergeConfig usage and multi-argument mergeConfig scenarios, expanding test coverage for the vitest config validation logic.


189-203: LGTM: Comprehensive test coverage for mergeConfig.

The new test cases verify that the validation logic correctly handles mergeConfig usage patterns, including scenarios where defineConfig appears in different argument positions.

code/core/src/shared/utils/module.ts (3)

54-57: API change looks good.

Signature with { skipCache = false } is sensible and backwards compatible.


75-77: Nice: clear require cache when skipping cache.

Prevents stale loads in CJS fallback.


67-70: No issues found—query parameter cache-busting is supported on file:// URLs.

Node's ESM loader accepts query parameters on file:// URLs across Node 18, 20, and 22, with modules loaded separately when query or fragment parameters differ. The ?${Date.now()} cache-buster at lines 67–70 is a supported and effective pattern.

code/core/src/manager-api/tests/stories.test.ts (1)

1109-1147: Nice coverage for filtered child selection

Test correctly asserts fallback to first visible child when the first is hidden.

code/addons/vitest/src/updateVitestFile.ts (1)

190-296: mergeConfig handling looks solid; good precedence for test-bearing defineConfig

The logic to locate defineConfig args, prefer the one with test, and relocate existing test into workspace/projects before merging is clear.

Please add a small fixture covering a mergeConfig(vite, defineConfig(objA), defineConfig(objB)) where neither has test to assert we still inject the Storybook project under test.

code/addons/vitest/src/updateVitestFile.test.ts (1)

7-9: Good move to diff-based assertions with getDiff

Switching to getDiff makes the tests resilient and focused. LGTM.

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

428-437: Safeguard settings spread in gotoStory

settings may be undefined; spreading it can throw. Use a default.

- store.setState({ settings: { ...settings, lastTrackedStoryId: entry.id } });
+ const prev = settings || {};
+ store.setState({ settings: { ...prev, lastTrackedStoryId: entry.id } });

Likely an incorrect or invalid review comment.

Comment on lines 136 to 138
case ProjectType.REACT_NATIVE_AND_RNW:
case ProjectType.REACT_NATIVE_WEB:
return CoreBuilder.Vite;
Copy link
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: This change is unrelated to the PR objectives.

This file modifies CLI builder detection logic for React Native projects, but the PR is titled "Bug: Do not display non-existing shortcuts in the settings" and the objectives describe fixing keyboard shortcuts display in the settings UI. The enriched AI summary confirms this adds a React Native + RNW → Vite builder mapping.

While the code change itself is technically correct (adding ProjectType.REACT_NATIVE_AND_RNW case with appropriate fallthrough to CoreBuilder.Vite), it appears to be completely unrelated to keyboard shortcuts.

Please clarify:

  • Why is this builder detection change included in a keyboard shortcuts PR?
  • Should this be in a separate PR?
  • Was this file included accidentally?
🤖 Prompt for AI Agents
In code/core/src/cli/detect.ts around lines 136-138, the added mapping of
ProjectType.REACT_NATIVE_AND_RNW / ProjectType.REACT_NATIVE_WEB to
CoreBuilder.Vite is unrelated to the keyboard-shortcuts bugfix PR; remove this
change from the current branch (revert these case additions) and either open a
separate PR containing the builder-detection change with its own
description/tests/docs or, if it must remain here, document why it belongs in
this PR by updating the PR description and adding a brief rationale and tests
demonstrating the new mapping.

Comment on lines 454 to 457
if (!gotoStory(entry)) {
// If the entry is not a story or docs, find the first descendant entry that is
gotoStory(api.findLeafEntry(filteredHash, entry.id));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Selecting first visible child should handle “no visible children”

If a component has zero visible children in the current filter, findLeafEntry(filteredHash, entry.id) can fail. Pair with a safe findLeafEntry (see below) to avoid errors and just no-op when nothing is visible.

🤖 Prompt for AI Agents
In code/core/src/manager-api/modules/stories.ts around lines 454 to 457, calling
gotoStory(api.findLeafEntry(filteredHash, entry.id)) can throw when
findLeafEntry returns nothing for components with zero visible children; change
this to call findLeafEntry once into a variable, check it for truthiness, and
only call gotoStory if a valid leaf was returned (no-op otherwise), and ensure
findLeafEntry itself returns null/undefined when no descendant is visible.

Comment on lines 28 to 32
export const isNextVersionGte = (version: string): boolean => {
const currentVersion = getNextjsVersion();
const coercedVersion = semver.coerce(currentVersion);
return coercedVersion ? semver.gte(coercedVersion, version) : false;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Document the public utility and verify edge case handling.

The function correctly uses semver.coerce to normalize the current Next.js version before comparison. However:

  1. Add JSDoc documentation explaining the purpose, parameters, and return behavior (especially that it returns false when the version cannot be parsed).
  2. Consider logging a warning when coerce returns null, as this indicates a version detection issue that could cause unexpected behavior downstream.

Apply this diff to add documentation:

+/**
+ * Checks if the current Next.js version is greater than or equal to the specified version.
+ * @param version - The version string to compare against (e.g., '16.0.0')
+ * @returns true if current version >= specified version, false if less than or if version cannot be determined
+ */
 export const isNextVersionGte = (version: string): boolean => {
   const currentVersion = getNextjsVersion();
   const coercedVersion = semver.coerce(currentVersion);
   return coercedVersion ? semver.gte(coercedVersion, version) : false;
 };

Verify edge cases with this script:


🏁 Script executed:

#!/bin/bash
# Description: Check for any unusual Next.js version formats in package.json that might fail coercion

echo "Checking Next.js version format..."
fd -t f "package.json" --exec jq -r 'select(.dependencies.next != null or .devDependencies.next != null) | .dependencies.next // .devDependencies.next' | head -5

Length of output: 237


Now let me search the codebase for the actual implementation:


🏁 Script executed:

#!/bin/bash
# Find getNextjsVersion function implementation
rg -n "getNextjsVersion" --type ts --type tsx -A 5 -B 2

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Find isNextVersionGte function usage
rg -n "isNextVersionGte" --type ts --type tsx -B 2 -A 2

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Check the full utils.ts file to see imports and structure
cat code/frameworks/nextjs/src/utils.ts

Length of output: 5608


🏁 Script executed:

#!/bin/bash
# Find where isNextVersionGte is called
rg -n "isNextVersionGte" --type ts -B 3 -A 3

Length of output: 2599


🏁 Script executed:

#!/bin/bash
# Check if the file has any existing documentation for exported functions
rg -n "^\s*\*\/" --type ts code/frameworks/nextjs/src/utils.ts | head -10

Length of output: 80


Add JSDoc documentation to the exported utility function.

The function correctly handles edge cases by returning false when version coercion fails. However, add JSDoc to document the purpose, parameters, and return behavior—consistent with other exported utilities in this file like scopedResolve and getNodeModulesExcludeRegex.

+/**
+ * Checks if the current Next.js version is greater than or equal to the specified version.
+ * @param version - The version string to compare against (e.g., '16.0.0')
+ * @returns true if current version >= specified version, false if less than or version cannot be parsed
+ */
 export const isNextVersionGte = (version: string): boolean => {
   const currentVersion = getNextjsVersion();
   const coercedVersion = semver.coerce(currentVersion);
   return coercedVersion ? semver.gte(coercedVersion, version) : false;
 };

Note: The null coercion scenario is unlikely in practice since getNextjsVersion() reads from Next.js's package.json which should contain valid semver. The graceful fallback to false is appropriate.

📝 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
export const isNextVersionGte = (version: string): boolean => {
const currentVersion = getNextjsVersion();
const coercedVersion = semver.coerce(currentVersion);
return coercedVersion ? semver.gte(coercedVersion, version) : false;
};
/**
* Checks if the current Next.js version is greater than or equal to the specified version.
* @param version - The version string to compare against (e.g., '16.0.0')
* @returns true if current version >= specified version, false if less than or version cannot be parsed
*/
export const isNextVersionGte = (version: string): boolean => {
const currentVersion = getNextjsVersion();
const coercedVersion = semver.coerce(currentVersion);
return coercedVersion ? semver.gte(coercedVersion, version) : false;
};
🤖 Prompt for AI Agents
In code/frameworks/nextjs/src/utils.ts around lines 28 to 32, add a JSDoc block
above the exported isNextVersionGte function that documents the function's
purpose (checks whether the current Next.js version is greater than or equal to
a provided semver), the parameter (version: string — the semver string to
compare against), and the return value (boolean — true if current Next.js
version coerced via semver is >= the provided version; false if coercion fails
or the comparison is not satisfied). Also mention the edge-case behavior in the
JSDoc: that coercion may return null and in that case the function returns false
as a graceful fallback.

Comment on lines 125 to 250
describe('promptForAutomigrations', () => {
it('should call multiselect with required: false', async () => {
const { prompt } = await import('storybook/internal/node-logger');
const multiselectMock = vi.mocked(prompt.multiselect);
multiselectMock.mockResolvedValue(['fix1']);

const fix1 = createMockFix('fix1', { needsFix: true });
const project1 = createMockProject('/project1/.storybook');

const automigrations = [
{
fix: fix1,
reports: [
{
result: { needsFix: true },
status: 'check_succeeded' as const,
project: project1,
},
],
},
];

await promptForAutomigrations(automigrations, { dryRun: false, yes: false });

expect(multiselectMock).toHaveBeenCalledWith(
expect.objectContaining({
message: 'Select automigrations to run',
required: false,
})
);
});

it('should return empty array when user selects nothing', async () => {
const { prompt } = await import('storybook/internal/node-logger');
const multiselectMock = vi.mocked(prompt.multiselect);
multiselectMock.mockResolvedValue([]);

const fix1 = createMockFix('fix1', { needsFix: true });
const project1 = createMockProject('/project1/.storybook');

const automigrations = [
{
fix: fix1,
reports: [
{
result: { needsFix: true },
status: 'check_succeeded' as const,
project: project1,
},
],
},
];

const result = await promptForAutomigrations(automigrations, {
dryRun: false,
yes: false,
});

expect(result).toEqual([]);
});

it('should return all automigrations when yes option is true', async () => {
const { logger } = await import('storybook/internal/node-logger');
const logSpy = vi.spyOn(logger, 'log');

const fix1 = createMockFix('fix1', { needsFix: true });
const fix2 = createMockFix('fix2', { needsFix: true });
const project1 = createMockProject('/project1/.storybook');

const automigrations = [
{
fix: fix1,
reports: [
{
result: { needsFix: true },
status: 'check_succeeded' as const,
project: project1,
},
],
},
{
fix: fix2,
reports: [
{
result: { needsFix: true },
status: 'check_succeeded' as const,
project: project1,
},
],
},
];

const result = await promptForAutomigrations(automigrations, { dryRun: false, yes: true });

expect(result).toEqual(automigrations);
expect(logSpy).toHaveBeenCalledWith('Running all detected automigrations:');
});

it('should return empty array when dryRun is true', async () => {
const { logger } = await import('storybook/internal/node-logger');
const logSpy = vi.spyOn(logger, 'log');

const fix1 = createMockFix('fix1', { needsFix: true });
const project1 = createMockProject('/project1/.storybook');

const automigrations = [
{
fix: fix1,
reports: [
{
result: { needsFix: true },
status: 'check_succeeded' as const,
project: project1,
},
],
},
];

const result = await promptForAutomigrations(automigrations, { dryRun: true, yes: false });

expect(result).toEqual([]);
expect(logSpy).toHaveBeenCalledWith(
'Detected automigrations (dry run - no changes will be made):'
);
});
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Move mock implementations to beforeEach blocks.

Per coding guidelines, mock implementations should be placed in beforeEach blocks rather than inline within test cases. This ensures consistent mock setup and makes tests more maintainable.

Based on coding guidelines.

Apply this pattern:

 describe('promptForAutomigrations', () => {
+  let multiselectMock: any;
+  let logSpy: any;
+
+  beforeEach(async () => {
+    const { prompt, logger } = await import('storybook/internal/node-logger');
+    multiselectMock = vi.mocked(prompt.multiselect);
+    logSpy = vi.spyOn(logger, 'log');
+  });
+
   it('should call multiselect with required: false', async () => {
-    const { prompt } = await import('storybook/internal/node-logger');
-    const multiselectMock = vi.mocked(prompt.multiselect);
     multiselectMock.mockResolvedValue(['fix1']);
     // ... rest of test
   });
📝 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
describe('promptForAutomigrations', () => {
it('should call multiselect with required: false', async () => {
const { prompt } = await import('storybook/internal/node-logger');
const multiselectMock = vi.mocked(prompt.multiselect);
multiselectMock.mockResolvedValue(['fix1']);
const fix1 = createMockFix('fix1', { needsFix: true });
const project1 = createMockProject('/project1/.storybook');
const automigrations = [
{
fix: fix1,
reports: [
{
result: { needsFix: true },
status: 'check_succeeded' as const,
project: project1,
},
],
},
];
await promptForAutomigrations(automigrations, { dryRun: false, yes: false });
expect(multiselectMock).toHaveBeenCalledWith(
expect.objectContaining({
message: 'Select automigrations to run',
required: false,
})
);
});
it('should return empty array when user selects nothing', async () => {
const { prompt } = await import('storybook/internal/node-logger');
const multiselectMock = vi.mocked(prompt.multiselect);
multiselectMock.mockResolvedValue([]);
const fix1 = createMockFix('fix1', { needsFix: true });
const project1 = createMockProject('/project1/.storybook');
const automigrations = [
{
fix: fix1,
reports: [
{
result: { needsFix: true },
status: 'check_succeeded' as const,
project: project1,
},
],
},
];
const result = await promptForAutomigrations(automigrations, {
dryRun: false,
yes: false,
});
expect(result).toEqual([]);
});
it('should return all automigrations when yes option is true', async () => {
const { logger } = await import('storybook/internal/node-logger');
const logSpy = vi.spyOn(logger, 'log');
const fix1 = createMockFix('fix1', { needsFix: true });
const fix2 = createMockFix('fix2', { needsFix: true });
const project1 = createMockProject('/project1/.storybook');
const automigrations = [
{
fix: fix1,
reports: [
{
result: { needsFix: true },
status: 'check_succeeded' as const,
project: project1,
},
],
},
{
fix: fix2,
reports: [
{
result: { needsFix: true },
status: 'check_succeeded' as const,
project: project1,
},
],
},
];
const result = await promptForAutomigrations(automigrations, { dryRun: false, yes: true });
expect(result).toEqual(automigrations);
expect(logSpy).toHaveBeenCalledWith('Running all detected automigrations:');
});
it('should return empty array when dryRun is true', async () => {
const { logger } = await import('storybook/internal/node-logger');
const logSpy = vi.spyOn(logger, 'log');
const fix1 = createMockFix('fix1', { needsFix: true });
const project1 = createMockProject('/project1/.storybook');
const automigrations = [
{
fix: fix1,
reports: [
{
result: { needsFix: true },
status: 'check_succeeded' as const,
project: project1,
},
],
},
];
const result = await promptForAutomigrations(automigrations, { dryRun: true, yes: false });
expect(result).toEqual([]);
expect(logSpy).toHaveBeenCalledWith(
'Detected automigrations (dry run - no changes will be made):'
);
});
});
describe('promptForAutomigrations', () => {
let multiselectMock: any;
let logSpy: any;
beforeEach(async () => {
const { prompt, logger } = await import('storybook/internal/node-logger');
multiselectMock = vi.mocked(prompt.multiselect);
logSpy = vi.spyOn(logger, 'log');
});
it('should call multiselect with required: false', async () => {
multiselectMock.mockResolvedValue(['fix1']);
const fix1 = createMockFix('fix1', { needsFix: true });
const project1 = createMockProject('/project1/.storybook');
const automigrations = [
{
fix: fix1,
reports: [
{
result: { needsFix: true },
status: 'check_succeeded' as const,
project: project1,
},
],
},
];
await promptForAutomigrations(automigrations, { dryRun: false, yes: false });
expect(multiselectMock).toHaveBeenCalledWith(
expect.objectContaining({
message: 'Select automigrations to run',
required: false,
})
);
});
it('should return empty array when user selects nothing', async () => {
multiselectMock.mockResolvedValue([]);
const fix1 = createMockFix('fix1', { needsFix: true });
const project1 = createMockProject('/project1/.storybook');
const automigrations = [
{
fix: fix1,
reports: [
{
result: { needsFix: true },
status: 'check_succeeded' as const,
project: project1,
},
],
},
];
const result = await promptForAutomigrations(automigrations, {
dryRun: false,
yes: false,
});
expect(result).toEqual([]);
});
it('should return all automigrations when yes option is true', async () => {
const fix1 = createMockFix('fix1', { needsFix: true });
const fix2 = createMockFix('fix2', { needsFix: true });
const project1 = createMockProject('/project1/.storybook');
const automigrations = [
{
fix: fix1,
reports: [
{
result: { needsFix: true },
status: 'check_succeeded' as const,
project: project1,
},
],
},
{
fix: fix2,
reports: [
{
result: { needsFix: true },
status: 'check_succeeded' as const,
project: project1,
},
],
},
];
const result = await promptForAutomigrations(automigrations, { dryRun: false, yes: true });
expect(result).toEqual(automigrations);
expect(logSpy).toHaveBeenCalledWith('Running all detected automigrations:');
});
it('should return empty array when dryRun is true', async () => {
const fix1 = createMockFix('fix1', { needsFix: true });
const project1 = createMockProject('/project1/.storybook');
const automigrations = [
{
fix: fix1,
reports: [
{
result: { needsFix: true },
status: 'check_succeeded' as const,
project: project1,
},
],
},
];
const result = await promptForAutomigrations(automigrations, { dryRun: true, yes: false });
expect(result).toEqual([]);
expect(logSpy).toHaveBeenCalledWith(
'Detected automigrations (dry run - no changes will be made):'
);
});
});
🤖 Prompt for AI Agents
In code/lib/cli-storybook/src/automigrate/multi-project.test.ts around lines
125-250, the tests define mock implementations (e.g.,
vi.mocked(prompt.multiselect).mockResolvedValue and vi.spyOn(logger, 'log'))
inline in each it block; move those mock setups into a shared beforeEach block
so all tests get a consistent mock environment. Create a top-level
describe('promptForAutomigrations') beforeEach that imports prompt and logger,
creates the multiselectMock and logSpy (and resets/clears mocks between tests),
and sets default mockResolvedValue/spy behavior; remove the in-test mock
implementations and only override specific behavior inside individual tests when
needed. Ensure to call vi.clearAllMocks() or reset mocks in an
afterEach/beforeEach to avoid cross-test leakage.

Comment on lines 547 to 548
This change aligns with Node.js ESM requirements, where relative imports must specify the full file extension. This applies to `.storybook/main.js` and any custom preset files. While TypeScript-based files (`.storybook/main.ts`) will continue to work with extensionless imports for now through automatic resolution, we recommend migrating to explicit extensions for consistency and better compatibility.

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

TypeScript still requires explicit extensions in NodeNext/Bundler mode

TypeScript configs that follow our earlier guidance (moduleResolution: "bundler" or "nodenext") no longer allow importing ./my-file without the .js suffix; the compiler raises TS2691 unless you keep the legacy node resolver or flip on allowImportingTsExtensions. Telling readers that .storybook/main.ts “will continue to work with extensionless imports” is misleading and will regress anyone who updated their tsconfig per the preceding section. Please adjust the wording (or call out the exact resolver setting that keeps this working) so the docs don’t direct people toward a configuration that TypeScript immediately rejects.

🤖 Prompt for AI Agents
In MIGRATION.md around lines 547 to 548, the guidance incorrectly implies
.storybook/main.ts will continue to work with extensionless imports under
NodeNext/Bundler—this is misleading because TypeScript with moduleResolution
"bundler" or "nodenext" requires explicit .js/.ts extensions (TS2691) unless the
legacy "node" resolver or allowImportingTsExtensions is enabled; update the
wording to state that extensionless imports are rejected by those resolvers and
either recommend adding explicit extensions or explicitly call out the exact
resolver setting (or allowImportingTsExtensions flag) required to keep
extensionless imports working.

@JReinhold JReinhold deleted the branch storybookjs:next October 23, 2025 12:14
@JReinhold JReinhold closed this Oct 23, 2025
@Sidnioulz Sidnioulz reopened this Oct 25, 2025
@Sidnioulz Sidnioulz changed the base branch from 10.1 to 10.1-with-canary-release October 25, 2025 17:12
@storybook-app-bot
Copy link

storybook-app-bot bot commented Oct 25, 2025

Package Benchmarks

Commit: 78ca2f1, ran on 3 November 2025 at 09:31:59 UTC

No significant changes detected, all good. 👏

@Sidnioulz Sidnioulz changed the base branch from 10.1-with-canary-release to next November 3, 2025 09:17
@Sidnioulz Sidnioulz changed the title Bug: Do not display non-existing shortcuts in the settings [age Bug: Do not display non-existing shortcuts in the settings page Nov 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Keyboard shortcuts are missing labels on page refresh

4 participants