diff --git a/.circleci/config.yml b/.circleci/config.yml
index d6ef4a75091..ae9183fdf07 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -652,19 +652,19 @@ workflows:
requires:
- unit-tests
- create-sandboxes:
- parallelism: 20
+ parallelism: 19
requires:
- build
- build-sandboxes:
- parallelism: 20
+ parallelism: 19
requires:
- create-sandboxes
- chromatic-sandboxes:
- parallelism: 17
+ parallelism: 16
requires:
- build-sandboxes
- e2e-production:
- parallelism: 15
+ parallelism: 14
requires:
- build-sandboxes
- e2e-dev:
@@ -672,7 +672,7 @@ workflows:
requires:
- create-sandboxes
- test-runner-production:
- parallelism: 15
+ parallelism: 14
requires:
- build-sandboxes
- bench:
@@ -706,22 +706,22 @@ workflows:
requires:
- build
- create-sandboxes:
- parallelism: 33
+ parallelism: 31
requires:
- build
# - smoke-test-sandboxes: # disabled for now
# requires:
# - create-sandboxes
- build-sandboxes:
- parallelism: 33
+ parallelism: 31
requires:
- create-sandboxes
- chromatic-sandboxes:
- parallelism: 30
+ parallelism: 28
requires:
- build-sandboxes
- e2e-production:
- parallelism: 28
+ parallelism: 26
requires:
- build-sandboxes
- e2e-dev:
@@ -729,7 +729,7 @@ workflows:
requires:
- create-sandboxes
- test-runner-production:
- parallelism: 28
+ parallelism: 26
requires:
- build-sandboxes
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5ba60ca1636..24af8968f63 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 7.6.5
+
+- Angular: Update Angular cli templates - [#25152](https://github.com/storybookjs/storybook/pull/25152), thanks [@Marklb](https://github.com/Marklb)!
+- Blocks: Fix Subtitle block for unattached docs pages - [#25157](https://github.com/storybookjs/storybook/pull/25157), thanks [@kripod](https://github.com/kripod)!
+- SvelteKit: Fix missing `$app` modules - [#25132](https://github.com/storybookjs/storybook/pull/25132), thanks [@paoloricciuti](https://github.com/paoloricciuti)!
+
## 7.6.4
- Angular: Fix CSF Plugin - [#25098](https://github.com/storybookjs/storybook/pull/25098), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!
diff --git a/MIGRATION.md b/MIGRATION.md
index 78f0a0c5779..787e893df97 100644
--- a/MIGRATION.md
+++ b/MIGRATION.md
@@ -12,6 +12,7 @@
- [Icons is deprecated](#icons-is-deprecated)
- [React-docgen component analysis by default](#react-docgen-component-analysis-by-default)
- [Removed postinstall](#removed-postinstall)
+ - [Removed stories.json](#removed-storiesjson)
- [Framework-specific changes](#framework-specific-changes)
- [Angular: Drop support for Angular \< 15](#angular-drop-support-for-angular--15)
- [Next.js: Drop support for version \< 13.5](#nextjs-drop-support-for-version--135)
@@ -473,6 +474,12 @@ For more information see: https://storybook.js.org/docs/react/api/main-config-ty
We removed the `@storybook/postinstall` package, which provided some utilities for addons to programmatically modify user configuration files on install. This package was years out of date, so this should be a non-disruptive change. If your addon used the package, you can view the old source code [here](https://github.com/storybookjs/storybook/tree/release-7-5/code/lib/postinstall) and adapt it into your addon.
+#### Removed stories.json
+
+In addition to the built storybook, `storybook build` generates two files, `index.json` and `stories.json`, that list out the contents of the Storybook. `stories.json` is a legacy format and we included it for backwards compatibility. As of 8.0 we no longer build `stories.json` by default, and we will remove it completely in 9.0.
+
+In the meantime if you have code that relies on `stories.json`, you can find code that transforms the "v4" `index.json` to the "v3" `stories.json` format (and their respective TS types): https://github.com/storybookjs/storybook/blob/release-7-5/code/lib/core-server/src/utils/stories-json.ts#L71-L91
+
### Framework-specific changes
#### Angular: Drop support for Angular \< 15
diff --git a/code/addons/docs/src/preset.ts b/code/addons/docs/src/preset.ts
index de10b683efa..0877643bae9 100644
--- a/code/addons/docs/src/preset.ts
+++ b/code/addons/docs/src/preset.ts
@@ -238,7 +238,6 @@ export const viteFinal = async (config: any, options: Options) => {
enforce: 'pre',
config: () => ({
resolve: {
- // TODO: does this work if pre-existing alias is an array?
alias: {
react,
'react-dom': reactDom,
diff --git a/code/builders/builder-vite/src/optimizeDeps.ts b/code/builders/builder-vite/src/optimizeDeps.ts
index 725d625b1ec..1972fde8e8e 100644
--- a/code/builders/builder-vite/src/optimizeDeps.ts
+++ b/code/builders/builder-vite/src/optimizeDeps.ts
@@ -75,13 +75,13 @@ const INCLUDE_CANDIDATES = [
'prettier/parser-typescript',
'prop-types',
'qs',
- // 'react-dom', // TODO: de we really need to disable these?
- // 'react-dom/client',
+ 'react-dom',
+ 'react-dom/client',
'react-fast-compare',
'react-is',
'react-textarea-autosize',
- // 'react',
- // 'react/jsx-runtime',
+ 'react',
+ 'react/jsx-runtime',
'refractor/core',
'refractor/lang/bash.js',
'refractor/lang/css.js',
diff --git a/code/frameworks/sveltekit/src/plugins/mock-sveltekit-stores.ts b/code/frameworks/sveltekit/src/plugins/mock-sveltekit-stores.ts
index b5e63f8591e..f338760c390 100644
--- a/code/frameworks/sveltekit/src/plugins/mock-sveltekit-stores.ts
+++ b/code/frameworks/sveltekit/src/plugins/mock-sveltekit-stores.ts
@@ -7,7 +7,9 @@ export function mockSveltekitStores() {
config: () => ({
resolve: {
alias: {
- $app: resolve(__dirname, '../src/mocks/app/'),
+ '$app/forms': resolve(__dirname, '../src/mocks/app/forms.ts'),
+ '$app/navigation': resolve(__dirname, '../src/mocks/app/navigation.ts'),
+ '$app/stores': resolve(__dirname, '../src/mocks/app/stores.ts'),
},
},
}),
diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/Environment.svelte b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/Environment.svelte
new file mode 100644
index 00000000000..7cdc852f7fc
--- /dev/null
+++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/Environment.svelte
@@ -0,0 +1,8 @@
+
+
+
{browser}
+{dev}
+{building}
+{version}
diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/Paths.svelte b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/Paths.svelte
new file mode 100644
index 00000000000..f4c1e81b53f
--- /dev/null
+++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/Paths.svelte
@@ -0,0 +1,6 @@
+
+
+{assets}
+{base}
diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/environment.stories.js b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/environment.stories.js
new file mode 100644
index 00000000000..a8baaabbf5b
--- /dev/null
+++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/environment.stories.js
@@ -0,0 +1,10 @@
+import { expect, fn, within } from '@storybook/test';
+import Environment from './Environment.svelte';
+
+export default {
+ title: 'stories/sveltekit/modules/environment',
+ component: Environment,
+ tags: ['autodocs'],
+};
+
+export const Default = {};
diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/paths.stories.js b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/paths.stories.js
new file mode 100644
index 00000000000..5d87296483a
--- /dev/null
+++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-js/paths.stories.js
@@ -0,0 +1,10 @@
+import { expect, fn, within } from '@storybook/test';
+import Paths from './Paths.svelte';
+
+export default {
+ title: 'stories/sveltekit/modules/paths',
+ component: Paths,
+ tags: ['autodocs'],
+};
+
+export const Default = {};
diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/Environment.svelte b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/Environment.svelte
new file mode 100644
index 00000000000..7cdc852f7fc
--- /dev/null
+++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/Environment.svelte
@@ -0,0 +1,8 @@
+
+
+{browser}
+{dev}
+{building}
+{version}
diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/Paths.svelte b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/Paths.svelte
new file mode 100644
index 00000000000..f4c1e81b53f
--- /dev/null
+++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/Paths.svelte
@@ -0,0 +1,6 @@
+
+
+{assets}
+{base}
diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/environment.stories.js b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/environment.stories.js
new file mode 100644
index 00000000000..a8baaabbf5b
--- /dev/null
+++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/environment.stories.js
@@ -0,0 +1,10 @@
+import { expect, fn, within } from '@storybook/test';
+import Environment from './Environment.svelte';
+
+export default {
+ title: 'stories/sveltekit/modules/environment',
+ component: Environment,
+ tags: ['autodocs'],
+};
+
+export const Default = {};
diff --git a/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/paths.stories.js b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/paths.stories.js
new file mode 100644
index 00000000000..5d87296483a
--- /dev/null
+++ b/code/frameworks/sveltekit/template/stories_svelte-kit-skeleton-ts/paths.stories.js
@@ -0,0 +1,10 @@
+import { expect, fn, within } from '@storybook/test';
+import Paths from './Paths.svelte';
+
+export default {
+ title: 'stories/sveltekit/modules/paths',
+ component: Paths,
+ tags: ['autodocs'],
+};
+
+export const Default = {};
diff --git a/code/lib/cli/src/sandbox-templates.ts b/code/lib/cli/src/sandbox-templates.ts
index f2d4950bf2d..491c26ace8c 100644
--- a/code/lib/cli/src/sandbox-templates.ts
+++ b/code/lib/cli/src/sandbox-templates.ts
@@ -416,28 +416,6 @@ const baseTemplates = {
// Remove smoke-test from the list once https://github.com/storybookjs/storybook/issues/19351 is fixed.
skipTasks: ['smoke-test', 'e2e-tests-dev', 'bench'],
},
- 'preact-webpack5/default-js': {
- name: 'Preact CLI Latest (Webpack | JavaScript)',
- script:
- 'npx preact-cli create default {{beforeDir}} --name preact-app --yarn --no-install && cd {{beforeDir}} && echo "module.exports = {}" > webpack.config.js',
- expected: {
- framework: '@storybook/preact-webpack5',
- renderer: '@storybook/preact',
- builder: '@storybook/builder-webpack5',
- },
- skipTasks: ['e2e-tests-dev', 'bench'],
- },
- 'preact-webpack5/default-ts': {
- name: 'Preact CLI Latest (Webpack | TypeScript)',
- script:
- 'npx preact-cli create typescript {{beforeDir}} --name preact-app --yarn --no-install && cd {{beforeDir}} && echo "module.exports = {}" > webpack.config.js',
- expected: {
- framework: '@storybook/preact-webpack5',
- renderer: '@storybook/preact',
- builder: '@storybook/builder-webpack5',
- },
- skipTasks: ['e2e-tests-dev', 'bench'],
- },
'preact-vite/default-js': {
name: 'Preact Latest (Vite | JavaScript)',
script: 'npm create vite --yes {{beforeDir}} -- --template preact',
@@ -618,7 +596,6 @@ export const merged: TemplateKey[] = [
'react-webpack/18-ts',
'react-webpack/17-ts',
'angular-cli/15-ts',
- 'preact-webpack5/default-ts',
'preact-vite/default-ts',
'html-webpack/default',
'html-vite/default-ts',
@@ -638,7 +615,6 @@ export const daily: TemplateKey[] = [
'nextjs/default-js',
'nextjs/prerelease',
'qwik-vite/default-ts',
- 'preact-webpack5/default-js',
'preact-vite/default-js',
'html-vite/default-js',
];
diff --git a/code/lib/core-server/src/build-static.ts b/code/lib/core-server/src/build-static.ts
index 020b9060af6..b85932ca246 100644
--- a/code/lib/core-server/src/build-static.ts
+++ b/code/lib/core-server/src/build-static.ts
@@ -22,7 +22,7 @@ import {
copyAllStaticFilesRelativeToMain,
} from './utils/copy-all-static-files';
import { getBuilders } from './utils/get-builders';
-import { convertToIndexV3, extractStoriesJson } from './utils/stories-json';
+import { extractStoriesJson } from './utils/stories-json';
import { extractStorybookMetadata } from './utils/metadata';
import { StoryIndexGenerator } from './utils/StoryIndexGenerator';
import { summarizeIndex } from './utils/summarizeIndex';
@@ -172,19 +172,11 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption
storyIndexers: deprecatedStoryIndexers,
indexers,
docs: docsOptions,
- storiesV2Compatibility: !features?.storyStoreV7,
storyStoreV7: !!features?.storyStoreV7,
build,
});
initializedStoryIndexGenerator = generator.initialize().then(() => generator);
- effects.push(
- extractStoriesJson(
- join(options.outputDir, 'stories.json'),
- initializedStoryIndexGenerator as Promise,
- convertToIndexV3
- )
- );
effects.push(
extractStoriesJson(
join(options.outputDir, 'index.json'),
diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.deprecated.test.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.deprecated.test.ts
deleted file mode 100644
index 60d700bad62..00000000000
--- a/code/lib/core-server/src/utils/StoryIndexGenerator.deprecated.test.ts
+++ /dev/null
@@ -1,1485 +0,0 @@
-/* eslint-disable @typescript-eslint/no-shadow */
-/// ;
-
-/**
- * @jest-environment node
- */
-
-import path from 'path';
-import fs from 'fs-extra';
-import { normalizeStoriesEntry } from '@storybook/core-common';
-import type { NormalizedStoriesSpecifier, StoryIndexEntry } from '@storybook/types';
-import { loadCsf, getStorySortParameter } from '@storybook/csf-tools';
-import { toId } from '@storybook/csf';
-import { logger, once } from '@storybook/node-logger';
-
-import type { StoryIndexGeneratorOptions } from './StoryIndexGenerator';
-import { StoryIndexGenerator } from './StoryIndexGenerator';
-
-jest.mock('@storybook/csf-tools');
-jest.mock('@storybook/csf', () => {
- const csf = jest.requireActual('@storybook/csf');
- return {
- ...csf,
- toId: jest.fn(csf.toId),
- };
-});
-
-jest.mock('@storybook/node-logger');
-
-const toIdMock = toId as jest.Mock>;
-const loadCsfMock = loadCsf as jest.Mock>;
-const getStorySortParameterMock = getStorySortParameter as jest.Mock<
- ReturnType
->;
-
-const csfIndexer = async (fileName: string, opts: any) => {
- const code = (await fs.readFile(fileName, 'utf-8')).toString();
- return loadCsf(code, { ...opts, fileName }).parse();
-};
-
-const storiesMdxIndexer = async (fileName: string, opts: any) => {
- let code = (await fs.readFile(fileName, 'utf-8')).toString();
- const { compile } = await import('@storybook/mdx2-csf');
- code = await compile(code, {});
- return loadCsf(code, { ...opts, fileName }).parse();
-};
-
-const options: StoryIndexGeneratorOptions = {
- configDir: path.join(__dirname, '__mockdata__'),
- workingDir: path.join(__dirname, '__mockdata__'),
- storyIndexers: [
- { test: /\.stories\.mdx$/, indexer: storiesMdxIndexer },
- { test: /\.stories\.(m?js|ts)x?$/, indexer: csfIndexer },
- ],
- indexers: [],
- storiesV2Compatibility: false,
- storyStoreV7: true,
- docs: { defaultName: 'docs', autodocs: false },
-};
-
-describe('StoryIndexGenerator with deprecated indexer API', () => {
- beforeEach(() => {
- const actual = jest.requireActual('@storybook/csf-tools');
- loadCsfMock.mockImplementation(actual.loadCsf);
- jest.mocked(logger.warn).mockClear();
- jest.mocked(once.warn).mockClear();
- });
- describe('extraction', () => {
- const storiesSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/A.stories.(ts|js|mjs|jsx)',
- options
- );
- const docsSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/docs2/*.mdx',
- options
- );
-
- describe('single file specifier', () => {
- it('extracts stories from the right files', async () => {
- const specifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/A.stories.js',
- options
- );
-
- const generator = new StoryIndexGenerator([specifier], options);
- await generator.initialize();
-
- expect(await generator.getIndex()).toMatchInlineSnapshot(`
- Object {
- "entries": Object {
- "a--story-one": Object {
- "id": "a--story-one",
- "importPath": "./src/A.stories.js",
- "name": "Story One",
- "tags": Array [
- "component-tag",
- "story-tag",
- "story",
- ],
- "title": "A",
- "type": "story",
- },
- },
- "v": 4,
- }
- `);
- });
- });
- describe('non-recursive specifier', () => {
- it('extracts stories from the right files', async () => {
- const specifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/*/*.stories.(ts|js|mjs|jsx)',
- options
- );
-
- const generator = new StoryIndexGenerator([specifier], options);
- await generator.initialize();
-
- expect(await generator.getIndex()).toMatchInlineSnapshot(`
- Object {
- "entries": Object {
- "nested-button--story-one": Object {
- "id": "nested-button--story-one",
- "importPath": "./src/nested/Button.stories.ts",
- "name": "Story One",
- "tags": Array [
- "component-tag",
- "story",
- ],
- "title": "nested/Button",
- "type": "story",
- },
- "second-nested-g--story-one": Object {
- "id": "second-nested-g--story-one",
- "importPath": "./src/second-nested/G.stories.ts",
- "name": "Story One",
- "tags": Array [
- "story",
- ],
- "title": "second-nested/G",
- "type": "story",
- },
- },
- "v": 4,
- }
- `);
- });
- });
- describe('recursive specifier', () => {
- it('extracts stories from the right files', async () => {
- const specifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/**/*.stories.(ts|js|mjs|jsx)',
- options
- );
-
- const generator = new StoryIndexGenerator([specifier], options);
- await generator.initialize();
-
- expect(await generator.getIndex()).toMatchInlineSnapshot(`
- Object {
- "entries": Object {
- "a--story-one": Object {
- "id": "a--story-one",
- "importPath": "./src/A.stories.js",
- "name": "Story One",
- "tags": Array [
- "component-tag",
- "story-tag",
- "story",
- ],
- "title": "A",
- "type": "story",
- },
- "b--story-one": Object {
- "id": "b--story-one",
- "importPath": "./src/B.stories.ts",
- "name": "Story One",
- "tags": Array [
- "autodocs",
- "story",
- ],
- "title": "B",
- "type": "story",
- },
- "d--story-one": Object {
- "id": "d--story-one",
- "importPath": "./src/D.stories.jsx",
- "name": "Story One",
- "tags": Array [
- "autodocs",
- "story",
- ],
- "title": "D",
- "type": "story",
- },
- "first-nested-deeply-f--story-one": Object {
- "id": "first-nested-deeply-f--story-one",
- "importPath": "./src/first-nested/deeply/F.stories.js",
- "name": "Story One",
- "tags": Array [
- "story",
- ],
- "title": "first-nested/deeply/F",
- "type": "story",
- },
- "h--story-one": Object {
- "id": "h--story-one",
- "importPath": "./src/H.stories.mjs",
- "name": "Story One",
- "tags": Array [
- "autodocs",
- "story",
- ],
- "title": "H",
- "type": "story",
- },
- "nested-button--story-one": Object {
- "id": "nested-button--story-one",
- "importPath": "./src/nested/Button.stories.ts",
- "name": "Story One",
- "tags": Array [
- "component-tag",
- "story",
- ],
- "title": "nested/Button",
- "type": "story",
- },
- "second-nested-g--story-one": Object {
- "id": "second-nested-g--story-one",
- "importPath": "./src/second-nested/G.stories.ts",
- "name": "Story One",
- "tags": Array [
- "story",
- ],
- "title": "second-nested/G",
- "type": "story",
- },
- },
- "v": 4,
- }
- `);
- });
- });
-
- describe('mdx tagged components', () => {
- it('adds docs entry with docs enabled', async () => {
- const specifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/nested/Page.stories.mdx',
- options
- );
-
- const generator = new StoryIndexGenerator([specifier], {
- ...options,
- });
- await generator.initialize();
-
- expect(await generator.getIndex()).toMatchInlineSnapshot(`
- Object {
- "entries": Object {
- "page--docs": Object {
- "id": "page--docs",
- "importPath": "./src/nested/Page.stories.mdx",
- "name": "docs",
- "storiesImports": Array [],
- "tags": Array [
- "stories-mdx",
- "docs",
- ],
- "title": "Page",
- "type": "docs",
- },
- "page--story-one": Object {
- "id": "page--story-one",
- "importPath": "./src/nested/Page.stories.mdx",
- "name": "StoryOne",
- "tags": Array [
- "stories-mdx",
- "story",
- ],
- "title": "Page",
- "type": "story",
- },
- },
- "v": 4,
- }
- `);
- });
- });
-
- describe('autodocs', () => {
- const autodocsOptions = {
- ...options,
- docs: { ...options.docs, autodocs: 'tag' as const },
- };
- it('generates an entry per CSF file with the autodocs tag', async () => {
- const specifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/**/*.stories.(ts|js|mjs|jsx)',
- options
- );
-
- const generator = new StoryIndexGenerator([specifier], autodocsOptions);
- await generator.initialize();
-
- expect(await generator.getIndex()).toMatchInlineSnapshot(`
- Object {
- "entries": Object {
- "a--story-one": Object {
- "id": "a--story-one",
- "importPath": "./src/A.stories.js",
- "name": "Story One",
- "tags": Array [
- "component-tag",
- "story-tag",
- "story",
- ],
- "title": "A",
- "type": "story",
- },
- "b--docs": Object {
- "id": "b--docs",
- "importPath": "./src/B.stories.ts",
- "name": "docs",
- "storiesImports": Array [],
- "tags": Array [
- "autodocs",
- "docs",
- ],
- "title": "B",
- "type": "docs",
- },
- "b--story-one": Object {
- "id": "b--story-one",
- "importPath": "./src/B.stories.ts",
- "name": "Story One",
- "tags": Array [
- "autodocs",
- "story",
- ],
- "title": "B",
- "type": "story",
- },
- "d--docs": Object {
- "id": "d--docs",
- "importPath": "./src/D.stories.jsx",
- "name": "docs",
- "storiesImports": Array [],
- "tags": Array [
- "autodocs",
- "docs",
- ],
- "title": "D",
- "type": "docs",
- },
- "d--story-one": Object {
- "id": "d--story-one",
- "importPath": "./src/D.stories.jsx",
- "name": "Story One",
- "tags": Array [
- "autodocs",
- "story",
- ],
- "title": "D",
- "type": "story",
- },
- "first-nested-deeply-f--story-one": Object {
- "id": "first-nested-deeply-f--story-one",
- "importPath": "./src/first-nested/deeply/F.stories.js",
- "name": "Story One",
- "tags": Array [
- "story",
- ],
- "title": "first-nested/deeply/F",
- "type": "story",
- },
- "h--docs": Object {
- "id": "h--docs",
- "importPath": "./src/H.stories.mjs",
- "name": "docs",
- "storiesImports": Array [],
- "tags": Array [
- "autodocs",
- "docs",
- ],
- "title": "H",
- "type": "docs",
- },
- "h--story-one": Object {
- "id": "h--story-one",
- "importPath": "./src/H.stories.mjs",
- "name": "Story One",
- "tags": Array [
- "autodocs",
- "story",
- ],
- "title": "H",
- "type": "story",
- },
- "nested-button--story-one": Object {
- "id": "nested-button--story-one",
- "importPath": "./src/nested/Button.stories.ts",
- "name": "Story One",
- "tags": Array [
- "component-tag",
- "story",
- ],
- "title": "nested/Button",
- "type": "story",
- },
- "second-nested-g--story-one": Object {
- "id": "second-nested-g--story-one",
- "importPath": "./src/second-nested/G.stories.ts",
- "name": "Story One",
- "tags": Array [
- "story",
- ],
- "title": "second-nested/G",
- "type": "story",
- },
- },
- "v": 4,
- }
- `);
- });
-
- const autodocsTrueOptions = {
- ...autodocsOptions,
- docs: {
- ...autodocsOptions.docs,
- autodocs: true,
- },
- };
- it('generates an entry for every CSF file when docsOptions.autodocs = true', async () => {
- const specifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/**/*.stories.(ts|js|mjs|jsx)',
- options
- );
-
- const generator = new StoryIndexGenerator([specifier], autodocsTrueOptions);
- await generator.initialize();
-
- expect(Object.keys((await generator.getIndex()).entries)).toMatchInlineSnapshot(`
- Array [
- "a--docs",
- "a--story-one",
- "b--docs",
- "b--story-one",
- "d--docs",
- "d--story-one",
- "h--docs",
- "h--story-one",
- "first-nested-deeply-f--docs",
- "first-nested-deeply-f--story-one",
- "nested-button--docs",
- "nested-button--story-one",
- "second-nested-g--docs",
- "second-nested-g--story-one",
- ]
- `);
- });
-
- it('adds the autodocs tag to the autogenerated docs entries', async () => {
- const specifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/**/*.stories.(ts|js|mjs|jsx)',
- options
- );
-
- const generator = new StoryIndexGenerator([specifier], autodocsTrueOptions);
- await generator.initialize();
-
- const index = await generator.getIndex();
- expect(index.entries['first-nested-deeply-f--docs'].tags).toEqual(
- expect.arrayContaining(['autodocs'])
- );
- });
-
- it('throws an error if you attach a named MetaOf entry which clashes with a tagged autodocs entry', async () => {
- const csfSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/B.stories.ts',
- options
- );
-
- const docsSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './errors/MetaOfClashingDefaultName.mdx',
- options
- );
-
- const generator = new StoryIndexGenerator([csfSpecifier, docsSpecifier], autodocsOptions);
- await generator.initialize();
-
- await expect(generator.getIndex()).rejects.toThrowErrorMatchingInlineSnapshot(
- `"Unable to index ./errors/MetaOfClashingDefaultName.mdx,./src/B.stories.ts"`
- );
- });
-
- it('throws an error if you attach a unnamed MetaOf entry with the same name as the CSF file that clashes with a tagged autodocs entry', async () => {
- const csfSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/B.stories.ts',
- options
- );
-
- const docsSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './errors/B.mdx',
- options
- );
-
- const generator = new StoryIndexGenerator([csfSpecifier, docsSpecifier], autodocsOptions);
- await generator.initialize();
-
- await expect(generator.getIndex()).rejects.toThrowErrorMatchingInlineSnapshot(
- `"Unable to index ./errors/B.mdx,./src/B.stories.ts"`
- );
- });
-
- it('allows you to create a second unnamed MetaOf entry that does not clash with autodocs', async () => {
- const csfSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/B.stories.ts',
- options
- );
-
- const docsSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './errors/MetaOfNoName.mdx',
- options
- );
-
- const generator = new StoryIndexGenerator([csfSpecifier, docsSpecifier], autodocsOptions);
- await generator.initialize();
-
- expect(await generator.getIndex()).toMatchInlineSnapshot(`
- Object {
- "entries": Object {
- "b--docs": Object {
- "id": "b--docs",
- "importPath": "./src/B.stories.ts",
- "name": "docs",
- "storiesImports": Array [],
- "tags": Array [
- "autodocs",
- "docs",
- ],
- "title": "B",
- "type": "docs",
- },
- "b--metaofnoname": Object {
- "id": "b--metaofnoname",
- "importPath": "./errors/MetaOfNoName.mdx",
- "name": "MetaOfNoName",
- "storiesImports": Array [
- "./src/B.stories.ts",
- ],
- "tags": Array [
- "attached-mdx",
- "docs",
- ],
- "title": "B",
- "type": "docs",
- },
- "b--story-one": Object {
- "id": "b--story-one",
- "importPath": "./src/B.stories.ts",
- "name": "Story One",
- "tags": Array [
- "autodocs",
- "story",
- ],
- "title": "B",
- "type": "story",
- },
- },
- "v": 4,
- }
- `);
- });
- it('allows you to create a second MetaOf entry with a different name to autodocs', async () => {
- const csfSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/B.stories.ts',
- options
- );
-
- const docsSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './errors/MetaOfName.mdx',
- options
- );
-
- const generator = new StoryIndexGenerator([csfSpecifier, docsSpecifier], autodocsOptions);
- await generator.initialize();
-
- expect(await generator.getIndex()).toMatchInlineSnapshot(`
- Object {
- "entries": Object {
- "b--docs": Object {
- "id": "b--docs",
- "importPath": "./src/B.stories.ts",
- "name": "docs",
- "storiesImports": Array [],
- "tags": Array [
- "autodocs",
- "docs",
- ],
- "title": "B",
- "type": "docs",
- },
- "b--name": Object {
- "id": "b--name",
- "importPath": "./errors/MetaOfName.mdx",
- "name": "name",
- "storiesImports": Array [
- "./src/B.stories.ts",
- ],
- "tags": Array [
- "attached-mdx",
- "docs",
- ],
- "title": "B",
- "type": "docs",
- },
- "b--story-one": Object {
- "id": "b--story-one",
- "importPath": "./src/B.stories.ts",
- "name": "Story One",
- "tags": Array [
- "autodocs",
- "story",
- ],
- "title": "B",
- "type": "story",
- },
- },
- "v": 4,
- }
- `);
- });
-
- it('allows you to override autodocs with MetaOf if it is automatic', async () => {
- const csfSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/A.stories.js',
- options
- );
-
- const docsSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './errors/A.mdx',
- options
- );
-
- const generator = new StoryIndexGenerator(
- [csfSpecifier, docsSpecifier],
- autodocsTrueOptions
- );
- await generator.initialize();
-
- expect(await generator.getIndex()).toMatchInlineSnapshot(`
- Object {
- "entries": Object {
- "a--docs": Object {
- "id": "a--docs",
- "importPath": "./errors/A.mdx",
- "name": "docs",
- "storiesImports": Array [
- "./src/A.stories.js",
- ],
- "tags": Array [
- "attached-mdx",
- "docs",
- ],
- "title": "A",
- "type": "docs",
- },
- "a--story-one": Object {
- "id": "a--story-one",
- "importPath": "./src/A.stories.js",
- "name": "Story One",
- "tags": Array [
- "component-tag",
- "story-tag",
- "story",
- ],
- "title": "A",
- "type": "story",
- },
- },
- "v": 4,
- }
- `);
- });
-
- it('generates a combined entry if there are two stories files for the same title', async () => {
- const specifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './duplicate/*.stories.(ts|js|mjs|jsx)',
- options
- );
-
- const generator = new StoryIndexGenerator([specifier], autodocsOptions);
- await generator.initialize();
-
- expect(await generator.getIndex()).toMatchInlineSnapshot(`
- Object {
- "entries": Object {
- "duplicate-a--docs": Object {
- "id": "duplicate-a--docs",
- "importPath": "./duplicate/A.stories.js",
- "name": "docs",
- "storiesImports": Array [
- "./duplicate/SecondA.stories.js",
- ],
- "tags": Array [
- "autodocs",
- "docs",
- ],
- "title": "duplicate/A",
- "type": "docs",
- },
- "duplicate-a--story-one": Object {
- "id": "duplicate-a--story-one",
- "importPath": "./duplicate/A.stories.js",
- "name": "Story One",
- "tags": Array [
- "autodocs",
- "story",
- ],
- "title": "duplicate/A",
- "type": "story",
- },
- "duplicate-a--story-two": Object {
- "id": "duplicate-a--story-two",
- "importPath": "./duplicate/SecondA.stories.js",
- "name": "Story Two",
- "tags": Array [
- "autodocs",
- "story",
- ],
- "title": "duplicate/A",
- "type": "story",
- },
- },
- "v": 4,
- }
- `);
- });
-
- // https://github.com/storybookjs/storybook/issues/19142
- it('does not generate a docs page entry if there are no stories in the CSF file', async () => {
- const csfSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './errors/NoStories.stories.ts',
- options
- );
-
- const generator = new StoryIndexGenerator([csfSpecifier], autodocsOptions);
- await generator.initialize();
-
- expect(await generator.getIndex()).toMatchInlineSnapshot(`
- Object {
- "entries": Object {},
- "v": 4,
- }
- `);
- });
- });
-
- describe('docs specifier', () => {
- it('creates correct docs entries', async () => {
- const generator = new StoryIndexGenerator([storiesSpecifier, docsSpecifier], options);
- await generator.initialize();
-
- expect(await generator.getIndex()).toMatchInlineSnapshot(`
- Object {
- "entries": Object {
- "a--metaof": Object {
- "id": "a--metaof",
- "importPath": "./src/docs2/MetaOf.mdx",
- "name": "MetaOf",
- "storiesImports": Array [
- "./src/A.stories.js",
- ],
- "tags": Array [
- "attached-mdx",
- "docs",
- ],
- "title": "A",
- "type": "docs",
- },
- "a--second-docs": Object {
- "id": "a--second-docs",
- "importPath": "./src/docs2/SecondMetaOf.mdx",
- "name": "Second Docs",
- "storiesImports": Array [
- "./src/A.stories.js",
- ],
- "tags": Array [
- "attached-mdx",
- "docs",
- ],
- "title": "A",
- "type": "docs",
- },
- "a--story-one": Object {
- "id": "a--story-one",
- "importPath": "./src/A.stories.js",
- "name": "Story One",
- "tags": Array [
- "component-tag",
- "story-tag",
- "story",
- ],
- "title": "A",
- "type": "story",
- },
- "componentreference--docs": Object {
- "id": "componentreference--docs",
- "importPath": "./src/docs2/ComponentReference.mdx",
- "name": "docs",
- "storiesImports": Array [],
- "tags": Array [
- "unattached-mdx",
- "docs",
- ],
- "title": "ComponentReference",
- "type": "docs",
- },
- "docs2-yabbadabbadooo--docs": Object {
- "id": "docs2-yabbadabbadooo--docs",
- "importPath": "./src/docs2/Title.mdx",
- "name": "docs",
- "storiesImports": Array [],
- "tags": Array [
- "unattached-mdx",
- "docs",
- ],
- "title": "docs2/Yabbadabbadooo",
- "type": "docs",
- },
- "notitle--docs": Object {
- "id": "notitle--docs",
- "importPath": "./src/docs2/NoTitle.mdx",
- "name": "docs",
- "storiesImports": Array [],
- "tags": Array [
- "unattached-mdx",
- "docs",
- ],
- "title": "NoTitle",
- "type": "docs",
- },
- },
- "v": 4,
- }
- `);
- });
-
- it('does not append title prefix if meta references a CSF file', async () => {
- const generator = new StoryIndexGenerator(
- [
- storiesSpecifier,
- normalizeStoriesEntry(
- { directory: './src/docs2', files: '**/*.mdx', titlePrefix: 'titlePrefix' },
- options
- ),
- ],
- options
- );
- await generator.initialize();
-
- // NOTE: `toMatchInlineSnapshot` on objects sorts the keys, but in actuality, they are
- // not sorted by default.
- expect(Object.values((await generator.getIndex()).entries).map((e) => e.title))
- .toMatchInlineSnapshot(`
- Array [
- "A",
- "titlePrefix/ComponentReference",
- "A",
- "titlePrefix/NoTitle",
- "A",
- "titlePrefix/docs2/Yabbadabbadooo",
- ]
- `);
- });
-
- it('Allows you to override default name for docs files', async () => {
- const generator = new StoryIndexGenerator([storiesSpecifier, docsSpecifier], {
- ...options,
- docs: {
- ...options.docs,
- defaultName: 'Info',
- },
- });
- await generator.initialize();
-
- expect(await generator.getIndex()).toMatchInlineSnapshot(`
- Object {
- "entries": Object {
- "a--metaof": Object {
- "id": "a--metaof",
- "importPath": "./src/docs2/MetaOf.mdx",
- "name": "MetaOf",
- "storiesImports": Array [
- "./src/A.stories.js",
- ],
- "tags": Array [
- "attached-mdx",
- "docs",
- ],
- "title": "A",
- "type": "docs",
- },
- "a--second-docs": Object {
- "id": "a--second-docs",
- "importPath": "./src/docs2/SecondMetaOf.mdx",
- "name": "Second Docs",
- "storiesImports": Array [
- "./src/A.stories.js",
- ],
- "tags": Array [
- "attached-mdx",
- "docs",
- ],
- "title": "A",
- "type": "docs",
- },
- "a--story-one": Object {
- "id": "a--story-one",
- "importPath": "./src/A.stories.js",
- "name": "Story One",
- "tags": Array [
- "component-tag",
- "story-tag",
- "story",
- ],
- "title": "A",
- "type": "story",
- },
- "componentreference--info": Object {
- "id": "componentreference--info",
- "importPath": "./src/docs2/ComponentReference.mdx",
- "name": "Info",
- "storiesImports": Array [],
- "tags": Array [
- "unattached-mdx",
- "docs",
- ],
- "title": "ComponentReference",
- "type": "docs",
- },
- "docs2-yabbadabbadooo--info": Object {
- "id": "docs2-yabbadabbadooo--info",
- "importPath": "./src/docs2/Title.mdx",
- "name": "Info",
- "storiesImports": Array [],
- "tags": Array [
- "unattached-mdx",
- "docs",
- ],
- "title": "docs2/Yabbadabbadooo",
- "type": "docs",
- },
- "notitle--info": Object {
- "id": "notitle--info",
- "importPath": "./src/docs2/NoTitle.mdx",
- "name": "Info",
- "storiesImports": Array [],
- "tags": Array [
- "unattached-mdx",
- "docs",
- ],
- "title": "NoTitle",
- "type": "docs",
- },
- },
- "v": 4,
- }
- `);
- });
-
- it('pulls the attached story file to the front of the list', async () => {
- const generator = new StoryIndexGenerator(
- [
- normalizeStoriesEntry('./src/A.stories.js', options),
- normalizeStoriesEntry('./src/B.stories.ts', options),
- normalizeStoriesEntry('./complex/TwoStoryReferences.mdx', options),
- ],
- options
- );
- await generator.initialize();
- expect(await generator.getIndex()).toMatchInlineSnapshot(`
- Object {
- "entries": Object {
- "a--story-one": Object {
- "id": "a--story-one",
- "importPath": "./src/A.stories.js",
- "name": "Story One",
- "tags": Array [
- "component-tag",
- "story-tag",
- "story",
- ],
- "title": "A",
- "type": "story",
- },
- "b--story-one": Object {
- "id": "b--story-one",
- "importPath": "./src/B.stories.ts",
- "name": "Story One",
- "tags": Array [
- "autodocs",
- "story",
- ],
- "title": "B",
- "type": "story",
- },
- "b--twostoryreferences": Object {
- "id": "b--twostoryreferences",
- "importPath": "./complex/TwoStoryReferences.mdx",
- "name": "TwoStoryReferences",
- "storiesImports": Array [
- "./src/B.stories.ts",
- "./src/A.stories.js",
- ],
- "tags": Array [
- "attached-mdx",
- "docs",
- ],
- "title": "B",
- "type": "docs",
- },
- },
- "v": 4,
- }
- `);
- });
- });
-
- describe('errors', () => {
- it('when docs dependencies are missing', async () => {
- const generator = new StoryIndexGenerator(
- [normalizeStoriesEntry('./src/docs2/MetaOf.mdx', options)],
- options
- );
- await generator.initialize();
- await expect(() => generator.getIndex()).rejects.toThrowErrorMatchingInlineSnapshot(
- `"Unable to index ./src/docs2/MetaOf.mdx"`
- );
- });
- });
-
- describe('warnings', () => {
- it('when entries do not match any files', async () => {
- const generator = new StoryIndexGenerator(
- [normalizeStoriesEntry('./src/docs2/wrong.js', options)],
- options
- );
- await generator.initialize();
- await generator.getIndex();
-
- expect(once.warn).toHaveBeenCalledTimes(1);
- const logMessage = jest.mocked(once.warn).mock.calls[0][0];
- expect(logMessage).toContain(`No story files found for the specified pattern`);
- });
- });
-
- describe('duplicates', () => {
- it('errors when two MDX entries reference the same CSF file without a name', async () => {
- const docsErrorSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './errors/**/A.mdx',
- options
- );
-
- const generator = new StoryIndexGenerator(
- [storiesSpecifier, docsSpecifier, docsErrorSpecifier],
- options
- );
- await generator.initialize();
-
- await expect(generator.getIndex()).rejects.toThrowErrorMatchingInlineSnapshot(
- `"Unable to index ./errors/A.mdx,./errors/duplicate/A.mdx"`
- );
- });
-
- it('errors when a MDX entry has the same name as a story', async () => {
- const docsErrorSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './errors/MetaOfClashingName.mdx',
- options
- );
-
- const generator = new StoryIndexGenerator(
- [storiesSpecifier, docsSpecifier, docsErrorSpecifier],
- options
- );
- await generator.initialize();
-
- await expect(generator.getIndex()).rejects.toThrowErrorMatchingInlineSnapshot(
- `"Unable to index ./src/A.stories.js,./errors/MetaOfClashingName.mdx"`
- );
- });
-
- it('errors when a story has the default docs name', async () => {
- const docsErrorSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './errors/A.mdx',
- options
- );
-
- const generator = new StoryIndexGenerator(
- [storiesSpecifier, docsSpecifier, docsErrorSpecifier],
- {
- ...options,
- docs: { ...options.docs, defaultName: 'Story One' },
- }
- );
- await generator.initialize();
-
- await expect(generator.getIndex()).rejects.toThrowErrorMatchingInlineSnapshot(
- `"Unable to index ./src/A.stories.js,./errors/A.mdx"`
- );
- });
- it('errors when two duplicate stories exists, with duplicated entries details', async () => {
- const generator = new StoryIndexGenerator([storiesSpecifier, docsSpecifier], {
- ...options,
- });
- await generator.initialize();
- const mockEntry: StoryIndexEntry = {
- id: 'StoryId',
- name: 'StoryName',
- title: 'ComponentTitle',
- importPath: 'Path',
- type: 'story',
- };
- expect(() => {
- generator.chooseDuplicate(mockEntry, { ...mockEntry, importPath: 'DifferentPath' });
- }).toThrowErrorMatchingInlineSnapshot(`"Duplicate stories with id: StoryId"`);
- });
-
- it('DOES NOT error when the same MDX file matches two specifiers', async () => {
- const generator = new StoryIndexGenerator(
- [storiesSpecifier, docsSpecifier, docsSpecifier],
- options
- );
- await generator.initialize();
-
- expect(Object.keys((await generator.getIndex()).entries)).toMatchInlineSnapshot(`
- Array [
- "a--story-one",
- "componentreference--docs",
- "a--metaof",
- "notitle--docs",
- "a--second-docs",
- "docs2-yabbadabbadooo--docs",
- ]
- `);
-
- expect(logger.warn).not.toHaveBeenCalled();
- });
-
- it('DOES NOT throw when the same CSF file matches two specifiers', async () => {
- const generator = new StoryIndexGenerator([storiesSpecifier, storiesSpecifier], {
- ...options,
- });
- await generator.initialize();
- expect(Object.keys((await generator.getIndex()).entries)).toMatchInlineSnapshot(`
- Array [
- "a--story-one",
- ]
- `);
-
- expect(logger.warn).not.toHaveBeenCalled();
- });
-
- it('DOES NOT throw when the same CSF file is indexed by both a deprecated and current indexer', async () => {
- const generator = new StoryIndexGenerator([storiesSpecifier], {
- ...options,
- indexers: [
- {
- test: /\.stories\.(m?js|ts)x?$/,
- createIndex: async (fileName, options) => {
- const code = (await fs.readFile(fileName, 'utf-8')).toString();
- const csf = loadCsf(code, { ...options, fileName }).parse();
-
- // eslint-disable-next-line no-underscore-dangle
- return Object.entries(csf._stories).map(([exportName, story]) => ({
- type: 'story',
- importPath: fileName,
- exportName,
- name: story.name,
- title: csf.meta.title,
- metaId: csf.meta.id,
- tags: story.tags ?? csf.meta.tags,
- __id: story.id,
- }));
- },
- },
- ],
- });
- await generator.initialize();
- expect(Object.keys((await generator.getIndex()).entries)).toMatchInlineSnapshot(`
- Array [
- "a--story-one",
- ]
- `);
-
- expect(logger.warn).not.toHaveBeenCalled();
- });
- });
- });
-
- describe('sorting', () => {
- it('runs a user-defined sort function', async () => {
- const storiesSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/**/*.stories.(ts|js|mjs|jsx)',
- options
- );
- const docsSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/docs2/*.mdx',
- options
- );
-
- const generator = new StoryIndexGenerator([docsSpecifier, storiesSpecifier], options);
- await generator.initialize();
-
- (getStorySortParameter as jest.Mock).mockReturnValueOnce({
- order: ['docs2', 'D', 'B', 'nested', 'A', 'second-nested', 'first-nested/deeply'],
- });
-
- expect(Object.keys((await generator.getIndex()).entries)).toMatchInlineSnapshot(`
- Array [
- "docs2-yabbadabbadooo--docs",
- "d--story-one",
- "b--story-one",
- "nested-button--story-one",
- "a--metaof",
- "a--second-docs",
- "a--story-one",
- "second-nested-g--story-one",
- "componentreference--docs",
- "notitle--docs",
- "h--story-one",
- "first-nested-deeply-f--story-one",
- ]
- `);
- });
- });
-
- describe('caching', () => {
- describe('no invalidation', () => {
- it('does not extract csf files a second time', async () => {
- const specifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/**/*.stories.(ts|js|mjs|jsx)',
- options
- );
-
- loadCsfMock.mockClear();
- const generator = new StoryIndexGenerator([specifier], options);
- await generator.initialize();
- await generator.getIndex();
- expect(loadCsfMock).toHaveBeenCalledTimes(7);
-
- loadCsfMock.mockClear();
- await generator.getIndex();
- expect(loadCsfMock).not.toHaveBeenCalled();
- });
-
- it('does not extract docs files a second time', async () => {
- const storiesSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/A.stories.(ts|js|mjs|jsx)',
- options
- );
- const docsSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/docs2/*.mdx',
- options
- );
-
- const generator = new StoryIndexGenerator([storiesSpecifier, docsSpecifier], options);
- await generator.initialize();
- await generator.getIndex();
- expect(toId).toHaveBeenCalledTimes(6);
-
- toIdMock.mockClear();
- await generator.getIndex();
- expect(toId).not.toHaveBeenCalled();
- });
-
- it('does not call the sort function a second time', async () => {
- const specifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/**/*.stories.(ts|js|mjs|jsx)',
- options
- );
-
- const sortFn = jest.fn();
- getStorySortParameterMock.mockReturnValue(sortFn);
- const generator = new StoryIndexGenerator([specifier], options);
- await generator.initialize();
- await generator.getIndex();
- expect(sortFn).toHaveBeenCalled();
-
- sortFn.mockClear();
- await generator.getIndex();
- expect(sortFn).not.toHaveBeenCalled();
- });
- });
-
- describe('file changed', () => {
- it('calls extract csf file for just the one file', async () => {
- const specifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/**/*.stories.(ts|js|mjs|jsx)',
- options
- );
-
- loadCsfMock.mockClear();
- const generator = new StoryIndexGenerator([specifier], options);
- await generator.initialize();
- await generator.getIndex();
- expect(loadCsfMock).toHaveBeenCalledTimes(7);
-
- generator.invalidate(specifier, './src/B.stories.ts', false);
-
- loadCsfMock.mockClear();
- await generator.getIndex();
- expect(loadCsfMock).toHaveBeenCalledTimes(1);
- });
-
- it('calls extract docs file for just the one file', async () => {
- const storiesSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/A.stories.(ts|js|mjs|jsx)',
- options
- );
- const docsSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/docs2/*.mdx',
- options
- );
-
- const generator = new StoryIndexGenerator([storiesSpecifier, docsSpecifier], options);
- await generator.initialize();
- await generator.getIndex();
- expect(toId).toHaveBeenCalledTimes(6);
-
- generator.invalidate(docsSpecifier, './src/docs2/Title.mdx', false);
-
- toIdMock.mockClear();
- await generator.getIndex();
- expect(toId).toHaveBeenCalledTimes(1);
- });
-
- it('calls extract for a csf file and any of its docs dependents', async () => {
- const storiesSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/A.stories.(ts|js|mjs|jsx)',
- options
- );
- const docsSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/docs2/*.mdx',
- options
- );
-
- const generator = new StoryIndexGenerator([storiesSpecifier, docsSpecifier], options);
- await generator.initialize();
- await generator.getIndex();
- expect(toId).toHaveBeenCalledTimes(6);
-
- generator.invalidate(storiesSpecifier, './src/A.stories.js', false);
-
- toIdMock.mockClear();
- await generator.getIndex();
- expect(toId).toHaveBeenCalledTimes(3);
- });
-
- it('does call the sort function a second time', async () => {
- const specifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/**/*.stories.(ts|js|mjs|jsx)',
- options
- );
-
- const sortFn = jest.fn();
- getStorySortParameterMock.mockReturnValue(sortFn);
- const generator = new StoryIndexGenerator([specifier], options);
- await generator.initialize();
- await generator.getIndex();
- expect(sortFn).toHaveBeenCalled();
-
- generator.invalidate(specifier, './src/B.stories.ts', false);
-
- sortFn.mockClear();
- await generator.getIndex();
- expect(sortFn).toHaveBeenCalled();
- });
- });
-
- describe('file removed', () => {
- it('does not extract csf files a second time', async () => {
- const specifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/**/*.stories.(ts|js|mjs|jsx)',
- options
- );
-
- loadCsfMock.mockClear();
- const generator = new StoryIndexGenerator([specifier], options);
- await generator.initialize();
- await generator.getIndex();
- expect(loadCsfMock).toHaveBeenCalledTimes(7);
-
- generator.invalidate(specifier, './src/B.stories.ts', true);
-
- loadCsfMock.mockClear();
- await generator.getIndex();
- expect(loadCsfMock).not.toHaveBeenCalled();
- });
-
- it('does call the sort function a second time', async () => {
- const specifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/**/*.stories.(ts|js|mjs|jsx)',
- options
- );
-
- const sortFn = jest.fn();
- getStorySortParameterMock.mockReturnValue(sortFn);
- const generator = new StoryIndexGenerator([specifier], options);
- await generator.initialize();
- await generator.getIndex();
- expect(sortFn).toHaveBeenCalled();
-
- generator.invalidate(specifier, './src/B.stories.ts', true);
-
- sortFn.mockClear();
- await generator.getIndex();
- expect(sortFn).toHaveBeenCalled();
- });
-
- it('does not include the deleted stories in results', async () => {
- const specifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/**/*.stories.(ts|js|mjs|jsx)',
- options
- );
-
- loadCsfMock.mockClear();
- const generator = new StoryIndexGenerator([specifier], options);
- await generator.initialize();
- await generator.getIndex();
- expect(loadCsfMock).toHaveBeenCalledTimes(7);
-
- generator.invalidate(specifier, './src/B.stories.ts', true);
-
- expect(Object.keys((await generator.getIndex()).entries)).not.toContain('b--story-one');
- });
-
- it('does not include the deleted docs in results', async () => {
- const storiesSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/A.stories.(ts|js|mjs|jsx)',
- options
- );
- const docsSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/docs2/*.mdx',
- options
- );
-
- const generator = new StoryIndexGenerator([docsSpecifier, storiesSpecifier], options);
- await generator.initialize();
- await generator.getIndex();
- expect(toId).toHaveBeenCalledTimes(6);
-
- expect(Object.keys((await generator.getIndex()).entries)).toContain('notitle--docs');
-
- generator.invalidate(docsSpecifier, './src/docs2/NoTitle.mdx', true);
-
- expect(Object.keys((await generator.getIndex()).entries)).not.toContain('notitle--docs');
- });
-
- it('cleans up properly on dependent docs deletion', async () => {
- const storiesSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/A.stories.(ts|js|mjs|jsx)',
- options
- );
- const docsSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
- './src/docs2/*.mdx',
- options
- );
-
- const generator = new StoryIndexGenerator([docsSpecifier, storiesSpecifier], options);
- await generator.initialize();
- await generator.getIndex();
- expect(toId).toHaveBeenCalledTimes(6);
-
- expect(Object.keys((await generator.getIndex()).entries)).toContain('a--metaof');
-
- generator.invalidate(docsSpecifier, './src/docs2/MetaOf.mdx', true);
-
- expect(Object.keys((await generator.getIndex()).entries)).not.toContain('a--metaof');
-
- // this will throw if MetaOf is not removed from A's dependents
- generator.invalidate(storiesSpecifier, './src/A.stories.js', false);
- });
- });
- });
-});
diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts
index fad7090789f..cfabf443d83 100644
--- a/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts
+++ b/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts
@@ -48,7 +48,6 @@ const options: StoryIndexGeneratorOptions = {
workingDir: path.join(__dirname, '__mockdata__'),
storyIndexers: [],
indexers: [csfIndexer, createStoriesMdxIndexer(false)],
- storiesV2Compatibility: false,
storyStoreV7: true,
docs: { defaultName: 'docs', autodocs: false },
};
diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
index fc0f38aa883..d8f4e8ba95e 100644
--- a/code/lib/core-server/src/utils/StoryIndexGenerator.ts
+++ b/code/lib/core-server/src/utils/StoryIndexGenerator.ts
@@ -16,8 +16,6 @@ import type {
Path,
Tag,
StoryIndex,
- V3CompatIndexEntry,
- StoryId,
StoryName,
Indexer,
IndexerOptions,
@@ -54,7 +52,6 @@ type SpecifierStoriesCache = Record;
export type StoryIndexGeneratorOptions = {
workingDir: Path;
configDir: Path;
- storiesV2Compatibility: boolean;
storyStoreV7: boolean;
storyIndexers: StoryIndexer[];
indexers: Indexer[];
@@ -661,35 +658,9 @@ export class StoryIndexGenerator {
const sorted = await this.sortStories(indexEntries);
- let compat = sorted;
- if (this.options.storiesV2Compatibility) {
- const titleToStoryCount = Object.values(sorted).reduce((acc, story) => {
- acc[story.title] = (acc[story.title] || 0) + 1;
- return acc;
- }, {} as Record);
-
- // @ts-expect-error (Converted from ts-ignore)
- compat = Object.entries(sorted).reduce((acc, entry) => {
- const [id, story] = entry;
- if (story.type === 'docs') return acc;
-
- acc[id] = {
- ...story,
- kind: story.title,
- story: story.name,
- parameters: {
- __id: story.id,
- docsOnly: titleToStoryCount[story.title] === 1 && story.name === 'Page',
- fileName: story.importPath,
- },
- };
- return acc;
- }, {} as Record);
- }
-
this.lastIndex = {
v: 4,
- entries: compat,
+ entries: sorted,
};
return this.lastIndex;
diff --git a/code/lib/core-server/src/utils/__tests__/index-extraction.test.ts b/code/lib/core-server/src/utils/__tests__/index-extraction.test.ts
index 39820b3e2c1..48618f017c3 100644
--- a/code/lib/core-server/src/utils/__tests__/index-extraction.test.ts
+++ b/code/lib/core-server/src/utils/__tests__/index-extraction.test.ts
@@ -18,7 +18,6 @@ const options: StoryIndexGeneratorOptions = {
workingDir: path.join(__dirname, '..', '__mockdata__'),
storyIndexers: [],
indexers: [],
- storiesV2Compatibility: false,
storyStoreV7: true,
docs: { defaultName: 'docs', autodocs: false },
};
diff --git a/code/lib/core-server/src/utils/getStoryIndexGenerator.ts b/code/lib/core-server/src/utils/getStoryIndexGenerator.ts
index 952edb50782..74401020256 100644
--- a/code/lib/core-server/src/utils/getStoryIndexGenerator.ts
+++ b/code/lib/core-server/src/utils/getStoryIndexGenerator.ts
@@ -35,7 +35,6 @@ export async function getStoryIndexGenerator(
indexers: await indexers,
docs: await docsOptions,
workingDir,
- storiesV2Compatibility: !features?.storyStoreV7,
storyStoreV7: features.storyStoreV7 ?? false,
});
diff --git a/code/lib/core-server/src/utils/stories-json.test.ts b/code/lib/core-server/src/utils/stories-json.test.ts
index e1f0b1f6d61..521d4f94094 100644
--- a/code/lib/core-server/src/utils/stories-json.test.ts
+++ b/code/lib/core-server/src/utils/stories-json.test.ts
@@ -7,10 +7,9 @@ import debounce from 'lodash/debounce.js';
// @ts-expect-error -- cannot find declaration file
import { createStoriesMdxIndexer } from '@storybook/addon-docs/preset';
import { STORY_INDEX_INVALIDATED } from '@storybook/core-events';
-import type { StoryIndex } from '@storybook/types';
import { normalizeStoriesEntry } from '@storybook/core-common';
-import { useStoriesJson, DEBOUNCE, convertToIndexV3 } from './stories-json';
+import { useStoriesJson, DEBOUNCE } from './stories-json';
import type { ServerChannel } from './get-server-channel';
import type { StoryIndexGeneratorOptions } from './StoryIndexGenerator';
import { StoryIndexGenerator } from './StoryIndexGenerator';
@@ -49,7 +48,6 @@ const getInitializedStoryIndexGenerator = async (
indexers: [csfIndexer, createStoriesMdxIndexer(false)],
configDir: workingDir,
workingDir,
- storiesV2Compatibility: false,
storyStoreV7: true,
docs: { defaultName: 'docs', autodocs: false },
...overrides,
@@ -98,7 +96,7 @@ describe('useStoriesJson', () => {
initializedStoryIndexGenerator: getInitializedStoryIndexGenerator(),
});
- expect(use).toHaveBeenCalledTimes(2);
+ expect(use).toHaveBeenCalledTimes(1);
const route = use.mock.calls[0][1];
await route(request, response);
@@ -276,440 +274,6 @@ describe('useStoriesJson', () => {
`);
});
- it('scans and extracts stories v3', async () => {
- const mockServerChannel = { emit: jest.fn() } as any as ServerChannel;
- useStoriesJson({
- router,
- initializedStoryIndexGenerator: getInitializedStoryIndexGenerator(),
- workingDir,
- serverChannel: mockServerChannel,
- normalizedStories,
- });
-
- expect(use).toHaveBeenCalledTimes(2);
- const route = use.mock.calls[1][1];
-
- await route(request, response);
-
- expect(send).toHaveBeenCalledTimes(1);
- expect(JSON.parse(send.mock.calls[0][0])).toMatchInlineSnapshot(`
- Object {
- "stories": Object {
- "a--metaof": Object {
- "id": "a--metaof",
- "importPath": "./src/docs2/MetaOf.mdx",
- "kind": "A",
- "name": "MetaOf",
- "parameters": Object {
- "__id": "a--metaof",
- "docsOnly": true,
- "fileName": "./src/docs2/MetaOf.mdx",
- },
- "storiesImports": Array [
- "./src/A.stories.js",
- ],
- "story": "MetaOf",
- "tags": Array [
- "attached-mdx",
- "docs",
- ],
- "title": "A",
- },
- "a--second-docs": Object {
- "id": "a--second-docs",
- "importPath": "./src/docs2/SecondMetaOf.mdx",
- "kind": "A",
- "name": "Second Docs",
- "parameters": Object {
- "__id": "a--second-docs",
- "docsOnly": true,
- "fileName": "./src/docs2/SecondMetaOf.mdx",
- },
- "storiesImports": Array [
- "./src/A.stories.js",
- ],
- "story": "Second Docs",
- "tags": Array [
- "attached-mdx",
- "docs",
- ],
- "title": "A",
- },
- "a--story-one": Object {
- "id": "a--story-one",
- "importPath": "./src/A.stories.js",
- "kind": "A",
- "name": "Story One",
- "parameters": Object {
- "__id": "a--story-one",
- "docsOnly": false,
- "fileName": "./src/A.stories.js",
- },
- "story": "Story One",
- "tags": Array [
- "component-tag",
- "story-tag",
- "story",
- ],
- "title": "A",
- },
- "b--story-one": Object {
- "id": "b--story-one",
- "importPath": "./src/B.stories.ts",
- "kind": "B",
- "name": "Story One",
- "parameters": Object {
- "__id": "b--story-one",
- "docsOnly": false,
- "fileName": "./src/B.stories.ts",
- },
- "story": "Story One",
- "tags": Array [
- "autodocs",
- "story",
- ],
- "title": "B",
- },
- "d--story-one": Object {
- "id": "d--story-one",
- "importPath": "./src/D.stories.jsx",
- "kind": "D",
- "name": "Story One",
- "parameters": Object {
- "__id": "d--story-one",
- "docsOnly": false,
- "fileName": "./src/D.stories.jsx",
- },
- "story": "Story One",
- "tags": Array [
- "autodocs",
- "story",
- ],
- "title": "D",
- },
- "docs2-componentreference--docs": Object {
- "id": "docs2-componentreference--docs",
- "importPath": "./src/docs2/ComponentReference.mdx",
- "kind": "docs2/ComponentReference",
- "name": "docs",
- "parameters": Object {
- "__id": "docs2-componentreference--docs",
- "docsOnly": true,
- "fileName": "./src/docs2/ComponentReference.mdx",
- },
- "storiesImports": Array [],
- "story": "docs",
- "tags": Array [
- "unattached-mdx",
- "docs",
- ],
- "title": "docs2/ComponentReference",
- },
- "docs2-notitle--docs": Object {
- "id": "docs2-notitle--docs",
- "importPath": "./src/docs2/NoTitle.mdx",
- "kind": "docs2/NoTitle",
- "name": "docs",
- "parameters": Object {
- "__id": "docs2-notitle--docs",
- "docsOnly": true,
- "fileName": "./src/docs2/NoTitle.mdx",
- },
- "storiesImports": Array [],
- "story": "docs",
- "tags": Array [
- "unattached-mdx",
- "docs",
- ],
- "title": "docs2/NoTitle",
- },
- "docs2-yabbadabbadooo--docs": Object {
- "id": "docs2-yabbadabbadooo--docs",
- "importPath": "./src/docs2/Title.mdx",
- "kind": "docs2/Yabbadabbadooo",
- "name": "docs",
- "parameters": Object {
- "__id": "docs2-yabbadabbadooo--docs",
- "docsOnly": true,
- "fileName": "./src/docs2/Title.mdx",
- },
- "storiesImports": Array [],
- "story": "docs",
- "tags": Array [
- "unattached-mdx",
- "docs",
- ],
- "title": "docs2/Yabbadabbadooo",
- },
- "first-nested-deeply-f--story-one": Object {
- "id": "first-nested-deeply-f--story-one",
- "importPath": "./src/first-nested/deeply/F.stories.js",
- "kind": "first-nested/deeply/F",
- "name": "Story One",
- "parameters": Object {
- "__id": "first-nested-deeply-f--story-one",
- "docsOnly": false,
- "fileName": "./src/first-nested/deeply/F.stories.js",
- },
- "story": "Story One",
- "tags": Array [
- "story",
- ],
- "title": "first-nested/deeply/F",
- },
- "h--story-one": Object {
- "id": "h--story-one",
- "importPath": "./src/H.stories.mjs",
- "kind": "H",
- "name": "Story One",
- "parameters": Object {
- "__id": "h--story-one",
- "docsOnly": false,
- "fileName": "./src/H.stories.mjs",
- },
- "story": "Story One",
- "tags": Array [
- "autodocs",
- "story",
- ],
- "title": "H",
- },
- "nested-button--story-one": Object {
- "id": "nested-button--story-one",
- "importPath": "./src/nested/Button.stories.ts",
- "kind": "nested/Button",
- "name": "Story One",
- "parameters": Object {
- "__id": "nested-button--story-one",
- "docsOnly": false,
- "fileName": "./src/nested/Button.stories.ts",
- },
- "story": "Story One",
- "tags": Array [
- "component-tag",
- "story",
- ],
- "title": "nested/Button",
- },
- "nested-page--docs": Object {
- "id": "nested-page--docs",
- "importPath": "./src/nested/Page.stories.mdx",
- "kind": "nested/Page",
- "name": "docs",
- "parameters": Object {
- "__id": "nested-page--docs",
- "docsOnly": true,
- "fileName": "./src/nested/Page.stories.mdx",
- },
- "storiesImports": Array [],
- "story": "docs",
- "tags": Array [
- "stories-mdx",
- "docs",
- ],
- "title": "nested/Page",
- },
- "nested-page--story-one": Object {
- "id": "nested-page--story-one",
- "importPath": "./src/nested/Page.stories.mdx",
- "kind": "nested/Page",
- "name": "StoryOne",
- "parameters": Object {
- "__id": "nested-page--story-one",
- "docsOnly": false,
- "fileName": "./src/nested/Page.stories.mdx",
- },
- "story": "StoryOne",
- "tags": Array [
- "stories-mdx",
- "story",
- ],
- "title": "nested/Page",
- },
- "second-nested-g--story-one": Object {
- "id": "second-nested-g--story-one",
- "importPath": "./src/second-nested/G.stories.ts",
- "kind": "second-nested/G",
- "name": "Story One",
- "parameters": Object {
- "__id": "second-nested-g--story-one",
- "docsOnly": false,
- "fileName": "./src/second-nested/G.stories.ts",
- },
- "story": "Story One",
- "tags": Array [
- "story",
- ],
- "title": "second-nested/G",
- },
- },
- "v": 3,
- }
- `);
- });
-
- it('scans and extracts stories v2', async () => {
- const mockServerChannel = { emit: jest.fn() } as any as ServerChannel;
- useStoriesJson({
- router,
- initializedStoryIndexGenerator: getInitializedStoryIndexGenerator({
- storiesV2Compatibility: true,
- }),
- workingDir,
- serverChannel: mockServerChannel,
- normalizedStories,
- });
-
- expect(use).toHaveBeenCalledTimes(2);
- const route = use.mock.calls[1][1];
-
- await route(request, response);
-
- expect(send).toHaveBeenCalledTimes(1);
- expect(JSON.parse(send.mock.calls[0][0])).toMatchInlineSnapshot(`
- Object {
- "stories": Object {
- "a--story-one": Object {
- "id": "a--story-one",
- "importPath": "./src/A.stories.js",
- "kind": "A",
- "name": "Story One",
- "parameters": Object {
- "__id": "a--story-one",
- "docsOnly": false,
- "fileName": "./src/A.stories.js",
- },
- "story": "Story One",
- "tags": Array [
- "component-tag",
- "story-tag",
- "story",
- ],
- "title": "A",
- },
- "b--story-one": Object {
- "id": "b--story-one",
- "importPath": "./src/B.stories.ts",
- "kind": "B",
- "name": "Story One",
- "parameters": Object {
- "__id": "b--story-one",
- "docsOnly": false,
- "fileName": "./src/B.stories.ts",
- },
- "story": "Story One",
- "tags": Array [
- "autodocs",
- "story",
- ],
- "title": "B",
- },
- "d--story-one": Object {
- "id": "d--story-one",
- "importPath": "./src/D.stories.jsx",
- "kind": "D",
- "name": "Story One",
- "parameters": Object {
- "__id": "d--story-one",
- "docsOnly": false,
- "fileName": "./src/D.stories.jsx",
- },
- "story": "Story One",
- "tags": Array [
- "autodocs",
- "story",
- ],
- "title": "D",
- },
- "first-nested-deeply-f--story-one": Object {
- "id": "first-nested-deeply-f--story-one",
- "importPath": "./src/first-nested/deeply/F.stories.js",
- "kind": "first-nested/deeply/F",
- "name": "Story One",
- "parameters": Object {
- "__id": "first-nested-deeply-f--story-one",
- "docsOnly": false,
- "fileName": "./src/first-nested/deeply/F.stories.js",
- },
- "story": "Story One",
- "tags": Array [
- "story",
- ],
- "title": "first-nested/deeply/F",
- },
- "h--story-one": Object {
- "id": "h--story-one",
- "importPath": "./src/H.stories.mjs",
- "kind": "H",
- "name": "Story One",
- "parameters": Object {
- "__id": "h--story-one",
- "docsOnly": false,
- "fileName": "./src/H.stories.mjs",
- },
- "story": "Story One",
- "tags": Array [
- "autodocs",
- "story",
- ],
- "title": "H",
- },
- "nested-button--story-one": Object {
- "id": "nested-button--story-one",
- "importPath": "./src/nested/Button.stories.ts",
- "kind": "nested/Button",
- "name": "Story One",
- "parameters": Object {
- "__id": "nested-button--story-one",
- "docsOnly": false,
- "fileName": "./src/nested/Button.stories.ts",
- },
- "story": "Story One",
- "tags": Array [
- "component-tag",
- "story",
- ],
- "title": "nested/Button",
- },
- "nested-page--story-one": Object {
- "id": "nested-page--story-one",
- "importPath": "./src/nested/Page.stories.mdx",
- "kind": "nested/Page",
- "name": "StoryOne",
- "parameters": Object {
- "__id": "nested-page--story-one",
- "docsOnly": false,
- "fileName": "./src/nested/Page.stories.mdx",
- },
- "story": "StoryOne",
- "tags": Array [
- "stories-mdx",
- "story",
- ],
- "title": "nested/Page",
- },
- "second-nested-g--story-one": Object {
- "id": "second-nested-g--story-one",
- "importPath": "./src/second-nested/G.stories.ts",
- "kind": "second-nested/G",
- "name": "Story One",
- "parameters": Object {
- "__id": "second-nested-g--story-one",
- "docsOnly": false,
- "fileName": "./src/second-nested/G.stories.ts",
- },
- "story": "Story One",
- "tags": Array [
- "story",
- ],
- "title": "second-nested/G",
- },
- },
- "v": 3,
- }
- `);
- });
-
it('disallows .mdx files without storyStoreV7', async () => {
const mockServerChannel = { emit: jest.fn() } as any as ServerChannel;
useStoriesJson({
@@ -722,8 +286,8 @@ describe('useStoriesJson', () => {
normalizedStories,
});
- expect(use).toHaveBeenCalledTimes(2);
- const route = use.mock.calls[1][1];
+ expect(use).toHaveBeenCalledTimes(1);
+ const route = use.mock.calls[0][1];
await route(request, response);
@@ -739,152 +303,6 @@ describe('useStoriesJson', () => {
`);
});
- it('allows disabling storyStoreV7 if no .mdx files are used', async () => {
- const mockServerChannel = { emit: jest.fn() } as any as ServerChannel;
- useStoriesJson({
- router,
- initializedStoryIndexGenerator: getInitializedStoryIndexGenerator(
- { storyStoreV7: false },
- normalizedStories.slice(0, 1)
- ),
- workingDir,
- serverChannel: mockServerChannel,
- normalizedStories,
- });
-
- expect(use).toHaveBeenCalledTimes(2);
- const route = use.mock.calls[1][1];
-
- await route(request, response);
-
- expect(send).toHaveBeenCalledTimes(1);
- expect(JSON.parse(send.mock.calls[0][0])).toMatchInlineSnapshot(`
- Object {
- "stories": Object {
- "a--story-one": Object {
- "id": "a--story-one",
- "importPath": "./src/A.stories.js",
- "kind": "A",
- "name": "Story One",
- "parameters": Object {
- "__id": "a--story-one",
- "docsOnly": false,
- "fileName": "./src/A.stories.js",
- },
- "story": "Story One",
- "tags": Array [
- "component-tag",
- "story-tag",
- "story",
- ],
- "title": "A",
- },
- "b--story-one": Object {
- "id": "b--story-one",
- "importPath": "./src/B.stories.ts",
- "kind": "B",
- "name": "Story One",
- "parameters": Object {
- "__id": "b--story-one",
- "docsOnly": false,
- "fileName": "./src/B.stories.ts",
- },
- "story": "Story One",
- "tags": Array [
- "autodocs",
- "story",
- ],
- "title": "B",
- },
- "d--story-one": Object {
- "id": "d--story-one",
- "importPath": "./src/D.stories.jsx",
- "kind": "D",
- "name": "Story One",
- "parameters": Object {
- "__id": "d--story-one",
- "docsOnly": false,
- "fileName": "./src/D.stories.jsx",
- },
- "story": "Story One",
- "tags": Array [
- "autodocs",
- "story",
- ],
- "title": "D",
- },
- "first-nested-deeply-f--story-one": Object {
- "id": "first-nested-deeply-f--story-one",
- "importPath": "./src/first-nested/deeply/F.stories.js",
- "kind": "first-nested/deeply/F",
- "name": "Story One",
- "parameters": Object {
- "__id": "first-nested-deeply-f--story-one",
- "docsOnly": false,
- "fileName": "./src/first-nested/deeply/F.stories.js",
- },
- "story": "Story One",
- "tags": Array [
- "story",
- ],
- "title": "first-nested/deeply/F",
- },
- "h--story-one": Object {
- "id": "h--story-one",
- "importPath": "./src/H.stories.mjs",
- "kind": "H",
- "name": "Story One",
- "parameters": Object {
- "__id": "h--story-one",
- "docsOnly": false,
- "fileName": "./src/H.stories.mjs",
- },
- "story": "Story One",
- "tags": Array [
- "autodocs",
- "story",
- ],
- "title": "H",
- },
- "nested-button--story-one": Object {
- "id": "nested-button--story-one",
- "importPath": "./src/nested/Button.stories.ts",
- "kind": "nested/Button",
- "name": "Story One",
- "parameters": Object {
- "__id": "nested-button--story-one",
- "docsOnly": false,
- "fileName": "./src/nested/Button.stories.ts",
- },
- "story": "Story One",
- "tags": Array [
- "component-tag",
- "story",
- ],
- "title": "nested/Button",
- },
- "second-nested-g--story-one": Object {
- "id": "second-nested-g--story-one",
- "importPath": "./src/second-nested/G.stories.ts",
- "kind": "second-nested/G",
- "name": "Story One",
- "parameters": Object {
- "__id": "second-nested-g--story-one",
- "docsOnly": false,
- "fileName": "./src/second-nested/G.stories.ts",
- },
- "story": "Story One",
- "tags": Array [
- "story",
- ],
- "title": "second-nested/G",
- },
- },
- "v": 3,
- }
- `);
- });
-
it('can handle simultaneous access', async () => {
const mockServerChannel = { emit: jest.fn() } as any as ServerChannel;
@@ -896,7 +314,7 @@ describe('useStoriesJson', () => {
initializedStoryIndexGenerator: getInitializedStoryIndexGenerator(),
});
- expect(use).toHaveBeenCalledTimes(2);
+ expect(use).toHaveBeenCalledTimes(1);
const route = use.mock.calls[0][1];
const firstPromise = route(request, response);
@@ -928,7 +346,7 @@ describe('useStoriesJson', () => {
initializedStoryIndexGenerator: getInitializedStoryIndexGenerator(),
});
- expect(use).toHaveBeenCalledTimes(2);
+ expect(use).toHaveBeenCalledTimes(1);
const route = use.mock.calls[0][1];
await route(request, response);
@@ -957,7 +375,7 @@ describe('useStoriesJson', () => {
initializedStoryIndexGenerator: getInitializedStoryIndexGenerator(),
});
- expect(use).toHaveBeenCalledTimes(2);
+ expect(use).toHaveBeenCalledTimes(1);
const route = use.mock.calls[0][1];
// Don't wait for the first request here before starting the second
@@ -992,7 +410,7 @@ describe('useStoriesJson', () => {
initializedStoryIndexGenerator: getInitializedStoryIndexGenerator(),
});
- expect(use).toHaveBeenCalledTimes(2);
+ expect(use).toHaveBeenCalledTimes(1);
const route = use.mock.calls[0][1];
await route(request, response);
@@ -1021,85 +439,3 @@ describe('useStoriesJson', () => {
});
});
});
-
-describe('convertToIndexV3', () => {
- it('converts v7 index.json to v6 stories.json', () => {
- const indexJson: StoryIndex = {
- v: 4,
- entries: {
- 'a--docs': {
- id: 'a--docs',
- importPath: './src/docs2/MetaOf.mdx',
- name: 'docs',
- storiesImports: ['./src/A.stories.js'],
- title: 'A',
- type: 'docs',
- },
- 'a--story-one': {
- id: 'a--story-one',
- importPath: './src/A.stories.js',
- name: 'Story One',
- title: 'A',
- type: 'story',
- },
- 'b--story-one': {
- id: 'b--story-one',
- importPath: './src/B.stories.ts',
- name: 'Story One',
- title: 'B',
- type: 'story',
- },
- },
- };
-
- expect(convertToIndexV3(indexJson)).toMatchInlineSnapshot(`
- Object {
- "stories": Object {
- "a--docs": Object {
- "id": "a--docs",
- "importPath": "./src/docs2/MetaOf.mdx",
- "kind": "A",
- "name": "docs",
- "parameters": Object {
- "__id": "a--docs",
- "docsOnly": true,
- "fileName": "./src/docs2/MetaOf.mdx",
- },
- "storiesImports": Array [
- "./src/A.stories.js",
- ],
- "story": "docs",
- "title": "A",
- },
- "a--story-one": Object {
- "id": "a--story-one",
- "importPath": "./src/A.stories.js",
- "kind": "A",
- "name": "Story One",
- "parameters": Object {
- "__id": "a--story-one",
- "docsOnly": false,
- "fileName": "./src/A.stories.js",
- },
- "story": "Story One",
- "title": "A",
- },
- "b--story-one": Object {
- "id": "b--story-one",
- "importPath": "./src/B.stories.ts",
- "kind": "B",
- "name": "Story One",
- "parameters": Object {
- "__id": "b--story-one",
- "docsOnly": false,
- "fileName": "./src/B.stories.ts",
- },
- "story": "Story One",
- "title": "B",
- },
- },
- "v": 3,
- }
- `);
- });
-});
diff --git a/code/lib/core-server/src/utils/stories-json.ts b/code/lib/core-server/src/utils/stories-json.ts
index 7070245a604..074c0248cb0 100644
--- a/code/lib/core-server/src/utils/stories-json.ts
+++ b/code/lib/core-server/src/utils/stories-json.ts
@@ -1,7 +1,7 @@
import type { Router, Request, Response } from 'express';
import { writeJSON } from 'fs-extra';
-import type { NormalizedStoriesSpecifier, StoryIndex, StoryIndexV3 } from '@storybook/types';
+import type { NormalizedStoriesSpecifier, StoryIndex } from '@storybook/types';
import debounce from 'lodash/debounce.js';
import { STORY_INDEX_INVALIDATED } from '@storybook/core-events';
@@ -54,38 +54,4 @@ export function useStoriesJson({
res.send(err instanceof Error ? err.toString() : String(err));
}
});
-
- router.use('/stories.json', async (req: Request, res: Response) => {
- try {
- const generator = await initializedStoryIndexGenerator;
- const index = convertToIndexV3(await generator.getIndex());
- res.header('Content-Type', 'application/json');
- res.send(JSON.stringify(index));
- } catch (err) {
- res.status(500);
- res.send(err instanceof Error ? err.toString() : String(err));
- }
- });
}
-
-export const convertToIndexV3 = (index: StoryIndex): StoryIndexV3 => {
- const { entries } = index;
- const stories = Object.entries(entries).reduce((acc, [id, entry]) => {
- const { type, ...rest } = entry;
- acc[id] = {
- ...rest,
- kind: rest.title,
- story: rest.name,
- parameters: {
- __id: rest.id,
- docsOnly: type === 'docs',
- fileName: rest.importPath,
- },
- };
- return acc;
- }, {} as StoryIndexV3['stories']);
- return {
- v: 3,
- stories,
- };
-};
diff --git a/code/renderers/react/src/docs/jsxDecorator.test.tsx b/code/renderers/react/src/docs/jsxDecorator.test.tsx
index a1206a028c0..a0e29407262 100644
--- a/code/renderers/react/src/docs/jsxDecorator.test.tsx
+++ b/code/renderers/react/src/docs/jsxDecorator.test.tsx
@@ -1,6 +1,6 @@
/* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */
import type { FC, PropsWithChildren } from 'react';
-import React, { createElement, Profiler } from 'react';
+import React, { StrictMode, createElement, Profiler } from 'react';
import PropTypes from 'prop-types';
import { addons, useEffect } from '@storybook/preview-api';
import { SNIPPET_RENDERED } from '@storybook/docs-tools';
@@ -155,6 +155,40 @@ describe('renderJsx', () => {
`);
});
+ it('StrictMode', () => {
+ function StrictModeComponent(props: any) {
+ return (
+
+ {props.children}
+
+ );
+ }
+
+ expect(renderJsx(createElement(StrictModeComponent, {}, 'I am StrictMode'), {}))
+ .toMatchInlineSnapshot(`
+
+ I am StrictMode
+
+ `);
+ });
+
+ it('Suspense', () => {
+ function SuspenseComponent(props: any) {
+ return (
+
+ {props.children}
+
+ );
+ }
+
+ expect(renderJsx(createElement(SuspenseComponent, {}, 'I am Suspense'), {}))
+ .toMatchInlineSnapshot(`
+
+ I am Suspense
+
+ `);
+ });
+
it('should not add default props to string if the prop value has not changed', () => {
const Container = ({ className, children }: { className: string; children: string }) => {
return {children}
;
diff --git a/code/renderers/react/src/docs/jsxDecorator.tsx b/code/renderers/react/src/docs/jsxDecorator.tsx
index a3b56f73d6b..e43702daf02 100644
--- a/code/renderers/react/src/docs/jsxDecorator.tsx
+++ b/code/renderers/react/src/docs/jsxDecorator.tsx
@@ -11,8 +11,6 @@ import { logger } from '@storybook/client-logger';
import type { ReactRenderer } from '../types';
-import { isMemo, isForwardRef } from './lib';
-
// Recursively remove "_owner" property from elements to avoid crash on docs page when passing components as an array prop (#17482)
// Note: It may be better to use this function only in development environment.
function simplifyNodeForStringify(node: ReactNode): ReactNode {
@@ -56,7 +54,7 @@ export const renderJsx = (code: React.ReactElement, options: JSXOptions) => {
const Type = renderedJSX.type;
// @ts-expect-error (Converted from ts-ignore)
- for (let i = 0; i < options.skip; i += 1) {
+ for (let i = 0; i < options?.skip; i += 1) {
if (typeof renderedJSX === 'undefined') {
logger.warn('Cannot skip undefined element');
return null;
@@ -80,21 +78,25 @@ export const renderJsx = (code: React.ReactElement, options: JSXOptions) => {
}
}
- const displayNameDefaults =
- typeof options.displayName === 'string'
- ? { showFunctions: true, displayName: () => options.displayName }
- : {
- // To get exotic component names resolving properly
- displayName: (el: any): string =>
- el.type.displayName ||
- (el.type === Symbol.for('react.profiler') ? 'Profiler' : null) ||
- getDocgenSection(el.type, 'displayName') ||
- (el.type.name !== '_default' ? el.type.name : null) ||
- (typeof el.type === 'function' ? 'No Display Name' : null) ||
- (isForwardRef(el.type) ? el.type.render.name : null) ||
- (isMemo(el.type) ? el.type.type.name : null) ||
- el.type,
- };
+ let displayNameDefaults;
+
+ if (typeof options?.displayName === 'string') {
+ displayNameDefaults = { showFunctions: true, displayName: () => options.displayName };
+ /**
+ * add `renderedJSX?.type`to handle this case:
+ *
+ * https://github.com/zhyd1997/storybook/blob/20863a75ba4026d7eba6b288991a2cf091d4dfff/code/renderers/react/template/stories/errors.stories.tsx#L14
+ *
+ * or it show the error message when run `yarn build-storybook --quiet`:
+ *
+ * Cannot read properties of undefined (reading '__docgenInfo').
+ */
+ } else if (renderedJSX?.type && getDocgenSection(renderedJSX.type, 'displayName')) {
+ displayNameDefaults = {
+ // To get exotic component names resolving properly
+ displayName: (el: any): string => getDocgenSection(el.type, 'displayName'),
+ };
+ }
const filterDefaults = {
filterProps: (value: any, key: string): boolean => value !== undefined,
diff --git a/code/ui/components/package.json b/code/ui/components/package.json
index 1cab757ae8e..e3c856d3e11 100644
--- a/code/ui/components/package.json
+++ b/code/ui/components/package.json
@@ -67,7 +67,6 @@
"@storybook/theming": "workspace:*",
"@storybook/types": "workspace:*",
"memoizerific": "^1.11.3",
- "use-resize-observer": "^9.1.0",
"util-deprecate": "^1.0.2"
},
"devDependencies": {
@@ -82,7 +81,8 @@
"react-syntax-highlighter": "^15.4.5",
"react-textarea-autosize": "^8.3.0",
"ts-dedent": "^2.0.0",
- "typescript": "^5.3.2"
+ "typescript": "^5.3.2",
+ "use-resize-observer": "^9.1.0"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
@@ -94,8 +94,7 @@
"bundler": {
"entries": [
"./src/index.ts"
- ],
- "platform": "neutral"
+ ]
},
"gitHead": "e6a7fd8a655c69780bc20b9749c2699e44beae17"
}
diff --git a/code/ui/components/src/components/form/input/input.tsx b/code/ui/components/src/components/form/input/input.tsx
index c4897ea7931..cf143915b42 100644
--- a/code/ui/components/src/components/form/input/input.tsx
+++ b/code/ui/components/src/components/form/input/input.tsx
@@ -1,11 +1,30 @@
-import type { HTMLProps, SelectHTMLAttributes } from 'react';
+import type { FC, HTMLProps, SelectHTMLAttributes } from 'react';
import React, { forwardRef } from 'react';
import type { CSSObject, FunctionInterpolation } from '@storybook/theming';
import { styled } from '@storybook/theming';
-import type { TextareaAutosizeProps } from 'react-textarea-autosize';
import TextareaAutoResize from 'react-textarea-autosize';
+/**
+ * these types are copied from `react-textarea-autosize`.
+ * I copied them because of https://github.com/storybookjs/storybook/issues/18734
+ * Maybe there's some bug in `tsup` or `react-textarea-autosize`?
+ */
+type TextareaPropsRaw = React.TextareaHTMLAttributes;
+type Style = Omit, 'maxHeight' | 'minHeight'> & {
+ height?: number;
+};
+type TextareaHeightChangeMeta = {
+ rowHeight: number;
+};
+export interface TextareaAutosizeProps extends Omit {
+ maxRows?: number;
+ minRows?: number;
+ onHeightChange?: (height: number, meta: TextareaHeightChangeMeta) => void;
+ cacheMeasurements?: boolean;
+ style?: Style;
+}
+
const styleResets: CSSObject = {
// resets
appearance: 'none',
@@ -187,8 +206,8 @@ type TextareaProps = Omit<
align?: Alignments;
valid?: ValidationStates;
height?: number;
-};
-export const Textarea = Object.assign(
+} & React.RefAttributes;
+export const Textarea: FC = Object.assign(
styled(
forwardRef(function Textarea({ size, valid, align, ...props }, ref) {
return ;
diff --git a/docs/configure/frameworks-feature-support.md b/docs/configure/frameworks-feature-support.md
index 8b1ae823ed3..306ee9e9416 100644
--- a/docs/configure/frameworks-feature-support.md
+++ b/docs/configure/frameworks-feature-support.md
@@ -10,10 +10,108 @@ Below is a comprehensive table of whatβs supported in which framework integrat
Core frameworks have dedicated maintainers or contributors who are responsible for maintaining the integration. As such, you can use most Storybook features in these frameworks.
-
+| | React | Vue | Angular | Web Components |
+| ----------------------------------------------------------------------------------------- | ----- | --- | ------- | -------------- |
+| **Essentials** | | | | |
+| [Actions](../essentials/actions.md) | β
| β
| β
| β
|
+| [Backgrounds](../essentials/backgrounds.md) | β
| β
| β
| β
|
+| [Docs](../writing-docs/index.md) | β
| β
| β
| β
|
+| [Viewport](../essentials/viewport.md) | β
| β
| β
| β
|
+| [Controls](../essentials/controls.md) | β
| β
| β
| β
|
+| [Measure](../essentials/measure-and-outline.md#measure-addon) | β
| β
| β
| β
|
+| [Outline](../essentials/measure-and-outline.md#outline-addon) | β
| β
| β
| β
|
+| **Addons** | | | | |
+| [A11y](../writing-tests/accessibility-testing.md) | β
| β
| β
| β
|
+| [Interactions](../writing-tests/interaction-testing.md) | β
| β
| β
| β
|
+| [Test runner](../writing-tests/test-runner.md) | β
| β
| β
| β
|
+| [Test coverage](../writing-tests/test-coverage.md) | β
| β
| β
| β
|
+| [CSS resources](https://github.com/storybookjs/addon-cssresources) | β
| β
| β
| β
|
+| [Design assets](https://github.com/storybookjs/addon-design-assets) | β
| β
| β
| β
|
+| [Events](https://github.com/storybookjs/addon-events) | β
| β
| β
| β
|
+| [Google analytics](https://github.com/storybookjs/addon-google-analytics) | β
| β
| β
| β
|
+| [GraphQL](https://github.com/storybookjs/addon-graphql) | β
| | β
| |
+| [Jest](https://github.com/storybookjs/storybook/tree/next/code/addons/jest) | β
| β
| β
| β
|
+| [Links](https://github.com/storybookjs/storybook/tree/next/code/addons/links) | β
| β
| β
| β
|
+| [Queryparams](https://github.com/storybookjs/addon-queryparams) | β
| β
| β
| β
|
+| [Storysource](https://github.com/storybookjs/storybook/tree/next/code/addons/storysource) | β
| β
| β
| β
|
+| **Docs** | | | | |
+| [CSF Stories](../api/csf.md) | β
| β
| β
| β
|
+| [Autodocs](../writing-docs/autodocs.md) | β
| β
| β
| β
|
+| [Doc Blocks - ArgTypes](../api/doc-block-argtypes.md) | β
| β
| β
| β
|
+| [Doc Blocks - Canvas](../api/doc-block-canvas.md) | β
| β
| β
| β
|
+| [Doc Blocks - ColorPalette](../api/doc-block-colorpalette.md) | β
| β
| β
| β
|
+| [Doc Blocks - Controls](../api/doc-block-controls.md) | β
| β
| β
| β
|
+| [Doc Blocks - Description](../api/doc-block-description.md) | β
| β
| β
| β
|
+| [Doc Blocks - IconGallery](../api/doc-block-icongallery.md) | β
| β
| β
| β
|
+| [Doc Blocks - Markdown](../api/doc-block-markdown.md) | β
| β
| β
| β
|
+| [Doc Blocks - Meta](../api/doc-block-meta.md) | β
| β
| β
| β
|
+| [Doc Blocks - Primary](../api/doc-block-primary.md) | β
| β
| β
| β
|
+| [Doc Blocks - Source](../api/doc-block-source.md) | β
| β
| β
| β
|
+| [Doc Blocks - Story](../api/doc-block-story.md) | β
| β
| β
| β
|
+| [Doc Blocks - Stories](../api/doc-block-stories.md) | β
| β
| β
| β
|
+| [Doc Blocks - Subtitle](../api/doc-block-subtitle.md) | β
| β
| β
| β
|
+| [Doc Blocks - Title](../api/doc-block-title.md) | β
| β
| β
| β
|
+| [Doc Blocks - Typeset](../api/doc-block-typeset.md) | β
| β
| β
| β
|
+| [Doc Blocks - Unstyled](../api/doc-block-unstyled.md) | β
| β
| β
| β
|
+| [Doc Blocks - UseOf](../api/doc-block-useof.md) | β
| β
| β
| β
|
+| Inline stories | β
| β
| β
| β
|
## Community frameworks
Community frameworks have fewer contributors which means they may not be as up to date as core frameworks. If you use one of these frameworks for your job, please consider contributing to its integration with Storybook.
-
+| | Ember | HTML | Svelte | Preact | Qwik | SolidJS |
+| ----------------------------------------------------------------------------------------- | ----- | ---- | ------ | ------ | ---- | ------- |
+| **Essentials** | | | | | | |
+| [Actions](../essentials/actions.md) | β
| β
| β
| β
| β
| β
|
+| [Backgrounds](../essentials/backgrounds.md) | β
| β
| β
| β
| β
| β
|
+| [Docs](../writing-docs/index.md) | β
| β
| β
| β
| β
| β
|
+| [Viewport](../essentials/viewport.md) | β
| β
| β
| β
| β
| β
|
+| [Controls](../essentials/controls.md) | β
| β
| β
| β
| β
| β
|
+| [Measure](../essentials/measure-and-outline.md#measure-addon) | β
| β
| β
| β
| β
| β
|
+| [Outline](../essentials/measure-and-outline.md#outline-addon) | β
| β
| β
| β
| β
| β
|
+| **Addons** | | | | | | |
+| [A11y](../writing-tests/accessibility-testing.md) | β
| β
| β
| β
| β
| β
|
+| [Interactions](../writing-tests/interaction-testing.md) | | β
| β
| β
| β
| β
|
+| [Test runner](../writing-tests/test-runner.md) | | β
| β
| β
| β
| β
|
+| [Test coverage](../writing-tests/test-coverage.md) | | β
| β
| β
| β
| β
|
+| [CSS resources](https://github.com/storybookjs/addon-cssresources) | β
| β
| β
| β
| β
| β
|
+| [Design assets](https://github.com/storybookjs/addon-design-assets) | β
| β
| β
| β
| β
| β
|
+| [Events](https://github.com/storybookjs/addon-events) | β
| β
| β
| β
| β
| β
|
+| [Google analytics](https://github.com/storybookjs/addon-google-analytics) | β
| β
| β
| β
| β
| β
|
+| [GraphQL](https://github.com/storybookjs/addon-graphql) | | | | | | |
+| [Jest](https://github.com/storybookjs/storybook/tree/next/code/addons/jest) | β
| β
| β
| β
| β
| β
|
+| [Links](https://github.com/storybookjs/storybook/tree/next/code/addons/links) | β
| β
| β
| β
| β
| β
|
+| [Queryparams](https://github.com/storybookjs/addon-queryparams) | β
| β
| β
| β
| β
| β
|
+| [Storysource](https://github.com/storybookjs/storybook/tree/next/code/addons/storysource) | β
| β
| β
| β
| β
| β
|
+| **Docs** | | | | | | |
+| [CSF Stories](../api/csf.md) | β
| β
| β
| β
| β
| β
|
+| [Autodocs](../writing-docs/autodocs.md) | | β
| β
| β
| β
| β
|
+| [Doc Blocks - ArgTypes](../api/doc-block-argtypes.md) | β
| β
| β
| β
| β
| β
|
+| [Doc Blocks - Canvas](../api/doc-block-canvas.md) | β
| β
| β
| β
| β
| β
|
+| [Doc Blocks - ColorPalette](../api/doc-block-colorpalette.md) | β
| β
| β
| β
| β
| β
|
+| [Doc Blocks - Controls](../api/doc-block-controls.md) | β
| β
| β
| β
| β
| β
|
+| [Doc Blocks - Description](../api/doc-block-description.md) | β
| β
| β
| β
| β
| β
|
+| [Doc Blocks - IconGallery](../api/doc-block-icongallery.md) | β
| β
| β
| β
| β
| β
|
+| [Doc Blocks - Markdown](../api/doc-block-markdown.md) | β
| β
| β
| β
| β
| β
|
+| [Doc Blocks - Meta](../api/doc-block-meta.md) | β
| β
| β
| β
| β
| β
|
+| [Doc Blocks - Primary](../api/doc-block-primary.md) | β
| β
| β
| β
| β
| β
|
+| [Doc Blocks - Source](../api/doc-block-source.md) | β
| β
| β
| β
| β
| β
|
+| [Doc Blocks - Story](../api/doc-block-story.md) | β
| β
| β
| β
| β
| β
|
+| [Doc Blocks - Stories](../api/doc-block-stories.md) | β
| β
| β
| β
| β
| β
|
+| [Doc Blocks - Subtitle](../api/doc-block-subtitle.md) | β
| β
| β
| β
| β
| β
|
+| [Doc Blocks - Title](../api/doc-block-title.md) | β
| β
| β
| β
| β
| β
|
+| [Doc Blocks - Typeset](../api/doc-block-typeset.md) | β
| β
| β
| β
| β
| β
|
+| [Doc Blocks - Unstyled](../api/doc-block-unstyled.md) | β
| β
| β
| β
| β
| β
|
+| [Doc Blocks - UseOf](../api/doc-block-useof.md) | β
| β
| β
| β
| β
| β
|
+| Inline stories | | β
| β
| | | |
+
+## Deprecated
+
+To align the Storybook ecosystem with the current state of frontend development, the following features and addons are now deprecated, no longer maintained, and will be removed in future versions of Storybook
+
+| Feature | Status |
+| -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| [Knobs](https://github.com/storybookjs/addon-knobs) | The Knobs addon was officially deprecated with the release of Storybook 6.3 and is no longer actively maintained. We recommend using the [controls](../essentials/controls.md) instead. |
+| [Storyshots](../writing-tests/snapshot-testing.md) | The Storyshots addon was officially deprecated with the release of Storybook 7.6 and is no longer actively maintained. See the [migration guide](../writing-tests/storyshots-migration-guide.md) for the available alternatives. |
+| [`StoriesOf`](https://github.com/storybookjs/storybook/blob/next/code/lib/preview-api/docs/storiesOf.md) | The `storiesOf` API was officially deprecated with the release of Storybook 7.5 and is no longer actively maintained. We recommend using the [CSF API](../api/csf.md) instead for writing stories.
See the [migration guide](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#storystorev6-and-storiesof-is-deprecated) for more information. |
diff --git a/docs/essentials/auto-generated-controls/angular.mdx b/docs/essentials/auto-generated-controls/angular.mdx
deleted file mode 100644
index 22417afdbb6..00000000000
--- a/docs/essentials/auto-generated-controls/angular.mdx
+++ /dev/null
@@ -1,68 +0,0 @@
-To use auto-detected controls with Angular, you must fill in the `component` field in your story metadata:
-
-```ts
-import { Button } from './button.component';
-
-export default {
- component: Button,
-};
-```
-
-Storybook uses this to auto-generate the `ArgTypes` for your component using [Compodoc](https://compodoc.app/). It supports `inputs`, `outputs`, `properties`, `methods`, `view/content child/children` as first class prop types.
-
-## Automatic Compodoc setup
-
-During `sb init`, you will be asked, whether you want to setup Compodoc for your project. Just answer the question with Yes. Compodoc is then ready to use!
-
-## Manual Compodoc setup
-
-You'll need to register Compodoc's `documentation.json` file in `.storybook/preview.ts`:
-
-```js
-import { setCompodocJson } from '@storybook/addon-docs/angular';
-import docJson from '../documentation.json';
-
-setCompodocJson(docJson);
-```
-
-Finally, to set up compodoc, you'll first need to install Compodoc:
-
-```sh
-yarn add -D @compodoc/compodoc
-```
-
-Then you'll need to configure Compodoc to generate a `documentation.json` file. Adding the following snippet to your `projects..architect.` in the `angular.json` creates a metadata file `./documentation.json` each time you run storybook:
-
-```jsonc
-// angular.json
-{
- "projects": {
- "your-project": {
- "architect": {
- "storybook": {
- ...,
- "compodoc": true,
- "compodocArgs": [
- "-e",
- "json",
- "-d",
- "." // the root folder of your project
- ],
- },
- "build-storybook": {
- ...,
- "compodoc": true,
- "compodocArgs": [
- "-e",
- "json",
- "-d",
- "." // the root folder of your project
- ],
- }
- }
- }
- }
-}
-```
-
-Unfortunately, it's not currently possible to update this dynamically as you edit your components, but [there's an open issue](https://github.com/storybookjs/storybook/issues/8672) to support this with improvements to Compodoc.
diff --git a/docs/essentials/auto-generated-controls/ember.mdx b/docs/essentials/auto-generated-controls/ember.mdx
deleted file mode 100644
index 1696095183a..00000000000
--- a/docs/essentials/auto-generated-controls/ember.mdx
+++ /dev/null
@@ -1,29 +0,0 @@
-Storybook for Ember relies on [@storybook/ember-cli-storybook addon](https://github.com/storybookjs/ember-cli-storybook), to extract documentation comments from your component source files. If you're using Storybook with Ember, you should already have this addon installed, and you will just need to enable it by adding the following config block in your `ember-cli-build.js` file:
-
-```js
-let app = new EmberApp(defaults, {
- '@storybook/ember-cli-storybook': {
- enableAddonDocsIntegration: true,
- },
-});
-```
-
-Now, running the ember-cli server will generate a JSON documentation file at `/dist/storybook-docgen/index.json`. Since generation of this file is tied into the ember-cli build, it will get regenerated every time component files are saved. For details on documenting your components, check out the examples in the addon that powers the generation [ember-cli-addon-docs-yuidoc](https://github.com/ember-learn/ember-cli-addon-docs-yuidoc#documenting-components).
-
-Storybook uses this file to auto-generate the `ArgTypes` for your component based on docgen information created by [ember-cli-addon-docs-yuidoc](https://github.com/ember-learn/ember-cli-addon-docs-yuidoc#documenting-components).
-
-You'll need to register that in `.storybook/preview.js`:
-
-```js
-import { setJSONDoc } from '@storybook/addon-docs/ember';
-import docJson from '../dist/storybook-docgen/index.json';
-setJSONDoc(docJson);
-```
-
-Finally, to use auto-detected controls with Ember, you must fill in the `component` field in your story metadata:
-
-```ts
-export default {
- component: 'button', // name of your button component from docgen-json file (index.js)
-};
-```
diff --git a/docs/essentials/auto-generated-controls/fallback.mdx b/docs/essentials/auto-generated-controls/fallback.mdx
deleted file mode 100644
index 9842a53b859..00000000000
--- a/docs/essentials/auto-generated-controls/fallback.mdx
+++ /dev/null
@@ -1,3 +0,0 @@
-Controls argument autodetection doesn't currently work with your framework.
-
-To use Controls, you'll need to [define the `ArgTypes` manually](#fully-custom-args)
diff --git a/docs/essentials/auto-generated-controls/react.mdx b/docs/essentials/auto-generated-controls/react.mdx
deleted file mode 100644
index 29bb07ede5c..00000000000
--- a/docs/essentials/auto-generated-controls/react.mdx
+++ /dev/null
@@ -1,14 +0,0 @@
-To use auto-detected controls with React, you must fill in the `component` field in your story metadata:
-
-```js
-// Button.stories.js|jsx|mjs|ts|tsx
-
-import { Button } from './Button';
-
-export default {
- component: Button,
-};
-```
-
-
-Storybook uses this to auto-generate the `ArgTypes` for your component based on either `PropTypes` (using [`react-docgen`](https://github.com/reactjs/react-docgen)) or `TypeScript` types (using [`react-docgen-typescript`](https://github.com/styleguidist/react-docgen-typescript)).
diff --git a/docs/essentials/auto-generated-controls/vue.mdx b/docs/essentials/auto-generated-controls/vue.mdx
deleted file mode 100644
index 740ea7bec82..00000000000
--- a/docs/essentials/auto-generated-controls/vue.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
-To use auto-detected controls with Vue, you must fill in the `component` field in your story metadata:
-
-```js
-import { Button } from './Button.vue';
-
-export default {
- component: Button,
-};
-```
-
-Storybook uses this to auto-generate the `ArgTypes` for your component using [`vue-docgen-api`](https://github.com/vue-styleguidist/vue-styleguidist/tree/dev/packages/vue-docgen-api). It supports `props`, `events`, and `slots` as first class prop types.
diff --git a/docs/essentials/auto-generated-controls/web-components.mdx b/docs/essentials/auto-generated-controls/web-components.mdx
deleted file mode 100644
index 473b67f07b2..00000000000
--- a/docs/essentials/auto-generated-controls/web-components.mdx
+++ /dev/null
@@ -1,20 +0,0 @@
-To use auto-detected controls with Web components, you must fill in the `component` field in your story metadata:
-
-```js
-export default {
- component: 'button',
-};
-```
-
-Storybook uses this to auto-generate the `ArgTypes` for your component using your [custom-elements.json](https://github.com/webcomponents/custom-elements-json) file.
-
-You'll need to register that in `.storybook/preview.js`:
-
-```js
-import { setCustomElementsManifest } from '@storybook/web-components';
-import customElements from '../custom-elements.json';
-
-setCustomElementsManifest(customElements);
-```
-
-You can generate a `custom-elements.json` using [@custom-elements-manifest/analyzer](https://github.com/open-wc/custom-elements-manifest). If you're using the pre-v1.0.0 version of `custom-elements.json` you can use either [web-component-analyzer](https://github.com/runem/web-component-analyzer).
diff --git a/docs/essentials/controls.md b/docs/essentials/controls.md
index efff7222f5b..5d86420c4ab 100644
--- a/docs/essentials/controls.md
+++ b/docs/essentials/controls.md
@@ -29,23 +29,83 @@ If you have stories in the older pre-Storybook 6 style, check the [args & contro
## Choosing the control type
-By default, Storybook will choose a control for each arg based on the initial value of the arg. It works well with certain types of args, such as boolean values or free-text strings, but in other cases, you want a more restricted control.
+
+
+By default, Storybook will choose a control for each arg based on its initial value. This will work well with specific arg types (e.g., `boolean` or `string`). To enable them, add the `component` annotation to the default export of your story file, and it will be used to infer the controls and auto-generate the matching [`argTypes`](../api/arg-types.md) for your component using [Compodoc](https://compodoc.app/) if you opt-in to use it, including first-class support for Angular's `inputs`, `outputs`, `properties`, `methods` and `view/content child/children`.
+
+
+
+
+
+
+
+
+
+
+
+By default, Storybook will choose a control for each arg based on its initial value. This will work well with specific arg types (e.g., `boolean` or `string`). To enable them, add the `component` annotation to the default export of your story file and it will be used to infer the controls and auto-generate the matching [`argTypes`](../api/arg-types.md) for your component using [`react-docgen`](https://github.com/reactjs/react-docgen) or [`react-docgen-typescript`](https://github.com/styleguidist/react-docgen-typescript) for TypeScript.
+
+
+
+
+
+
+
+
+
+
+
+By default, Storybook will choose a control for each arg based on its initial value. This will work well with specific arg types (e.g., `boolean` or `string`). To enable them, add the `component` annotation to the default export of your story file, and it will be used to infer the controls and auto-generate the matching [`argTypes`](../api/arg-types.md) for your component using [`vue-docgen-api`](https://github.com/vue-styleguidist/vue-styleguidist/tree/dev/packages/vue-docgen-api), including first-class support for Vue's `props`, `events`, and `slots`.
-
+
+
+
+
+By default, Storybook will choose a control for each arg based on its initial value. This will work well with specific arg types (e.g., `boolean` or `string`). To enable them, add the `component` annotation to the default export of your story file, and it will be used to infer the controls and auto-generate the matching [`argTypes`](../api/arg-types.md) for your component provided by the framework you've chosen to use.
+
+
+
+
+
+
+
+If you're using a framework that doesn't support this feature, you'll need to define the `argTypes` for your component [manually](#fully-custom-args).
+
+
+
+
+
For instance, suppose you have a `variant` arg on your story that should be `primary` or `secondary`:
@@ -70,7 +130,7 @@ By default, Storybook will render a free text input for the `variant` arg:
It works as long as you type a valid string into the auto-generated text control. Still, it's not the best UI for our scenario, given that the component only accepts `primary` or `secondary` as variants. Letβs replace it with Storybookβs radio component.
-We can specify which controls get used by declaring a custom [argType](../api/arg-types.md) for the `variant` property. ArgTypes encode basic metadata for args, such as name, description, defaultValue for an arg. These get automatically filled in by Storybook Docs.
+We can specify which controls get used by declaring a custom [argType](../api/arg-types.md) for the `variant` property. ArgTypes encode basic metadata for args, such as name, description, and defaultValue for an arg. These get automatically filled in by Storybook Docs.
`ArgTypes` can also contain arbitrary annotations, which the user can override. Since `variant` is a property of the component, let's put that annotation on the default export.
@@ -91,7 +151,7 @@ We can specify which controls get used by declaring a custom [argType](../api/ar
-ArgTypes are a powerful feature that can be used to customize the controls for your stories. See the documentation about [customizing controls](#annotation) with `argTypes` annotation for more information.
+ArgTypes are a powerful feature that can be used to customize the controls for your stories. For more information, see the documentation about [customizing controls](#annotation) with `argTypes` annotation.
@@ -101,14 +161,14 @@ This replaces the input with a radio group for a more intuitive experience.
## Custom control type matchers
-Controls can automatically be inferred from arg's name with [regex](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp), but currently only for the color picker and date picker controls. If you've used the Storybook CLI to setup your project it should have automatically created the following defaults in `.storybook/preview.js`:
+Controls can automatically be inferred from arg's name with [regex](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp), but currently only for the color picker and date picker controls. If you've used the Storybook CLI to setup your project, it should have automatically created the following defaults in `.storybook/preview.js`:
| Control | Default regex | Description |
| :-------: | :--------------------------------------: | :-------------------------------------------------------: |
| **color** | /(background|color)$/i
| Will display a color picker UI for the args that match it |
| **date** | `/Date$/` | Will display a date picker UI for the args that match it |
-If you haven't used the CLI to setup the configuration, or if you want to define your own patterns, use the `matchers` property in the `controls` parameter:
+If you haven't used the CLI to set the configuration, or if you want to define your patterns, use the `matchers` property in the `controls` parameter:
@@ -121,13 +181,13 @@ If you haven't used the CLI to setup the configuration, or if you want to define
-
+
## Fully custom args
-Until now, we only used auto-generated controls based on the component we're writing stories for. If we are writing [complex stories](../writing-stories/stories-for-multiple-components.md), we may want to add controls for args that arenβt part of the component. For example, here's how you could use a `footer` arg to populate a child component:
+Until now, we only used auto-generated controls based on the component for which we're writing stories. If we are writing [complex stories](../writing-stories/stories-for-multiple-components.md), we may want to add controls for args that arenβt part of the component. For example, here's how you could use a `footer` arg to populate a child component:
@@ -189,7 +249,7 @@ One way to deal with this is to use primitive values (e.g., strings) as arg valu
-Unless you need the flexibility of a function, an easier way to map primitives to complex values before rendering is to define a `mapping`, additionally, you can specify `control.labels` to configure custom labels for your checkbox, radio, or select input.
+Unless you need the flexibility of a function, an easier way to map primitives to complex values before rendering is to define a `mapping`; additionally, you can specify `control.labels` to configure custom labels for your checkbox, radio, or select input.
@@ -267,9 +327,9 @@ Numeric data types will default to a `number` control unless additional configur
Controls supports the following configuration [parameters](../writing-stories/parameters.md), either globally or on a per-story basis:
-## Show full documentation for each property
+#### Show full documentation for each property
-Since Controls is built on the same engine as Storybook Docs, it can also show property documentation alongside your controls using the expanded parameter (defaults to false). This means you embed a complete [`Controls`](../api/doc-block-controls.md) doc block in the controls panel. The description and default value rendering can be [customized](#fully-custom-args) in the same way as the doc block.
+Since Controls is built on the same engine as Storybook Docs, it can also show property documentation alongside your controls using the expanded parameter (defaults to false). This means you embed a complete [`Controls`](../api/doc-block-controls.md) doc block in the controls panel. The description and default value rendering can be [customized](#fully-custom-args) like the doc block.
To enable expanded mode globally, add the following to [`.storybook/preview.js`](../configure/index.md#configure-story-rendering):
@@ -284,11 +344,11 @@ To enable expanded mode globally, add the following to [`.storybook/preview.js`]
-And here's what the resulting UI looks like:
+Here's what the resulting UI looks like:
![Controls addon expanded](./addon-controls-expanded.png)
-### Specify initial preset color swatches
+#### Specify initial preset color swatches
For `color` controls, you can specify an array of `presetColors`, either on the `control` in `argTypes`, or as a parameter under the `controls` namespace:
@@ -305,11 +365,75 @@ For `color` controls, you can specify an array of `presetColors`, either on the
Color presets can be defined as an object with `color` and `title` or a simple CSS color string. These will then be available as swatches in the color picker. When you hover over the color swatch, you'll be able to see its title. It will default to the nearest CSS color name if none is specified.
+#### Filtering controls
+
+In specific cases, you may be required to display only a limited number of controls in the controls panel or all except a particular set.
+
+To make this possible, you can use optional `include` and `exclude` configuration fields in the `controls` parameter, which you can define as an array of strings or a regular expression.
+
+Consider the following story snippets:
+
+
+
+
+
+
+
+#### Sorting controls
+
+By default, controls are unsorted and use whatever order the args data is processed in (`none`). Additionally, you can sort them alphabetically by the arg's name (`alpha`) or with the required args first (`requiredFirst`).
+
+Consider the following snippet to force required args first:
+
+
+
+
+
+
+
+#### Hide NoControls warning
+
+If you don't plan to handle the control args inside your story, you can remove the warning with:
+
+
+
+
+
+
+
### Disable controls for specific properties
Aside from the features already documented here, Controls can also be disabled for individual properties.
-Suppose you want to disable Controls for a property called `foo` in a component's story. The following example illustrates how:
+Suppose you want to turn off Controls for a property called `foo` in a component's story. The following example illustrates how:
@@ -334,7 +458,7 @@ Resulting in the following change in Storybook UI:
/>
-The previous example also removed the prop documentation from the table. In some cases, this is fine. However, sometimes you might want to render the prop documentation, but without a control. The following example illustrates how:
+The previous example also removed the prop documentation from the table. In some cases, this is fine. However, sometimes you might want to render the prop documentation without a control. The following example illustrates how:
@@ -352,7 +476,7 @@ The previous example also removed the prop documentation from the table. In some
-π‘ As with other Storybook properties, such as [decorators](../writing-stories/decorators.md), you can apply the same pattern at a story level for more granular cases.
+As with other Storybook properties, such as [decorators](../writing-stories/decorators.md), you can apply the same pattern at a story level for more granular cases.
@@ -360,7 +484,7 @@ The previous example also removed the prop documentation from the table. In some
In some cases, it's useful to be able to conditionally exclude a control based on the value of another control. Controls supports basic versions of these use cases with the `if`, which can take a simple query object to determine whether to include the control.
-Consider a collection of "advanced" settings that are only visible when the user toggles an "advanced" toggle.
+Consider a collection of "advanced" settings only visible when the user toggles an "advanced" toggle.
@@ -410,66 +534,116 @@ It may also contain at most one of the following operators:
If no operator is provided, that is equivalent to `{ truthy: true }`.
-## Hide NoControls warning
+
+
+## Troubleshooting
+
+### Controls are not automatically generated for my component
-If you don't plan to handle the control args inside your Story, you can remove the warning with:
+Out of the box, Storybook will try to infer the required argTypes and associated controls for your stories based on the component's definition and the initial value of the args. However, in some cases, this may not be enough, and you may need to provide additional information to Storybook. To solve this, you can opt-in to use [Compodoc](https://compodoc.app/), a documentation generator for Angular applications that can extract the metadata of your components and generate the required argTypes and controls for your stories.
+
+Run the following command to install the tooling.
-## Filtering controls
+Update your `angular.json` file to include the following configuration to include it in the Storybook's inbuilt builder configuration.
-In specific cases, you may require to display only a limited number of controls in the controls panel, or all of them except a particular set.
+
-To make this possible, you can use optional `include` and `exclude` configuration fields in the `controls` parameter, which you can define as an array of strings, or as a regular expression.
+
-Consider the following story snippets:
+
+
+
+Finally, update your `.storybook/preview.ts` file to include the following configuration to import the metadata generated by Compodoc and use it to generate the controls and argTypes for your stories.
+
-## Sorting controls
+
-By default, controls are unsorted and use whatever order the args data is processed in (`none`). Additionally, you can sort them alphabetically by the arg's name (`alpha`) or with the required args first (`requiredFirst`).
+
-Consider the following snippet to force required args first:
+## Troubleshooting
+
+### Controls are not automatically generated for my component
+
+Out of the box, Storybook will try to infer the required argTypes and associated controls for your stories based on the component's definition and the initial value of the args. However, in some cases, this may not be enough, and you may need to provide additional information to Storybook. To solve this, you can generate a [`custom-elements.json`](https://github.com/webcomponents/custom-elements-json) file with [`@custom-elements-manifest/analyzer`](https://github.com/open-wc/custom-elements-manifest) if you're using the `pre-v1.0.0` version of the elements file or [`@custom-elements-manifest/analyzer`](https://github.com/open-wc/custom-elements-manifest) for newer versions and configure it in your Storybook UI configuration file (i.e., `.storybook/preview.js|ts`) to enable it.
+
+
+
+
+
+
+## Troubleshooting
+
+### Controls are not automatically generated for my component
+
+Out of the box, Storybook will try to infer the required argTypes and associated controls for your stories based on the metadata provided by the [`@storybook/ember-cli-storybook`](https://github.com/storybookjs/ember-cli-storybook) addon. However, in some cases, this may not be enough, and you may need to customize your project configuration to provide additional information to Storybook to generate the required argTypes and controls for your stories.
+
+Update your `ember-cli-build.js` configuration file to include the addon.
+
+
+
+
+
+
+
+
+Restart your application to generate the metadata file (i.e., `storybook-docgen/index.json`) and update your `.storybook/preview.js` file to include it, which will be used to create the controls and argTypes for your stories.
+
+
+
+
+
+
+
+
+
+
+Enabling this feature will generate a `storybook-docgen/index.json` automatically with each build. For more information on how the metadata is generated, refer to [documentation](https://github.com/storybookjs/storybook/tree/next/code/frameworks/ember) for the Ember framework.
+
+
+
+
diff --git a/docs/get-started/install.md b/docs/get-started/install.md
index 977a1e122c0..44b2cf7aad9 100644
--- a/docs/get-started/install.md
+++ b/docs/get-started/install.md
@@ -57,6 +57,12 @@ Storybook comes with a built-in development server featuring everything you need
+
+
+Storybook collects completely anonymous data to help us improve user experience. Participation is optional, and you may [opt-out](../configure/telemetry.md#how-to-opt-out) if you'd not like to share any information.
+
+
+
![Storybook welcome screen](./example-welcome.png)
There are some noteworthy items here:
@@ -66,36 +72,235 @@ There are some noteworthy items here:
- A few example stories to get you started.
-Troubleshooting
+Troubleshooting
+
+#### Run Storybook with other package managers
-Below are some of the most common installation issues and instructions on how to solve them.
+The Storybook CLI includes support for the industry's popular package managers (e.g., [Yarn](https://yarnpkg.com/), [npm](https://www.npmjs.com/), and [pnpm](https://pnpm.io/)) automatically detecting the one you are using when you initialize Storybook. However, if you want to use a specific package manager as the default, add the `--package-manager` flag to the installation command. For example:
-
-
+#### The CLI doesn't detect my framework
+
+If you're working with a custom environment set up or need set up Storybook manually, you can use the `--type` flag to specify the framework you need to use. Listed below are the supported frameworks and examples of how to use them:
+
+| Framework | Type |
+| -------------- | ---------------- |
+| Angular | `angular` |
+| Ember | `ember` |
+| HTML | `html` |
+| Next.js | `nextjs` |
+| Preact | `preact` |
+| Qwik | `qwik` |
+| React | `react` |
+| Server | `server` |
+| Solid | `solid` |
+| Svelte | `svelte` |
+| Vue 2 | `vue` |
+| Vue 3 | `vue3` |
+| Web Components | `web-components` |
-Storybook collects completely anonymous data to help us improve user experience. Participation is optional, and you may [opt-out](../configure/telemetry.md#how-to-opt-out) if you'd not like to share any information.
+
+
+
+
+
+
+#### Run Storybook with Webpack 4
+
+If you previously installed Storybook in a project that uses Webpack 4, it will no longer work. This is because Storybook now uses Webpack 5 by default. To solve this issue, we recommend you upgrade your project to Webpack 5 and then run the following command to migrate your project to the latest version of Storybook:
+
+
+
+
+
+
+
+
+
+#### Storybook doesn't work with my Angular project using the Angular CLI
+
+Out of the box, adding Storybook to an Angular project using the Angular CLI requires you to run the installation command from the root of the project or, if you're working with a monorepo environment, from the directory where the Angular configuration file (i.e., `angular.json`) is located as it will be used to set up the builder configuration necessary to run Storybook. However, if you need, you can extend the builder configuration to customize Storybook's behavior. Listed below are the supported options:
+
+| Configuration element | Description |
+| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `"browserTarget"` | Build target to be served using the following format.
`"example-project:builder:config"` |
+| `"tsConfig"` | Location of the TypeScript configuration file, relative to the current workspace.
`"tsConfig": "./tsconfig.json"`. |
+| `"port"` | Port used by Storybook.
`"port": 6006` |
+| `"host"` | Set up a custom host for Storybook.
`"host": "http://my-custom-host"` |
+| `"configDir"` | Storybook configuration directory location.
`"configDir": ".storybook"` |
+| `"https"` | Starts Storybook with HTTPS enabled.
`"https": true`
Requires custom certificate information. |
+| `"sslCa"` | Provides an SSL certificate authority.
`"sslCa": "your-custom-certificate-authority"`
Optional usage with `"https"` |
+| `"sslCert"` | Provides an SSL certificate.
`"sslCert": "your-custom-certificate"`
Required for `https` |
+| `"sslKey"` | Provides an SSL key to serve Storybook.
`"sslKey": "your-ssl-key"` |
+| `"smokeTest"` | Exit Storybook after successful start.
`"smokeTest": true` |
+| `"ci"` | Starts Storybook in CI mode (skips interactive prompts and will not open browser window).
`"ci": true` |
+| `"quiet"` | Filters Storybook verbose build output.
`"quiet": true` |
+| `"docs"` | Starts Storybook in [documentation mode](../writing-docs/build-documentation.md#preview-storybooks-documentation).
`"docs": true` |
+| `"styles"` | Provide the location of the [application's styles](../configure/styling-and-css.md#importing-css-files) to be used with Storybook.
`"styles": ["src/styles.css", "src/styles.scss"]`
|
+| `"stylePreprocessorOptions"` | Provides further customization for style preprocessors resolved to the workspace root.
`"stylePreprocessorOptions": { "includePaths": ["src/styles"] }` |
+
+
+
+
+
+#### The CLI doesn't support my Ember version
+
+The Ember framework relies on an auxiliary package named [`@storybook/ember-cli-storybook`](https://www.npmjs.com/package/@storybook/ember-cli-storybook) to help you set up Storybook in your project. During the installation process you might run into the following warning message in your terminal:
+
+```shell
+The ember generate entity-name command requires an entity name to be specified.
+For more details, use ember help.
+```
+
+It may be the case that you're using an outdated version of the package and you need to update it to the latest version to solve this issue.
+
+
+
+
+
+#### Writing native Svelte stories
+
+Storybook provides a Svelte addon maintained by the community, enabling you to write stories for your Svelte components using the template syntax. You'll need to take some additional steps to enable this feature.
+
+Run the following command to install the addon.
+
+
+
+
+
+
+
+Update your Storybook configuration file (i.e., `.storybook/main.js|ts`) to include it.
+
+
+
+
+
+
+
+
+
+The community actively maintains the Svelte CSF addon but still lacks some features currently available in the official Storybook Svelte framework support. For more information, see [addon's documentation](https://github.com/storybookjs/addon-svelte-csf).
-If all else fails, try asking for [help](https://storybook.js.org/community#support)
+
+
+#### The installation process seems flaky and keeps failing
+
+If you're still running into some issues during the installation process, we encourage you to check out the following resources:
+
+
+
+- [Storybook's Angular README](https://github.com/storybookjs/storybook/tree/next/code/frameworks/angular) for more information on how to set up Storybook in your Angular project.
+- [Storybook's help documentation](https://storybook.js.org/community#support) to contact the community and ask for help.
+
+
+
+
+
+- [Storybook's Ember README](https://github.com/storybookjs/storybook/tree/next/code/frameworks/ember) for more information on how to set up Storybook in your Ember project.
+- [Storybook's help documentation](https://storybook.js.org/community#support) to contact the community and ask for help.
+
+
+
+
+
+- [Storybook's HTML Webpack README](https://github.com/storybookjs/storybook/tree/next/code/frameworks/html-webpack5) for more information on how to set up Storybook in your HTML project with Webpack 5.
+- [Storybook's HTML Vite README](https://github.com/storybookjs/storybook/tree/next/code/frameworks/html-vite) for more information on how to set up Storybook in your HTML project with Vite.
+- [Storybook's help documentation](https://storybook.js.org/community#support) to contact the community and ask for help.
+
+
+
+
+
+- [Storybook's Preact Webpack README](https://github.com/storybookjs/storybook/tree/next/code/frameworks/preact-webpack5) for more information on how to set up Storybook in your Preact project with Webpack 5.
+- [Storybook's Preact Vite README](https://github.com/storybookjs/storybook/tree/next/code/frameworks/preact-vite) for more information on how to set up Storybook in your Preact project with Vite.
+- [Storybook's help documentation](https://storybook.js.org/community#support) to contact the community and ask for help.
+
+
+
+
+
+- [Storybook's Qwik README](https://github.com/literalpie/storybook-framework-qwik) for more information on how to set up Storybook in your Qwik project.
+- [Storybook's help documentation](https://storybook.js.org/community#support) to contact the community and ask for help.
+
+
+
+
+
+- [Storybook's React Webpack README](https://github.com/storybookjs/storybook/tree/next/code/frameworks/react-webpack5) for more information on how to set up Storybook in your React project with Webpack 5.
+- [Storybook's React Vite README](https://github.com/storybookjs/storybook/tree/next/code/frameworks/react-vite) for more information on how to set up Storybook in your React project with Vite.
+- [Storybook's help documentation](https://storybook.js.org/community#support) to contact the community and ask for help.
+
+
+
+
+
+- [Storybook's SolidJS README](https://github.com/storybookjs/solidjs) for more information on how to set up Storybook in your SolidJS project.
+- [Storybook's help documentation](https://storybook.js.org/community#support) to contact the community and ask for help.
+
+
+
+
+
+- [Storybook's SvelteKit README](https://github.com/storybookjs/storybook/tree/next/code/frameworks/sveltekit) for more information on how to set up Storybook in your SvelteKit project.
+- [Storybook's Svelte Webpack README](https://github.com/storybookjs/storybook/tree/next/code/frameworks/svelte-webpack5) for more information on how to set up Storybook in your Svelte project with Webpack 5.
+- [Storybook's help documentation](https://storybook.js.org/community#support) to contact the community and ask for help.
+
+
+
+
+
+- [Storybook's Vue 2 Webpack README](https://github.com/storybookjs/storybook/tree/main/code/frameworks/vue-webpack5) for more information on how to set up Storybook in your Vue 2 project with Webpack 5.
+- [Storybook's Vue 2 Vite README](https://github.com/storybookjs/storybook/tree/main/code/frameworks/vue-vite) for more information on how to set up Storybook in your Vue 2 project with Vite.
+- [Storybook's Vue 3 Webpack README](https://github.com/storybookjs/storybook/tree/next/code/frameworks/vue3-webpack5) for more information on how to set up Storybook in your Vue 3 project with Webpack 5.
+- [Storybook's Vue 3 Vite README](https://github.com/storybookjs/storybook/tree/next/code/frameworks/vue3-vite) for more information on how to set up Storybook in your Vue 3 project with Vite.
+- [Storybook's help documentation](https://storybook.js.org/community#support) to contact the community and ask for help.
+
+
+
+
+
+- [Storybook's Web Components Webpack README](https://github.com/storybookjs/storybook/tree/next/code/frameworks/web-components-webpack55) for more information on how to set up Storybook in your Web Components project with Webpack 5.
+- [Storybook's Web Components Vite README](https://github.com/storybookjs/storybook/tree/next/code/frameworks/web-components-vite) for more information on how to set up Storybook in your Web Components project with Vite.
+- [Storybook's help documentation](https://storybook.js.org/community#support) to contact the community and ask for help.
+
+
diff --git a/docs/get-started/installation-command-section/solid.mdx b/docs/get-started/installation-command-section/solid.mdx
deleted file mode 100644
index 41e5c040cc9..00000000000
--- a/docs/get-started/installation-command-section/solid.mdx
+++ /dev/null
@@ -1,15 +0,0 @@
-Use the Storybook CLI to install it in a single command. Run this inside your _existing projectβs_ root directory:
-
-- With npm:
-
-```shell
-npx storybook init
-```
-
-- With pnpm:
-
-```shell
-pnpm dlx storybook init
-```
-
-If you run into issues with the installation, check the [Troubleshooting section](#troubleshooting) below for guidance on how to solve it.
diff --git a/docs/get-started/installation-problems/angular.mdx b/docs/get-started/installation-problems/angular.mdx
deleted file mode 100644
index 96b96b440a7..00000000000
--- a/docs/get-started/installation-problems/angular.mdx
+++ /dev/null
@@ -1,45 +0,0 @@
-- Add the `--type angular` flag to the installation command to set up Storybook manually:
-
- ```shell
- npx storybook@latest init --type angular
- ```
-
-- Storybook's CLI provides support for [Yarn](https://yarnpkg.com/), [npm](https://www.npmjs.com/), and [pnpm](https://pnpm.io/) package managers. If you have Yarn installed in your environment but prefer to use another as your default package manager add the `--package-manager` flag to your installation command. For example:
-
- ```shell
- npx storybook@latest init --package-manager=npm
- ```
-
-- Storybook supports Webpack 5 out of the box. If you're upgrading from a previous version, run the following command to enable it:
-
- ```shell
- npx storybook@latest automigrate
- ```
-
- Check the [Migration Guide](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#automigrate) for more information on how to set up Webpack 5.
-
-- If you're using the custom Storybook builder, Storybook will not automatically open a browser window or tab. It's is a known issue that we plan to fix in future releases.
-
-- When adding Storybook to your Angular project, check if you're running the install command from the root directory. Or in a monorepo environment inside the directory where the `angular.json` file is located.
-
-- If you need further customization to the Storybook builder configuration, you can use the following table as a reference:
-
-| Configuration element | Description |
-| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| `"browserTarget"` | Build target to be served using the following format.
`"example-project:builder:config"` |
-| `"tsConfig"` | Location of the TypeScript configuration file, relative to the current workspace.
`"tsConfig": "./tsconfig.json"`. |
-| `"port"` | Port used by Storybook.
`"port": 6006` |
-| `"host"` | Set up a custom host for Storybook.
`"host": "http://my-custom-host"` |
-| `"configDir"` | Storybook configuration directory location.
`"configDir": ".storybook"` |
-| `"https"` | Starts Storybook with HTTPS enabled.
`"https": true`
Requires custom certificate information. |
-| `"sslCa"` | Provides an SSL certificate authority.
`"sslCa": "your-custom-certificate-authority"`
Optional usage with `"https"` |
-| `"sslCert"` | Provides an SSL certificate.
`"sslCert": "your-custom-certificate"`
Required for `https` |
-| `"sslKey"` | Provides an SSL key to serve Storybook.
`"sslKey": "your-ssl-key"` |
-| `"smokeTest"` | Exit Storybook after successful start.
`"smokeTest": true` |
-| `"ci"` | Starts Storybook in CI mode (skips interactive prompts and will not open browser window).
`"ci": true` |
-| `"quiet"` | Filters Storybook verbose build output.
`"quiet": true` |
-| `"docs"` | Starts Storybook in [documentation mode](../writing-docs/build-documentation.md#preview-storybooks-documentation).
`"docs": true` |
-| `"styles"` | Provide the location of the [application's styles](../configure/styling-and-css.md#importing-css-files) to be used with Storybook.
`"styles": ["src/styles.css", "src/styles.scss"]`
|
-| `"stylePreprocessorOptions"` | Provides further customization for style preprocessors resolved to the workspace root.
`"stylePreprocessorOptions": { "includePaths": ["src/styles"] }` |
-
-- For other installation issues, check the [Angular README](https://github.com/storybookjs/storybook/tree/next/code/frameworks/angular) for additional instructions.
diff --git a/docs/get-started/installation-problems/ember.mdx b/docs/get-started/installation-problems/ember.mdx
deleted file mode 100644
index a396c27891b..00000000000
--- a/docs/get-started/installation-problems/ember.mdx
+++ /dev/null
@@ -1,23 +0,0 @@
-- Add the `--type ember` flag to the installation command to set up Storybook manually:
-
- ```shell
- npx storybook@latest init --type ember
- ```
-
-- During the install process, if you get the following warning message:
-
- ```shell
- The ember generate entity-name command requires an entity name to be specified.
-
- For more details, use ember help.
- ```
-
-Update the [`@storybook/ember-cli-storybook`](https://www.npmjs.com/package/@storybook/ember-cli-storybook) package to the latest version to fix it.
-
-- Storybook's CLI provides support for [Yarn](https://yarnpkg.com/), [npm](https://www.npmjs.com/), and [pnpm](https://pnpm.io/) package managers. If you have Yarn installed in your environment but prefer to use another as your default package manager add the `--package-manager` flag to your installation command. For example:
-
- ```shell
- npx storybook@latest init --package-manager=npm
- ```
-
-- For other installation issues, check the [Ember README](https://github.com/storybookjs/storybook/tree/next/code/frameworks/ember) for additional instructions.
diff --git a/docs/get-started/installation-problems/html.mdx b/docs/get-started/installation-problems/html.mdx
deleted file mode 100644
index f43b72620b1..00000000000
--- a/docs/get-started/installation-problems/html.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
-- Add the `--type html` flag to the installation command to set up Storybook manually:
-
- ```shell
-npx storybook@latest init --type html
- ```
-
-- For other installation issues, check the React README files for additional instructions:
- - [HTML with Webpack]( https://github.com/storybookjs/storybook/tree/next/code/frameworks/html-webpack5)
- - [HTML with Vite](https://github.com/storybookjs/storybook/tree/next/code/frameworks/html-vite)
diff --git a/docs/get-started/installation-problems/preact.mdx b/docs/get-started/installation-problems/preact.mdx
deleted file mode 100644
index 24837b2c1eb..00000000000
--- a/docs/get-started/installation-problems/preact.mdx
+++ /dev/null
@@ -1,13 +0,0 @@
-- Add the `--type preact` flag to the installation command to set up Storybook manually:
-
- ```shell
- npx storybook@latest init --type preact
- ```
-
-- Storybook's CLI provides support for [Yarn](https://yarnpkg.com/), [npm](https://www.npmjs.com/), and [pnpm](https://pnpm.io/) package managers. If you have Yarn installed in your environment but prefer to use another as your default package manager add the `--package-manager` flag to your installation command. For example:
-
- ```shell
- npx storybook@latest init --package-manager=npm
- ```
-
-- For other installation issues, check the [Preact README](https://github.com/storybookjs/storybook/tree/next/code/frameworks/preact-webpack5) for additional instructions.
diff --git a/docs/get-started/installation-problems/qwik.mdx b/docs/get-started/installation-problems/qwik.mdx
deleted file mode 100644
index c6bd50a043b..00000000000
--- a/docs/get-started/installation-problems/qwik.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
-- Add the `--type qwik` flag to the installation command to set up Storybook manually:
-
- ```shell
- npx storybook@latest init --type qwik
- ```
-
-- Storybook's CLI provides support for [Yarn](https://yarnpkg.com/), [npm](https://www.npmjs.com/), and [pnpm](https://pnpm.io/) package managers. If you have Yarn installed in your environment but prefer to use another as your default package manager add the `--package-manager` flag to your installation command. For example:
-
- ```shell
- npx storybook@latest init --package-manager=npm
- ```
diff --git a/docs/get-started/installation-problems/react.mdx b/docs/get-started/installation-problems/react.mdx
deleted file mode 100644
index 30b4643208c..00000000000
--- a/docs/get-started/installation-problems/react.mdx
+++ /dev/null
@@ -1,23 +0,0 @@
-- Add the `--type react` flag to the installation command to set up Storybook manually:
-
- ```shell
- npx storybook@latest init --type react
- ```
-
-- Storybook's CLI provides support for [Yarn](https://yarnpkg.com/), [npm](https://www.npmjs.com/), and [pnpm](https://pnpm.io/) package managers. If you have Yarn installed in your environment but prefer to use another as your default package manager add the `--package-manager` flag to your installation command. For example:
-
- ```shell
- npx storybook@latest init --package-manager=npm
- ```
-
-- Storybook supports Webpack 5 out of the box. If you're upgrading from a previous version, run the following command to enable it:
-
- ```shell
- npx storybook@latest automigrate
- ```
-
-Check the [Migration Guide](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#cra5-upgrade) for more information on how to set up Webpack 5.
-
-- For other installation issues, check the React README files for additional instructions:
- - [React with Webpack](https://github.com/storybookjs/storybook/tree/next/code/frameworks/react-webpack5)
- - [React with Vite](https://github.com/storybookjs/storybook/tree/next/code/frameworks/react-vite)
diff --git a/docs/get-started/installation-problems/solid.mdx b/docs/get-started/installation-problems/solid.mdx
deleted file mode 100644
index 9308bb9b154..00000000000
--- a/docs/get-started/installation-problems/solid.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
-- Add the `--type solid` flag to the installation command to set up Storybook manually:
-
- ```shell
- npx storybook init --type solid
- ```
-
-- Storybook's CLI provides support for [Yarn](https://yarnpkg.com/), [npm](https://www.npmjs.com/), and [pnpm](https://pnpm.io/) package managers. If you have Yarn installed in your environment but prefer to use another as your default package manager add the `--package-manager` flag to your installation command. For example:
-
- ```shell
- npx storybook init --package-manager=npm
- ```
diff --git a/docs/get-started/installation-problems/svelte.mdx b/docs/get-started/installation-problems/svelte.mdx
deleted file mode 100644
index d0bfd5284d6..00000000000
--- a/docs/get-started/installation-problems/svelte.mdx
+++ /dev/null
@@ -1,25 +0,0 @@
-- Add the `--type svelte` flag to the installation command to set up Storybook manually:
-
- ```shell
- npx storybook@latest init --type svelte
- ```
-
-- Storybook's CLI provides support for [Yarn](https://yarnpkg.com/), [npm](https://www.npmjs.com/), and [pnpm](https://pnpm.io/) package managers. If you have Yarn installed in your environment but prefer to use another as your default package manager add the `--package-manager` flag to your installation command. For example:
-
- ```shell
- npx storybook@latest init --package-manager=npm
- ```
-
-### Svelte Native
-
-To enable support for Svelte Native story format you'll need to make the following changes your Storybook configuration:
-- Add the [`@storybook/addon-svelte-csf`](https://storybook.js.org/addons/@storybook/addon-svelte-csf/) addon as development dependency.
-- Enable the addon in your Storybook configuration file (e.g., `.storybook/main.js`).
-- Disable on demand story loading configuration property (e.g., `storyStoreV7`) in your Storybook configuration file.
-- For issues with Svelte Native Story Format, check the [Svelte Story Format addon repository](https://github.com/storybookjs/addon-svelte-csf) for instructions.
-
-
-- For other installation issues, check the Svelte README files for additional instructions:
- - [Svelte with Webpack](https://github.com/storybookjs/storybook/tree/next/code/frameworks/svelte-webpack5)
- - [Svelte with Vite](https://github.com/storybookjs/storybook/tree/next/code/frameworks/svelte-vite)
- - [SvelteKit](https://github.com/storybookjs/storybook/tree/next/code/frameworks/sveltekit)
diff --git a/docs/get-started/installation-problems/vue.mdx b/docs/get-started/installation-problems/vue.mdx
deleted file mode 100644
index 1268f226a36..00000000000
--- a/docs/get-started/installation-problems/vue.mdx
+++ /dev/null
@@ -1,19 +0,0 @@
-- Add the `--type vue` (for Vue 2), or `--type vue3` (for Vue 3) flag to the installation command to set up Storybook manually:
-
- ```shell
- # For Vue 2 projects
- npx storybook@latest init --type vue
-
- # For Vue 3 projects
- npx storybook@latest init --type vue3
- ```
-
-- Storybook's CLI provides support for [Yarn](https://yarnpkg.com/), [npm](https://www.npmjs.com/), and [pnpm](https://pnpm.io/) package managers. If you have Yarn installed in your environment but prefer to use another as your default package manager add the `--package-manager` flag to your installation command. For example:
-
- ```shell
- npx storybook@latest init --package-manager=npm
- ```
-
-- For other installation issues, check the Vue README files for additional instructions:
- - [Vue 3 with Webpack](https://github.com/storybookjs/storybook/tree/next/code/frameworks/vue3-webpack5)
- - [Vue 3 with Vite](https://github.com/storybookjs/storybook/tree/next/code/frameworks/vue3-vite)
diff --git a/docs/get-started/installation-problems/web-components.mdx b/docs/get-started/installation-problems/web-components.mdx
deleted file mode 100644
index 1a25db5eade..00000000000
--- a/docs/get-started/installation-problems/web-components.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
-- Add the `--type web_components` flag to the installation command to set up Storybook manually:
-
- ```shell
- npx storybook@latest init --type web_components
- ```
-
-- For other installation issues, check the Web Components README files for additional instructions:
- - [Web Components with Webpack](https://github.com/storybookjs/storybook/tree/next/code/frameworks/web-components-webpack5)
- - [Web Components with Vite](https://github.com/storybookjs/storybook/tree/next/code/frameworks/web-components-vite)
diff --git a/docs/snippets/angular/angular-project-compodoc-config.json.mdx b/docs/snippets/angular/angular-project-compodoc-config.json.mdx
new file mode 100644
index 00000000000..66d4130306e
--- /dev/null
+++ b/docs/snippets/angular/angular-project-compodoc-config.json.mdx
@@ -0,0 +1,50 @@
+```jsonc
+// angular.json
+
+{
+ "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+ "version": 1,
+ "newProjectRoot": "projects",
+ "projects": {
+ "your-project": {
+ "projectType": "application",
+ "schematics": {},
+ "root": "",
+ "sourceRoot": "src",
+ "prefix": "app",
+ "architect": {
+ "storybook": {
+ "builder": "@storybook/angular:start-storybook",
+ "options": {
+ "configDir": ".storybook",
+ "browserTarget": "your-project:build",
+ "compodoc": true,
+ "compodocArgs": [
+ "-e",
+ "json",
+ "-d",
+ "." // Add this line to introspect the relevant files starting from the root directory of your project.
+ ],
+ "port": 6006
+ }
+ },
+ "build-storybook": {
+ "builder": "@storybook/angular:build-storybook",
+ "options": {
+ "configDir": ".storybook",
+ "browserTarget": "your-project:build",
+ "compodoc": true,
+ "compodocArgs": [
+ "-e",
+ "json",
+ "-d",
+ "." // Add this line to introspect the relevant files starting from the root directory of your project.
+ ],
+ "outputDir": "storybook-static"
+ }
+ }
+ }
+ }
+ }
+}
+```
diff --git a/docs/snippets/angular/compodoc-install.npm.js.mdx b/docs/snippets/angular/compodoc-install.npm.js.mdx
new file mode 100644
index 00000000000..be943eda7dc
--- /dev/null
+++ b/docs/snippets/angular/compodoc-install.npm.js.mdx
@@ -0,0 +1,3 @@
+```shell
+npm install @compodoc/compodoc --save-dev
+```
diff --git a/docs/snippets/angular/compodoc-install.pnpm.js.mdx b/docs/snippets/angular/compodoc-install.pnpm.js.mdx
new file mode 100644
index 00000000000..116346afd22
--- /dev/null
+++ b/docs/snippets/angular/compodoc-install.pnpm.js.mdx
@@ -0,0 +1,3 @@
+```shell
+pnpm add --save-dev @compodoc/compodo
+```
diff --git a/docs/snippets/angular/compodoc-install.yarn.js.mdx b/docs/snippets/angular/compodoc-install.yarn.js.mdx
new file mode 100644
index 00000000000..8a6ee05f79b
--- /dev/null
+++ b/docs/snippets/angular/compodoc-install.yarn.js.mdx
@@ -0,0 +1,3 @@
+```shell
+yarn add --dev @compodoc/compodoc
+```
diff --git a/docs/snippets/angular/storybook-preview-compodoc-config.ts.mdx b/docs/snippets/angular/storybook-preview-compodoc-config.ts.mdx
new file mode 100644
index 00000000000..658a8906b1e
--- /dev/null
+++ b/docs/snippets/angular/storybook-preview-compodoc-config.ts.mdx
@@ -0,0 +1,24 @@
+```ts
+// .storybook/preview.ts
+
+import type { Preview } from '@storybook/angular';
+import { setCompodocJson } from '@storybook/addon-docs/angular';
+
+import docJson from '../documentation.json'; // The path to your generated json file from Compodoc contains all your documentation information.
+
+setCompodocJson(docJson);
+
+const preview: Preview = {
+ parameters: {
+ actions: { argTypesRegex: '^on[A-Z].*' },
+ controls: {
+ matchers: {
+ color: /(background|color)$/i,
+ date: /Date$/,
+ },
+ },
+ },
+};
+
+export default preview;
+```
diff --git a/docs/snippets/common/init-command-custom-package-manager.npx.js.mdx b/docs/snippets/common/init-command-custom-package-manager.npx.js.mdx
new file mode 100644
index 00000000000..e904617f28c
--- /dev/null
+++ b/docs/snippets/common/init-command-custom-package-manager.npx.js.mdx
@@ -0,0 +1,3 @@
+```shell
+npx storybook@latest init --package-manager=npm
+```
diff --git a/docs/snippets/common/init-command-custom-package-manager.pnpm.js.mdx b/docs/snippets/common/init-command-custom-package-manager.pnpm.js.mdx
new file mode 100644
index 00000000000..abd51d256e8
--- /dev/null
+++ b/docs/snippets/common/init-command-custom-package-manager.pnpm.js.mdx
@@ -0,0 +1,3 @@
+```shell
+pnpm dlx storybook@latest init --package-manager=npm
+```
diff --git a/docs/snippets/common/init-command-custom-package-manager.yarn.js.mdx b/docs/snippets/common/init-command-custom-package-manager.yarn.js.mdx
new file mode 100644
index 00000000000..d01bae57173
--- /dev/null
+++ b/docs/snippets/common/init-command-custom-package-manager.yarn.js.mdx
@@ -0,0 +1,3 @@
+```shell
+yarn dlx storybook@latest init --package-manager=npm
+```
diff --git a/docs/snippets/common/init-command-manual-framework.npx.js.mdx b/docs/snippets/common/init-command-manual-framework.npx.js.mdx
new file mode 100644
index 00000000000..b0cbed16788
--- /dev/null
+++ b/docs/snippets/common/init-command-manual-framework.npx.js.mdx
@@ -0,0 +1,3 @@
+```shell
+npx storybook@latest init --type solid
+```
diff --git a/docs/snippets/common/init-command-manual-framework.pnpm.js.mdx b/docs/snippets/common/init-command-manual-framework.pnpm.js.mdx
new file mode 100644
index 00000000000..483487b08de
--- /dev/null
+++ b/docs/snippets/common/init-command-manual-framework.pnpm.js.mdx
@@ -0,0 +1,3 @@
+```shell
+pnpm dlx storybook@latest init --type solid
+```
diff --git a/docs/snippets/common/init-command-manual-framework.yarn.js.mdx b/docs/snippets/common/init-command-manual-framework.yarn.js.mdx
new file mode 100644
index 00000000000..b1152c869f0
--- /dev/null
+++ b/docs/snippets/common/init-command-manual-framework.yarn.js.mdx
@@ -0,0 +1,3 @@
+```shell
+yarn dlx storybook@latest init --type solid
+```
diff --git a/docs/snippets/common/storybook-mdx-template-with-prop.mdx.mdx b/docs/snippets/common/storybook-mdx-template-with-prop.mdx.mdx
index 009ecd7eab7..d4cf4b10878 100644
--- a/docs/snippets/common/storybook-mdx-template-with-prop.mdx.mdx
+++ b/docs/snippets/common/storybook-mdx-template-with-prop.mdx.mdx
@@ -1,7 +1,7 @@
```mdx
{/* DocumentationTemplate.mdx */}
-import { Meta, Title, Subtitle, Description, Primary, Controls, Stories } from '@storybook/blocks';
+import { Meta, Title, Primary, Controls, Stories } from '@storybook/blocks';
{/*
* π The isTemplate property is required to tell Storybook that this is a template
diff --git a/docs/snippets/ember/button-story-default-export-with-component.js.mdx b/docs/snippets/ember/button-story-default-export-with-component.js.mdx
new file mode 100644
index 00000000000..c1e6396ad3e
--- /dev/null
+++ b/docs/snippets/ember/button-story-default-export-with-component.js.mdx
@@ -0,0 +1,7 @@
+```js
+// Button.stories.js
+
+export default {
+ component: 'button',
+};
+```
diff --git a/docs/snippets/ember/storybook-ember-cli-build.js.mdx b/docs/snippets/ember/storybook-ember-cli-build.js.mdx
new file mode 100644
index 00000000000..426e63872b8
--- /dev/null
+++ b/docs/snippets/ember/storybook-ember-cli-build.js.mdx
@@ -0,0 +1,17 @@
+```js
+// ember-cli-build.js
+
+'use strict';
+
+const EmberApp = require('ember-cli/lib/broccoli/ember-app');
+
+module.exports = function (defaults) {
+ const app = new EmberApp(defaults, {
+ '@storybook/ember-cli-storybook': {
+ enableAddonDocsIntegration: true,
+ },
+ });
+
+ return app.toTree();
+};
+```
diff --git a/docs/snippets/ember/storybook-preview-custom-metadata.js.mdx b/docs/snippets/ember/storybook-preview-custom-metadata.js.mdx
new file mode 100644
index 00000000000..ec446d1559a
--- /dev/null
+++ b/docs/snippets/ember/storybook-preview-custom-metadata.js.mdx
@@ -0,0 +1,19 @@
+```js
+// .storybook/preview.js
+
+import { setJSONDoc } from '@storybook/addon-docs/ember';
+
+import docJson from '../dist/storybook-docgen/index.json';
+setJSONDoc(docJson);
+
+export default {
+ parameters: {
+ controls: {
+ matchers: {
+ color: /(background|color)$/i,
+ date: /Date$/,
+ },
+ },
+ },
+};
+```
diff --git a/docs/snippets/preact/button-story-default-export-with-component.js.mdx b/docs/snippets/preact/button-story-default-export-with-component.js.mdx
new file mode 100644
index 00000000000..dfffb5de27e
--- /dev/null
+++ b/docs/snippets/preact/button-story-default-export-with-component.js.mdx
@@ -0,0 +1,9 @@
+```js
+// Button.stories.js|jsx
+
+import { Button } from './Button';
+
+export default {
+ component: Button,
+};
+```
diff --git a/docs/snippets/svelte/main-config-csf-addon-register.js.mdx b/docs/snippets/svelte/main-config-csf-addon-register.js.mdx
new file mode 100644
index 00000000000..5559ac261ca
--- /dev/null
+++ b/docs/snippets/svelte/main-config-csf-addon-register.js.mdx
@@ -0,0 +1,13 @@
+```js
+// .storybook/main.js
+
+export default {
+ // Replace sveltekit with svelte-vite if you are not working with SvelteKit
+ framework: '@storybook/sveltekit',
+ stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx|svelte)'],
+ addons: [
+ // Other Storybook addons
+ '@storybook/addon-svelte-csf', //π The Svelte CSF addon goes here
+ ],
+};
+```
diff --git a/docs/snippets/svelte/main-config-csf-addon-register.ts.mdx b/docs/snippets/svelte/main-config-csf-addon-register.ts.mdx
new file mode 100644
index 00000000000..3221880ebb9
--- /dev/null
+++ b/docs/snippets/svelte/main-config-csf-addon-register.ts.mdx
@@ -0,0 +1,17 @@
+```ts
+// .storybook/main.ts
+
+// Replace sveltekit with svelte-vite if you are not working with SvelteKit
+import type { StorybookConfig } from '@storybook/sveltekit';
+
+const config: StorybookConfig = {
+ framework: '@storybook/sveltekit',
+ stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx|svelte)'],
+ addons: [
+ // Other Storybook addons
+ '@storybook/addon-svelte-csf', //π The Svelte CSF addon goes here
+ ],
+};
+
+export default config;
+```
diff --git a/docs/snippets/svelte/svelte-csf-addon-install.npm.js.mdx b/docs/snippets/svelte/svelte-csf-addon-install.npm.js.mdx
new file mode 100644
index 00000000000..574d6365186
--- /dev/null
+++ b/docs/snippets/svelte/svelte-csf-addon-install.npm.js.mdx
@@ -0,0 +1,3 @@
+```shell
+npm install @storybook/addon-svelte-csf --save-dev
+```
diff --git a/docs/snippets/svelte/svelte-csf-addon-install.pnpm.js.mdx b/docs/snippets/svelte/svelte-csf-addon-install.pnpm.js.mdx
new file mode 100644
index 00000000000..4ff9be66f10
--- /dev/null
+++ b/docs/snippets/svelte/svelte-csf-addon-install.pnpm.js.mdx
@@ -0,0 +1,3 @@
+```shell
+pnpm add --save-dev @storybook/addon-svelte-csf
+```
diff --git a/docs/snippets/svelte/svelte-csf-addon-install.yarn.js.mdx b/docs/snippets/svelte/svelte-csf-addon-install.yarn.js.mdx
new file mode 100644
index 00000000000..5b3e6dff23f
--- /dev/null
+++ b/docs/snippets/svelte/svelte-csf-addon-install.yarn.js.mdx
@@ -0,0 +1,3 @@
+```shell
+yarn add --dev @storybook/addon-svelte-csf
+```
diff --git a/docs/snippets/web-components/button-story-default-export-with-component.ts.mdx b/docs/snippets/web-components/button-story-default-export-with-component.ts.mdx
new file mode 100644
index 00000000000..805431b359c
--- /dev/null
+++ b/docs/snippets/web-components/button-story-default-export-with-component.ts.mdx
@@ -0,0 +1,11 @@
+```ts
+// Button.stories.ts
+
+import type { Meta } from '@storybook/web-components';
+
+export default {
+ component: 'demo-button',
+};
+
+export default meta;
+```
diff --git a/docs/snippets/web-components/component-story-custom-args-complex.js.mdx b/docs/snippets/web-components/component-story-custom-args-complex.js.mdx
new file mode 100644
index 00000000000..0bd0906b20b
--- /dev/null
+++ b/docs/snippets/web-components/component-story-custom-args-complex.js.mdx
@@ -0,0 +1,42 @@
+```js
+// Button.stories.js
+
+import { html } from 'lit';
+
+export default {
+ component: 'custom-component',
+ //π Creates specific argTypes
+ argTypes: {
+ propertyA: {
+ options: ['Item One', 'Item Two', 'Item Three'],
+ control: { type: 'select' }, // Automatically inferred when 'options' is defined
+ },
+ propertyB: {
+ options: ['Another Item One', 'Another Item Two', 'Another Item Three'],
+ },
+ },
+};
+
+const someFunction = (valuePropertyA, valuePropertyB) => {
+ // Do some logic here
+};
+
+export const ExampleStory = {
+ render: ({ propertyA, propertyB }) => {
+ //π Assigns the function result to a variable
+ const someFunctionResult = someFunction(propertyA, propertyB);
+
+ return html`
+
+ `;
+ },
+ args: {
+ propertyA: 'Item One',
+ propertyB: 'Another Item One',
+ },
+};
+```
diff --git a/docs/snippets/web-components/component-story-custom-args-complex.ts.mdx b/docs/snippets/web-components/component-story-custom-args-complex.ts.mdx
new file mode 100644
index 00000000000..b772ef0301c
--- /dev/null
+++ b/docs/snippets/web-components/component-story-custom-args-complex.ts.mdx
@@ -0,0 +1,47 @@
+```ts
+// Button.stories.ts
+
+import type { Meta, StoryObj } from '@storybook/web-components';
+
+import { html } from 'lit';
+
+const meta: Meta = {
+ component: 'custom-component',
+ //π Creates specific argTypes with options
+ argTypes: {
+ propertyA: {
+ options: ['Item One', 'Item Two', 'Item Three'],
+ control: { type: 'select' }, // Automatically inferred when 'options' is defined
+ },
+ propertyB: {
+ options: ['Another Item One', 'Another Item Two', 'Another Item Three'],
+ },
+ },
+};
+
+export default meta;
+type Story = StoryObj;
+
+const someFunction = (valuePropertyA: any, valuePropertyB: any) => {
+ // Do some logic here
+};
+
+export const ExampleStory: Story = {
+ render: ({ propertyA, propertyB }) => {
+ //π Assigns the function result to a variable
+ const someFunctionResult = someFunction(propertyA, propertyB);
+
+ return html`
+
+ `;
+ },
+ args: {
+ propertyA: 'Item One',
+ propertyB: 'Another Item One',
+ },
+};
+```
diff --git a/docs/snippets/web-components/storybook-preview-custom-elements-config.js.mdx b/docs/snippets/web-components/storybook-preview-custom-elements-config.js.mdx
new file mode 100644
index 00000000000..10b4d63d5c0
--- /dev/null
+++ b/docs/snippets/web-components/storybook-preview-custom-elements-config.js.mdx
@@ -0,0 +1,19 @@
+```js
+// .storybook/preview.js
+
+import { setCustomElementsManifest } from '@storybook/web-components';
+import customElements from '../custom-elements.json';
+
+setCustomElementsManifest(customElements);
+
+export default {
+ parameters: {
+ controls: {
+ matchers: {
+ color: /(background|color)$/i,
+ date: /Date$/,
+ },
+ },
+ },
+};
+```
diff --git a/docs/snippets/web-components/storybook-preview-custom-elements-config.ts.mdx b/docs/snippets/web-components/storybook-preview-custom-elements-config.ts.mdx
new file mode 100644
index 00000000000..df8a1e37405
--- /dev/null
+++ b/docs/snippets/web-components/storybook-preview-custom-elements-config.ts.mdx
@@ -0,0 +1,24 @@
+```ts
+// .storybook/preview.ts
+
+import type { Preview } from '@storybook/web-components';
+import { setCustomElementsManifest } from '@storybook/web-components';
+
+import customElements from '../custom-elements.json';
+
+setCustomElementsManifest(customElements);
+
+const preview: Preview = {
+ parameters: {
+ actions: { argTypesRegex: '^on[A-Z].*' },
+ controls: {
+ matchers: {
+ color: /(background|color)$/i,
+ date: /Date$/i,
+ },
+ },
+ },
+};
+
+export default preview;
+```
diff --git a/docs/writing-stories/index.md b/docs/writing-stories/index.md
index c938a3f6092..a270dd95b1e 100644
--- a/docs/writing-stories/index.md
+++ b/docs/writing-stories/index.md
@@ -39,6 +39,7 @@ The _default_ export metadata controls how Storybook lists your stories and prov
'svelte/button-story-default-export-with-component.js.mdx',
'svelte/button-story-default-export-with-component.ts.mdx',
'web-components/button-story-default-export-with-component.js.mdx',
+ 'web-components/button-story-default-export-with-component.ts.mdx',
'html/button-story-default-export.js.mdx',
'html/button-story-default-export.ts.mdx',
'solid/button-story-default-export-with-component.js.mdx',