Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
b245ab5
Update stories-in-unit-tests.mdx
cabbiepete Jul 11, 2025
3875be1
Add code example for including the next js plugin in vitest
cabbiepete Jul 11, 2025
a9c2296
Update vite-includeStorybookNextjsPlugin.md
yannbf Jul 24, 2025
29221ab
Merge branch 'next' into patch-3
yannbf Jul 24, 2025
7246e24
Merge branch 'next' into patch-3
jonniebigodes Jul 26, 2025
275afb8
Add a correct color contrast
JatinMehta007 Sep 22, 2025
ebbcdbf
Merge pull request #1 from JatinMehta007/color-contrast
JatinMehta007 Sep 23, 2025
bc1e792
Initial plan
Copilot Oct 1, 2025
bad12a6
Fix multiselect prompt to allow proceeding without selecting automigr…
Copilot Oct 1, 2025
b88a73d
Add tests for promptForAutomigrations with required: false
Copilot Oct 1, 2025
15125f7
Initial plan
Copilot Oct 2, 2025
5c52a0d
Fix csf-factories automigration: update links and make relative impor…
Copilot Oct 2, 2025
08cc64c
Apply review feedback: add ref=upgrade query params and invert boolea…
Copilot Oct 2, 2025
7f79e3a
Update csf-factories text and options per review feedback
Copilot Oct 3, 2025
024e17e
Merge branch 'storybookjs:next' into next
JatinMehta007 Oct 7, 2025
7d1679f
Update documentation link anchor in TagsFilterPanel
JatinMehta007 Oct 7, 2025
3d8fbb8
Prevent navigating to hidden (filtered) item unless explicitly target…
ghengeveld Oct 13, 2025
b1b26aa
CLI: Avoid mixed CSF in files with unconventional stories
yannbf Oct 13, 2025
bcc2766
Merge branch 'next' into copilot/fix-ce491ec7-393c-48b2-9521-681fa893…
ndelangen Oct 13, 2025
79fb14f
Merge branch 'next' into fix/TagsFilterPanel
valentinpalkovic Oct 13, 2025
988ea6d
Update code/core/src/theming/convert.ts
valentinpalkovic Oct 13, 2025
feb2808
Update code/core/src/theming/convert.ts
valentinpalkovic Oct 13, 2025
574e4a7
Merge branch 'next' into fix/TagsFilterPanel
jonniebigodes Oct 13, 2025
1322e17
Fix types
ghengeveld Oct 14, 2025
9d8bead
Merge branch 'next' into fix/TagsFilterPanel
jonniebigodes Oct 14, 2025
4e4c61b
Merge branch 'next' into fix/TagsFilterPanel
jonniebigodes Oct 14, 2025
4894b51
Merge branch 'next' into patch-3
jonniebigodes Oct 14, 2025
98bd4d4
Update docs/_snippets/vite-includeStorybookNextjsPlugin.md
jonniebigodes Oct 14, 2025
fd467e3
Update docs/writing-tests/integrations/stories-in-unit-tests.mdx
jonniebigodes Oct 14, 2025
fbc89a6
Update docs/writing-tests/integrations/stories-in-unit-tests.mdx
jonniebigodes Oct 14, 2025
8c0d558
Merge pull request #32014 from cabbiepete/patch-3
jonniebigodes Oct 14, 2025
15bdc1d
Merge pull request #32668 from JatinMehta007/fix/TagsFilterPanel
jonniebigodes Oct 14, 2025
d594c3d
Merge branch 'next' into prevent-nav-to-hidden-item
yannbf Oct 15, 2025
4bc605b
Merge branch 'next' into copilot/fix-ce491ec7-393c-48b2-9521-681fa893…
yannbf Oct 15, 2025
11e49d4
Enhance module import functionality by adding skipCache option to imp…
yannbf Oct 15, 2025
ee0c7a3
Merge pull request #32716 from storybookjs/yann/bail-cf-factories-cod…
yannbf Oct 15, 2025
59e030b
move skipCache up
yannbf Oct 15, 2025
8d9ee89
Merge branch 'next' into copilot/fix-f2bf7ddc-5b58-455d-a384-3b659904…
yannbf Oct 15, 2025
d9d66e5
fixes
yannbf Oct 15, 2025
d944892
Merge pull request #32597 from storybookjs/copilot/fix-ce491ec7-393c-…
yannbf Oct 15, 2025
5b71a26
fix tests
yannbf Oct 15, 2025
5065e09
Merge branch 'next' into copilot/fix-f2bf7ddc-5b58-455d-a384-3b659904…
yannbf Oct 15, 2025
c8bbfad
Merge pull request #32728 from storybookjs/yann/fix-sb-add-csf-sync
yannbf Oct 15, 2025
e7b6005
Merge pull request #32610 from storybookjs/copilot/fix-f2bf7ddc-5b58-…
yannbf Oct 15, 2025
a115899
Merge pull request #32715 from storybookjs/prevent-nav-to-hidden-item
yannbf Oct 15, 2025
6a56090
Write changelog for 10.0.0-beta.12 [skip ci]
storybook-bot Oct 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.prerelease.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## 10.0.0-beta.12

- CLI: Avoid mixed CSF in files with unconventional stories - [#32716](https://github.com/storybookjs/storybook/pull/32716), thanks @yannbf!
- CLI: Fix CSF factories addon syncing in storybook add command - [#32728](https://github.com/storybookjs/storybook/pull/32728), thanks @yannbf!
- CLI: Make relative imports default in csf-factories codemod - [#32610](https://github.com/storybookjs/storybook/pull/32610), thanks @copilot-swe-agent!
- Core: Prevent navigating to hidden (filtered) item - [#32715](https://github.com/storybookjs/storybook/pull/32715), thanks @ghengeveld!
- Fix: Allow proceeding without selecting automigrations in upgrade command - [#32597](https://github.com/storybookjs/storybook/pull/32597), thanks @copilot-swe-agent!

## 10.0.0-beta.11

- Automigration: Improve the viewport/backgrounds automigration - [#32619](https://github.com/storybookjs/storybook/pull/32619), thanks @valentinpalkovic!
Expand Down
4 changes: 3 additions & 1 deletion code/core/src/common/utils/load-main-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,18 @@ import { validateConfigurationFiles } from './validate-configuration-files';
export async function loadMainConfig({
configDir = '.storybook',
cwd,
skipCache,
}: {
configDir: string;
cwd?: string;
skipCache?: boolean;
}): Promise<StorybookConfig> {
await validateConfigurationFiles(configDir, cwd);

const mainPath = getInterpretedFile(resolve(configDir, 'main')) as string;

try {
const out = await importModule(mainPath);
const out = await importModule(mainPath, { skipCache });
return out;
} catch (e) {
if (!(e instanceof Error)) {
Expand Down
53 changes: 27 additions & 26 deletions code/core/src/manager-api/modules/stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,15 +368,15 @@ export const init: ModuleFn<SubAPI, SubState> = ({
return parameters || undefined;
},
jumpToComponent: (direction) => {
const { index, storyId, refs, refId } = store.getState();
const { filteredIndex, storyId, refs, refId } = store.getState();
const story = api.getData(storyId, refId);

// cannot navigate when there's no current selection
if (!story) {
return;
}

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

if (!hash) {
return;
Expand All @@ -389,15 +389,15 @@ export const init: ModuleFn<SubAPI, SubState> = ({
}
},
jumpToStory: (direction) => {
const { index, storyId, refs, refId } = store.getState();
const { filteredIndex, storyId, refs, refId } = store.getState();
const story = api.getData(storyId, refId);

// cannot navigate when there's no current selection
if (!story) {
return;
}

const hash = story.refId ? refs[story.refId].index : index;
const hash = story.refId ? refs[story.refId].filteredIndex : filteredIndex;

if (!hash) {
return;
Expand Down Expand Up @@ -425,49 +425,50 @@ export const init: ModuleFn<SubAPI, SubState> = ({
},
selectStory: (titleOrId = undefined, name = undefined, options = {}) => {
const { ref } = options;
const { storyId, index, refs } = store.getState();
const { storyId, index, filteredIndex, refs, settings } = store.getState();

const hash = ref ? refs[ref].index : index;
const gotoStory = (entry?: API_HashEntry) => {
if (entry?.type === 'docs' || entry?.type === 'story') {
store.setState({ settings: { ...settings, lastTrackedStoryId: entry.id } });
navigate(`/${entry.type}/${entry.refId ? `${entry.refId}_${entry.id}` : entry.id}`);
return true;
}
return false;
};

const kindSlug = storyId?.split('--', 2)[0];

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

if (!name) {
// Find the entry (group, component or story) that is referred to
// Find the entry (group, component, story or docs) that is referred to
const entry = titleOrId ? hash[titleOrId] || hash[sanitize(titleOrId)] : hash[kindSlug];

if (!entry) {
throw new Error(`Unknown id or title: '${titleOrId}'`);
}

store.setState({
settings: { ...store.getState().settings, lastTrackedStoryId: entry.id },
});

// We want to navigate to the first ancestor entry that is a leaf
const leafEntry = api.findLeafEntry(hash, entry.id);
const fullId = leafEntry.refId ? `${leafEntry.refId}_${leafEntry.id}` : leafEntry.id;
navigate(`/${leafEntry.type}/${fullId}`);
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));
}
} else if (!titleOrId) {
// Navigate to a named story/docs within the current component (i.e. "kind")
// This is a slugified version of the kind, but that's OK, our toId function is idempotent
const id = toId(kindSlug, name);

api.selectStory(id, undefined, options);
gotoStory(hash[toId(kindSlug, name)]);
} else {
const id = ref ? `${ref}_${toId(titleOrId, name)}` : toId(titleOrId, name);
if (hash[id]) {
api.selectStory(id, undefined, options);
gotoStory(hash[id]);
} else {
// Support legacy API with component permalinks, where kind is `x/y` but permalink is 'z'
const entry = hash[sanitize(titleOrId)];
if (entry?.type === 'component') {
const foundId = entry.children.find((childId: any) => hash[childId].name === name);
if (foundId) {
api.selectStory(foundId, undefined, options);
}
const foundId = entry.children.find((childId) => hash[childId].name === name);
gotoStory(foundId ? hash[foundId] : undefined);
}
}
}
Expand All @@ -478,7 +479,7 @@ export const init: ModuleFn<SubAPI, SubState> = ({
return entry;
}

const childStoryId = entry.children[0];
const childStoryId = entry.children.find((childId) => index[childId]) || entry.children[0];
return api.findLeafEntry(index, childStoryId);
},
findLeafStoryId(index, storyId) {
Expand Down
39 changes: 39 additions & 0 deletions code/core/src/manager-api/tests/stories.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,45 @@ describe('stories API', () => {
api.selectStory('a--1');
expect(store.getState().settings.lastTrackedStoryId).toBe('a--1');
});
it('selects first visible child when component is clicked with filtered index', () => {
const initialState = { path: '/story/a--1', storyId: 'a--1', viewMode: 'story' };
const moduleArgs = createMockModuleArgs({ initialState });
const { api } = initStories(moduleArgs as unknown as ModuleArgs);
const { navigate, store } = moduleArgs;

// Set index with stories
api.setIndex({ v: 5, entries: navigationEntries });

// Set up filtered index where first child (a--1) is hidden
const filteredIndex = {
a: {
id: 'a',
type: 'component' as const,
name: 'a',
depth: 0,
tags: [],
children: ['a--1', 'a--2'],
importPath: './a.ts',
},
'a--2': {
...navigationEntries['a--2'],
type: 'story' as const,
subtype: 'story' as const,
parent: 'a',
depth: 1,
tags: [],
prepared: false,
exportName: '2',
},
// Note: 'a--1' is missing from filtered index (hidden)
};

store.setState({ filteredIndex });

// When selecting the component, it should select the first visible child (a--2)
api.selectStory('a');
expect(navigate).toHaveBeenCalledWith('/story/a--2');
});
describe('deprecated api', () => {
it('allows navigating to a combination of title + name', () => {
const initialState = { path: '/story/a--1', storyId: 'a--1', viewMode: 'story' };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ export const TagsFilterPanel = ({
title: 'Learn how to add tags',
icon: <DocumentIcon />,
right: <ShareAltIcon />,
href: api.getDocsUrl({ subpath: 'writing-stories/tags#filtering-by-custom-tags' }),
href: api.getDocsUrl({ subpath: 'writing-stories/tags#custom-tags' }),
},
]);
}
Expand Down
5 changes: 4 additions & 1 deletion code/core/src/node-logger/prompts/prompt-provider-clack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ export class ClackPromptProvider extends PromptProvider {
options: MultiSelectPromptOptions<T>,
promptOptions?: PromptOptions
): Promise<T[]> {
const result = await clack.multiselect<T>(options);
const result = await clack.multiselect<T>({
...options,
required: options.required,
});
this.handleCancel(result, promptOptions);
logTracker.addLog('prompt', options.message, { choice: result });
return result as T[];
Expand Down
12 changes: 10 additions & 2 deletions code/core/src/shared/utils/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ let isTypescriptLoaderRegistered = false;
* // Returns the default export or the entire module
* ```
*/
export async function importModule(path: string) {
export async function importModule(
path: string,
{ skipCache = false }: { skipCache?: boolean } = {}
) {
if (!isTypescriptLoaderRegistered) {
const typescriptLoaderUrl = importMetaResolve('storybook/internal/bin/loader');
register(typescriptLoaderUrl, import.meta.url);
Expand All @@ -61,12 +64,17 @@ export async function importModule(path: string) {
let mod;
try {
const resolvedPath = win32.isAbsolute(path) ? pathToFileURL(path).href : path;
mod = await import(resolvedPath);
// When applicable, add a hash to the import URL to bypass cache
const importUrl = skipCache ? `${resolvedPath}?${Date.now()}` : resolvedPath;
mod = await import(importUrl);
} catch (importError) {
try {
// fallback to require to support older behavior
// this is relevant for presets that are only available with the "require" condition in a package's export map
const require = createRequire(import.meta.url);
if (skipCache) {
delete require.cache[require.resolve(path)];
}
mod = require(path);
} catch (requireError) {
/*
Expand Down
7 changes: 3 additions & 4 deletions code/lib/cli-storybook/src/add.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { isAbsolute, join } from 'node:path';

import {
type PackageManagerName,
serverRequire,
loadMainConfig,
syncStorybookAddons,
versions,
} from 'storybook/internal/common';
Expand Down Expand Up @@ -182,7 +180,8 @@ export async function add(

// TODO: remove try/catch once CSF factories is shipped, for now gracefully handle any error
try {
await syncStorybookAddons(mainConfig, previewConfigPath!, configDir);
const newMainConfig = await loadMainConfig({ configDir, skipCache: true });
await syncStorybookAddons(newMainConfig, previewConfigPath!, configDir);
} catch (e) {
//
}
Expand Down
Loading