diff --git a/.github/workflows/storybook-check.yml b/.github/workflows/storybook-check.yml index 4edace160224ae..abeefdaf6a2ced 100644 --- a/.github/workflows/storybook-check.yml +++ b/.github/workflows/storybook-check.yml @@ -36,6 +36,20 @@ jobs: - name: Setup Node.js and install dependencies uses: ./.github/setup-node + # Every story URL is built from a declared ID. Regenerating the + # committed list of them fails when one is gone, so losing a + # published URL has to be deliberate. The story index is read from + # source, so this runs before the build rather than after it. + - name: Check no story URL disappeared + run: npm run storybook:story-id-snapshot + + - name: Check story ID snapshot is up to date + run: | + if ! git diff --exit-code -- storybook/story-ids.txt; then + echo "::error::The Storybook story ID snapshot is out of date. Review the diff above to confirm the change is expected, then run 'npm run storybook:story-id-snapshot' locally and commit the updated storybook/story-ids.txt." + exit 1 + fi + - name: Build Storybook env: NODE_OPTIONS: --max-old-space-size=8192 diff --git a/package.json b/package.json index 11830d2ce1800b..832c70d5d85c42 100644 --- a/package.json +++ b/package.json @@ -189,6 +189,7 @@ "storybook:e2e:build": "npm run --workspace @wordpress/storybook-playwright storybook:build", "storybook:manifest-snapshot": "npm run --workspace @wordpress/storybook manifest-snapshot", "test": "npm run lint && npm run test:unit", + "storybook:story-id-snapshot": "npm run --workspace @wordpress/storybook story-id-snapshot", "test:create-block": "npm run --workspace @wordpress/validation-tools test-create-block --", "test:e2e": "npm exec --no --workspace @wordpress/e2e-tests-playwright -- wp-scripts test-e2e --config playwright.config.ts", "test:e2e:debug": "npm exec --no --workspace @wordpress/e2e-tests-playwright -- wp-scripts test-e2e --config playwright.config.ts --ui", diff --git a/packages/icons/src/icon/stories/index.story.tsx b/packages/icons/src/icon/stories/index.story.tsx index db1b7ef806db57..248a16512e1937 100644 --- a/packages/icons/src/icon/stories/index.story.tsx +++ b/packages/icons/src/icon/stories/index.story.tsx @@ -6,6 +6,8 @@ import * as icons from '../../'; const meta = { component: Icon, + // Shares its `id` and `title` with `storybook/stories/icons/library.story.tsx` + // so both files merge into one entry. Change them together. id: 'icons-icon', title: 'Design System/Icons/Icon', parameters: { diff --git a/packages/theme/src/color-ramps/stories/brand-fallbacks.story.tsx b/packages/theme/src/color-ramps/stories/brand-fallbacks.story.tsx index 71f22494f72abe..a46b2588dfb6b6 100644 --- a/packages/theme/src/color-ramps/stories/brand-fallbacks.story.tsx +++ b/packages/theme/src/color-ramps/stories/brand-fallbacks.story.tsx @@ -45,6 +45,7 @@ const Verifier: React.FC< VerifierProps > = () => null; */ const meta: Meta< typeof Verifier > = { title: 'Design System/Theme/Theme Provider/Brand Color Fallbacks', + id: 'design-system-theme-theme-provider-brand-color-fallbacks', component: Verifier, argTypes: { adminThemeColor: { diff --git a/packages/theme/src/color-ramps/stories/index.story.tsx b/packages/theme/src/color-ramps/stories/index.story.tsx index b3e326d93dc6c4..405aefe6f9582d 100644 --- a/packages/theme/src/color-ramps/stories/index.story.tsx +++ b/packages/theme/src/color-ramps/stories/index.story.tsx @@ -20,6 +20,7 @@ const ColorGen = ( props: { const meta: Meta< typeof ColorGen > = { title: 'Design System/Theme/Theme Provider/Color Scales', + id: 'design-system-theme-theme-provider-color-scales', component: ColorGen, argTypes: { background: { diff --git a/packages/theme/src/stories/index.story.tsx b/packages/theme/src/stories/index.story.tsx index 3196b745412216..0b7fe875300ecf 100644 --- a/packages/theme/src/stories/index.story.tsx +++ b/packages/theme/src/stories/index.story.tsx @@ -9,6 +9,7 @@ const wpdsColorTokens = wpdsTokens.filter( ( name ) => const meta: Meta< typeof ThemeProvider > = { title: 'Design System/Theme/Theme Provider', + id: 'design-system-theme-theme-provider', component: ThemeProvider, args: { isRoot: true, diff --git a/packages/theme/src/stories/introduction.mdx b/packages/theme/src/stories/introduction.mdx index 48466f6d120b10..d19e08bb930cb4 100644 --- a/packages/theme/src/stories/introduction.mdx +++ b/packages/theme/src/stories/introduction.mdx @@ -1,6 +1,6 @@ import { Meta, Markdown } from '@storybook/addon-docs/blocks'; import Readme from '../../README.md?raw'; - + {Readme} diff --git a/packages/theme/src/stories/tokens.mdx b/packages/theme/src/stories/tokens.mdx index fdbe171645b3e2..ede51670cb89df 100644 --- a/packages/theme/src/stories/tokens.mdx +++ b/packages/theme/src/stories/tokens.mdx @@ -1,6 +1,6 @@ import { Meta, Markdown } from '@storybook/addon-docs/blocks'; import Tokens from '../../docs/tokens.md?raw'; - + {Tokens} diff --git a/packages/theme/src/stories/typography.mdx b/packages/theme/src/stories/typography.mdx index da97a1b0ac03ba..9305d3bec9fe9f 100644 --- a/packages/theme/src/stories/typography.mdx +++ b/packages/theme/src/stories/typography.mdx @@ -2,7 +2,7 @@ import { Meta } from '@storybook/addon-docs/blocks'; import { TypographyTokenPreview } from './typography-preview'; import '@wordpress/theme/design-tokens.css'; - + # Typography tokens diff --git a/storybook/README.md b/storybook/README.md index 0c6c17af2f21a5..a29f5b5498266b 100644 --- a/storybook/README.md +++ b/storybook/README.md @@ -29,3 +29,31 @@ When a snapshot changes due to code changes or Storybook upgrades, a pull reques npm run storybook:build npm run storybook:manifest-snapshot ``` + +## Stable story URLs + +A story's URL is built from its `id`. Without one, Storybook derives the `id` from the `title`, so moving a story to another sidebar folder changes its URL and breaks existing links. Every story and doc therefore declares an `id`, and the `title` only sets its place in the sidebar: + +```ts +const meta = { + title: 'Components/Containers/Card', // Sidebar position, free to change. + id: 'components-card', // URL, never changes. +}; +``` + +In MDX, set it on the `Meta` block: ``. An MDX doc attached to a CSF file with `of={ … }` inherits that file's `id` and needs none of its own. + +For a new story, use the `id` Storybook would derive from the `title`: lowercase, with each run of other characters replaced by a hyphen (`Components/Containers/Card` becomes `components-containers-card`). Never change the `id` of an existing story. + +Two checks enforce this: + +- `storybook/test/story-ids.test.ts` fails when a story or doc does not declare an `id`. +- `storybook/story-ids.txt` lists every published story and doc URL. CI regenerates it and fails when a URL in the committed list has disappeared. + +To update the list, run the following from the top-level Gutenberg directory and commit the result. No Storybook build is needed. + +```bash +npm run storybook:story-id-snapshot +``` + +New URLs are added without complaint. If a URL disappears because a story moved, restore its original `id` instead. If the removal is intended, for example when a component's stories are deleted, commit the regenerated list in the same pull request so the removed URLs are visible in the diff. Documentation for a deprecated component should usually be marked deprecated rather than deleted, so its URL keeps working. diff --git a/storybook/main.ts b/storybook/main.ts index f7dc6882b12ed3..506f20b6029a95 100644 --- a/storybook/main.ts +++ b/storybook/main.ts @@ -7,6 +7,7 @@ import type { StorybookConfig } from '@storybook/react-vite'; import dsTokenFallbacks from '@wordpress/theme/postcss-plugins/postcss-ds-token-fallbacks'; import dsTokenFallbacksJs from '@wordpress/theme/vite-plugins/vite-ds-token-fallbacks'; import { statusIndexer } from './status-indexer.js'; +import { storyGlobs } from './story-globs.js'; /** * @see https://storybook.js.org/docs/faq#how-do-i-fix-module-resolution-in-special-environments @@ -19,39 +20,11 @@ function getAbsolutePath( packageName: string ) { const { NODE_ENV = 'development' } = process.env; -const stories = [ - './stories/playground/**/*.story.@(jsx|tsx)', - './stories/**/*.mdx', - './stories/design-system/**/*.story.@(ts|tsx|mts|cts)', - '../packages/block-editor/src/**/stories/*.story.@(jsx|tsx|mdx)', - '../packages/editor/src/**/stories/*.story.@(jsx|tsx|mdx)', - '../packages/global-styles-ui/src/**/stories/*.story.@(jsx|tsx|mdx)', - '../packages/components/src/**/stories/*.story.@(jsx|tsx)', - '../packages/components/src/**/stories/*.mdx', - '../packages/icons/src/**/stories/*.story.@(tsx|mdx)', - './stories/icons/**/*.story.@(ts|tsx|mts|cts)', - '../packages/dataviews/src/**/stories/*.story.@(tsx|mdx)', - '../packages/fields/src/**/stories/*.story.@(tsx|mdx)', - '../packages/image-cropper/src/**/stories/*.story.@(tsx|mdx)', - '../packages/media-editor/src/**/stories/*.story.@(tsx|mdx)', - '../packages/media-fields/src/**/stories/*.story.@(tsx|mdx)', - '../packages/theme/src/**/stories/*.mdx', - '../packages/theme/src/**/stories/*.story.@(tsx|mdx)', - '../packages/grid/src/**/stories/*.story.@(ts|tsx|mts|cts)', - '../packages/widget-primitives/src/**/stories/*.mdx', - '../packages/widget-primitives/src/**/stories/*.story.@(ts|tsx|mts|cts)', - '../packages/widget-dashboard/src/**/stories/*.mdx', - '../packages/widget-dashboard/src/**/stories/*.story.@(ts|tsx|mts|cts)', - '../packages/ui/src/**/stories/*.mdx', - '../packages/ui/src/**/stories/*.story.@(ts|tsx|mts|cts)', - '../packages/admin-ui/src/**/stories/*.story.@(ts|tsx|mts|cts)', -]; - const config: StorybookConfig = { core: { disableTelemetry: true, }, - stories, + stories: storyGlobs, // Tags stories with their `componentStatus` so the sidebar can show it. experimental_indexers: ( existing = [] ) => [ statusIndexer, ...existing ], staticDirs: [ './static' ], diff --git a/storybook/package.json b/storybook/package.json index 4c3ffe98ccf971..0b0cf8832ade57 100644 --- a/storybook/package.json +++ b/storybook/package.json @@ -82,6 +82,7 @@ }, "scripts": { "manifest-snapshot": "node ./scripts/generate-manifest-snapshot.mjs ./build/manifests/components.json", + "story-id-snapshot": "node ./scripts/generate-story-id-snapshot.mjs", "postmanifest-snapshot": "prettier --write components-manifest.yml", "storybook:build": "NODE_OPTIONS=--max-old-space-size=8192 storybook build -c . -o ./build && node ./scripts/sanitize-components-manifest.mjs ./build/manifests/components.json && node ./scripts/inject-legacy-docgen.mjs ./build/manifests/components.json", "storybook:dev": "storybook dev -c . -p 50240", diff --git a/storybook/scripts/generate-story-id-snapshot.mjs b/storybook/scripts/generate-story-id-snapshot.mjs new file mode 100644 index 00000000000000..1cc9010f6ef5bd --- /dev/null +++ b/storybook/scripts/generate-story-id-snapshot.mjs @@ -0,0 +1,87 @@ +#!/usr/bin/env node +/** + * Generates a snapshot of every Storybook story and doc URL. + * + * A story's URL is its ID, and every published link, bookmark, handbook page + * and deprecation notice points at one. The snapshot records the full set so + * that losing one has to be a deliberate act rather than a side effect of a + * rename nobody looked at twice. + * + * The snapshot is regenerated in CI, which fails if an ID it used to contain + * is gone. Removing a page on purpose still works: delete it and commit the + * regenerated snapshot in the same change, where the loss is visible in the + * diff. New IDs are written without complaint. + * + * The story index is built from source, so this needs no Storybook build. + * + * Usage: node generate-story-id-snapshot.mjs + */ +import { readFile, writeFile } from 'node:fs/promises'; +import path from 'node:path'; +import { buildIndex } from 'storybook/internal/core-server'; + +const CONFIG_DIR = path.join( import.meta.dirname, '..' ); +const SNAPSHOT_PATH = path.join( CONFIG_DIR, 'story-ids.txt' ); + +const HEADER = + '# Auto-generated snapshot of every Storybook story and doc URL.\n' + + '# Do not edit by hand. Regenerate with `npm run storybook:story-id-snapshot`\n' + + '# and commit the result. See `storybook/README.md` for details.\n'; + +/** + * Reads the IDs out of the committed snapshot, ignoring its header. + * + * @return {Promise} The IDs, or an empty list if there is no + * snapshot yet. + */ +async function readSnapshot() { + try { + const raw = await readFile( SNAPSHOT_PATH, 'utf8' ); + return raw + .split( '\n' ) + .map( ( line ) => line.trim() ) + .filter( ( line ) => line && ! line.startsWith( '#' ) ); + } catch ( error ) { + if ( + /** @type {NodeJS.ErrnoException} */ ( error ).code !== 'ENOENT' + ) { + throw error; + } + return []; + } +} + +const { entries } = await buildIndex( { configDir: CONFIG_DIR } ); +const current = Object.keys( entries ).sort(); + +if ( current.length === 0 ) { + console.error( + 'No stories were indexed. The Storybook configuration may have changed.' + ); + process.exit( 1 ); +} + +const committed = await readSnapshot(); +const currentIds = new Set( current ); +const committedIds = new Set( committed ); +const removed = committed.filter( ( id ) => ! currentIds.has( id ) ); +const added = current.filter( ( id ) => ! committedIds.has( id ) ); + +await writeFile( SNAPSHOT_PATH, HEADER + current.join( '\n' ) + '\n' ); + +if ( added.length > 0 ) { + console.log( `Added ${ added.length } story ID(s) to the snapshot.` ); +} + +if ( removed.length > 0 ) { + console.error( + `${ removed.length } story URL(s) no longer exist:\n\n` + + removed.map( ( id ) => ` - ${ id }` ).join( '\n' ) + + `\n\nEach of these is a published URL that will start 404ing. If a story moved\n` + + `in the sidebar, give it back its original \`id\` so the URL follows it.\n` + + `Documentation for a deprecated component is usually better marked deprecated\n` + + `than deleted. If the removal is intended, commit the regenerated\n` + + `storybook/story-ids.txt so the lost URLs show up in the diff.\n` + ); + process.exitCode = 1; +} diff --git a/storybook/stories/design-system/contributing.mdx b/storybook/stories/design-system/contributing.mdx index 1b1a1eebffacdc..ec6bd9788fdbf2 100644 --- a/storybook/stories/design-system/contributing.mdx +++ b/storybook/stories/design-system/contributing.mdx @@ -1,7 +1,7 @@ import { Meta, Markdown } from '@storybook/addon-docs/blocks'; import Contributing from './contributing.md?raw'; - + { /* The content lives in contributing.md so people and agents can read the same text outside Storybook. */ } diff --git a/storybook/stories/design-system/introduction.mdx b/storybook/stories/design-system/introduction.mdx index 7a733fc2627a07..a29e3be5637bdf 100644 --- a/storybook/stories/design-system/introduction.mdx +++ b/storybook/stories/design-system/introduction.mdx @@ -1,6 +1,6 @@ import { Meta } from '@storybook/addon-docs/blocks'; - + # WordPress Design System diff --git a/storybook/stories/design-system/patterns/destructive-actions.mdx b/storybook/stories/design-system/patterns/destructive-actions.mdx index 3e0195384c06ad..8d950d856c6f98 100644 --- a/storybook/stories/design-system/patterns/destructive-actions.mdx +++ b/storybook/stories/design-system/patterns/destructive-actions.mdx @@ -1,6 +1,6 @@ import { Meta } from '@storybook/addon-docs/blocks'; - + # Destructive Actions diff --git a/storybook/stories/design-system/patterns/error-messages.mdx b/storybook/stories/design-system/patterns/error-messages.mdx index fa0a212787b016..72f6cbd23f69ac 100644 --- a/storybook/stories/design-system/patterns/error-messages.mdx +++ b/storybook/stories/design-system/patterns/error-messages.mdx @@ -1,6 +1,6 @@ import { Meta } from '@storybook/addon-docs/blocks'; - + # Error Messages diff --git a/storybook/stories/design-system/patterns/save-and-submit.story.tsx b/storybook/stories/design-system/patterns/save-and-submit.story.tsx index 1a1cb3f5462694..973c1fee2eea56 100644 --- a/storybook/stories/design-system/patterns/save-and-submit.story.tsx +++ b/storybook/stories/design-system/patterns/save-and-submit.story.tsx @@ -53,6 +53,7 @@ const LayoutCardSaveExample = () => { const meta: Meta< typeof LayoutCardSaveExample > = { title: 'Design System/Patterns/Save & Submit', + id: 'design-system-patterns-save-submit', component: LayoutCardSaveExample, parameters: { controls: { disable: true }, diff --git a/storybook/stories/design-system/theme-example-application.story.tsx b/storybook/stories/design-system/theme-example-application.story.tsx index 86a4c83ff81f00..09f83a662e6037 100644 --- a/storybook/stories/design-system/theme-example-application.story.tsx +++ b/storybook/stories/design-system/theme-example-application.story.tsx @@ -140,6 +140,7 @@ const siteLanguageOptions = [ const meta: Meta< ExampleApplicationArgs > = { title: 'Design System/Theme/Theme Provider/Example Application', + id: 'design-system-theme-theme-provider-example-application', component: ThemeProvider, parameters: { controls: { expanded: true }, diff --git a/storybook/stories/design-system/tokens/motion.story.tsx b/storybook/stories/design-system/tokens/motion.story.tsx index 530fca9d3bd8d7..326835da89dc3a 100644 --- a/storybook/stories/design-system/tokens/motion.story.tsx +++ b/storybook/stories/design-system/tokens/motion.story.tsx @@ -233,6 +233,7 @@ function MotionDemo() { const meta: Meta< typeof MotionDemo > = { title: 'Design System/Tokens/Motion', + id: 'design-system-tokens-motion', component: MotionDemo, parameters: { controls: { hideNoControlsWarning: true }, diff --git a/storybook/stories/icons/library.story.tsx b/storybook/stories/icons/library.story.tsx index 426758c23eab91..851b5340c2531e 100644 --- a/storybook/stories/icons/library.story.tsx +++ b/storybook/stories/icons/library.story.tsx @@ -50,6 +50,8 @@ function nameToSlug( name: string ): string { const meta: Meta = { component: Icon, + // Shares its `id` and `title` with `packages/icons/src/icon/stories/index.story.tsx` + // so both files merge into one entry. Change them together. id: 'icons-icon', title: 'Design System/Icons/Icon', tags: [ '!autodocs' ], diff --git a/storybook/stories/introduction.mdx b/storybook/stories/introduction.mdx index 3ce356dd65614c..ac9b4c01895186 100644 --- a/storybook/stories/introduction.mdx +++ b/storybook/stories/introduction.mdx @@ -2,7 +2,7 @@ import { Markdown, Meta } from '@storybook/addon-docs/blocks'; import { JumpToIcon } from '@storybook/icons'; import { InlineIcon } from './docs/inline-icon'; - + # Introduction diff --git a/storybook/stories/playground/draggable-cross-document-fallback.story.jsx b/storybook/stories/playground/draggable-cross-document-fallback.story.jsx index e81d316d389821..834bdb509a857d 100644 --- a/storybook/stories/playground/draggable-cross-document-fallback.story.jsx +++ b/storybook/stories/playground/draggable-cross-document-fallback.story.jsx @@ -8,6 +8,7 @@ import { WithWpCompatOverlaySlot } from './with-wp-compat-overlay-slot'; export default { title: 'Playground/Debug fixtures/Draggable cross-document fallback', + id: 'playground-debug-fixtures-draggable-cross-document-fallback', component: Draggable, decorators: [ WithWpCompatOverlaySlot ], }; diff --git a/storybook/stories/playground/index.story.jsx b/storybook/stories/playground/index.story.jsx index aaa12a99e62d4d..a80a81aff555cb 100644 --- a/storybook/stories/playground/index.story.jsx +++ b/storybook/stories/playground/index.story.jsx @@ -8,6 +8,7 @@ registerCoreBlocks(); export default { title: 'Playground/Block Editor', + id: 'playground-block-editor', parameters: { sourceLink: 'storybook/stories/playground', }, diff --git a/storybook/stories/playground/popover-with-slotfill.story.jsx b/storybook/stories/playground/popover-with-slotfill.story.jsx index 43ad8b0c9a7e2a..0bde02e0117861 100644 --- a/storybook/stories/playground/popover-with-slotfill.story.jsx +++ b/storybook/stories/playground/popover-with-slotfill.story.jsx @@ -4,6 +4,7 @@ import { Popover } from '@wordpress/ui'; export default { title: 'Playground/Debug fixtures/Popover with SlotFill', + id: 'playground-debug-fixtures-popover-with-slotfill', component: Popover.Root, }; diff --git a/storybook/stories/playground/wp-compat-overlay-slot.story.jsx b/storybook/stories/playground/wp-compat-overlay-slot.story.jsx index de767a5f4bba38..0a4b3ea25def13 100644 --- a/storybook/stories/playground/wp-compat-overlay-slot.story.jsx +++ b/storybook/stories/playground/wp-compat-overlay-slot.story.jsx @@ -79,6 +79,7 @@ function UiPopoverFixture() { // of scope. export default { title: 'Playground/Debug fixtures/WP Compat Overlay Slot', + id: 'playground-debug-fixtures-wp-compat-overlay-slot', decorators: [ WithWpCompatOverlaySlot ], }; diff --git a/storybook/story-globs.ts b/storybook/story-globs.ts new file mode 100644 index 00000000000000..8e312675a6e1a6 --- /dev/null +++ b/storybook/story-globs.ts @@ -0,0 +1,32 @@ +/** + * Globs matching every story and doc Storybook indexes, relative to this + * directory. Kept apart from `main.ts` so the story ID test can walk the same + * set of files without loading the full Storybook configuration. + */ +export const storyGlobs = [ + './stories/playground/**/*.story.@(jsx|tsx)', + './stories/**/*.mdx', + './stories/design-system/**/*.story.@(ts|tsx|mts|cts)', + '../packages/block-editor/src/**/stories/*.story.@(jsx|tsx|mdx)', + '../packages/editor/src/**/stories/*.story.@(jsx|tsx|mdx)', + '../packages/global-styles-ui/src/**/stories/*.story.@(jsx|tsx|mdx)', + '../packages/components/src/**/stories/*.story.@(jsx|tsx)', + '../packages/components/src/**/stories/*.mdx', + '../packages/icons/src/**/stories/*.story.@(tsx|mdx)', + './stories/icons/**/*.story.@(ts|tsx|mts|cts)', + '../packages/dataviews/src/**/stories/*.story.@(tsx|mdx)', + '../packages/fields/src/**/stories/*.story.@(tsx|mdx)', + '../packages/image-cropper/src/**/stories/*.story.@(tsx|mdx)', + '../packages/media-editor/src/**/stories/*.story.@(tsx|mdx)', + '../packages/media-fields/src/**/stories/*.story.@(tsx|mdx)', + '../packages/theme/src/**/stories/*.mdx', + '../packages/theme/src/**/stories/*.story.@(tsx|mdx)', + '../packages/grid/src/**/stories/*.story.@(ts|tsx|mts|cts)', + '../packages/widget-primitives/src/**/stories/*.mdx', + '../packages/widget-primitives/src/**/stories/*.story.@(ts|tsx|mts|cts)', + '../packages/widget-dashboard/src/**/stories/*.mdx', + '../packages/widget-dashboard/src/**/stories/*.story.@(ts|tsx|mts|cts)', + '../packages/ui/src/**/stories/*.mdx', + '../packages/ui/src/**/stories/*.story.@(ts|tsx|mts|cts)', + '../packages/admin-ui/src/**/stories/*.story.@(ts|tsx|mts|cts)', +]; diff --git a/storybook/story-ids.txt b/storybook/story-ids.txt new file mode 100644 index 00000000000000..640f9980c28682 --- /dev/null +++ b/storybook/story-ids.txt @@ -0,0 +1,1180 @@ +# Auto-generated snapshot of every Storybook story and doc URL. +# Do not edit by hand. Regenerate with `npm run storybook:story-id-snapshot` +# and commit the result. See `storybook/README.md` for details. +admin-ui-breadcrumbs--docs +admin-ui-breadcrumbs--single-item +admin-ui-breadcrumbs--three-levels +admin-ui-breadcrumbs--two-levels +admin-ui-page--default +admin-ui-page--docs +admin-ui-page--full-header +admin-ui-page--with-actions +admin-ui-page--with-breadcrumbs +admin-ui-page--with-breadcrumbs-and-badges +admin-ui-page--with-breadcrumbs-and-subtitle +admin-ui-page--with-image-visual +admin-ui-page--with-interactive-navigation +admin-ui-page--with-navigation +admin-ui-page--with-navigation-and-actions +admin-ui-page--with-subtitle +admin-ui-page--with-title-and-badges +admin-ui-page--with-visual +admin-ui-page--with-visual-and-breadcrumbs +admin-ui-page--without-header +blockeditor-alignmentcontrol--default +blockeditor-alignmentcontrol--docs +blockeditor-alignmenttoolbar--default +blockeditor-alignmenttoolbar--docs +blockeditor-blockalignmentmatrixcontrol--default +blockeditor-blockalignmentmatrixcontrol--docs +blockeditor-blockdraggable--default +blockeditor-blockdraggable--docs +blockeditor-blockmover--default +blockeditor-blockmover--docs +blockeditor-blockmover--hide-drag-handle +blockeditor-blockmover--horizontal +blockeditor-blockpatternslist--default +blockeditor-blockpatternslist--docs +blockeditor-blockpopover--default +blockeditor-blockpopover--docs +blockeditor-blocktitle--default +blockeditor-blocktitle--docs +blockeditor-borderradiuscontrol--default +blockeditor-borderradiuscontrol--docs +blockeditor-colorpalettecontrol--default +blockeditor-colorpalettecontrol--docs +blockeditor-dateformatpicker--default +blockeditor-dateformatpicker--docs +blockeditor-dimensioncontrol--default +blockeditor-dimensioncontrol--docs +blockeditor-dimensioncontrol--with-custom-label +blockeditor-dimensioncontrol--with-initial-value +blockeditor-dimensioncontrol--with-min-height-label +blockeditor-dimensioncontrol--with-preset-value +blockeditor-dimensioncontrol--with-presets +blockeditor-dimensioncontrol--with-width-label +blockeditor-dimensionstool-aspectratiotool--default +blockeditor-dimensionstool-aspectratiotool--docs +blockeditor-dimensionstool-dimensionstool--default +blockeditor-dimensionstool-dimensionstool--docs +blockeditor-dimensionstool-scaletool--default +blockeditor-dimensionstool-scaletool--docs +blockeditor-dimensionstool-widthheighttool--default +blockeditor-dimensionstool-widthheighttool--docs +blockeditor-fontfamilycontrol--default +blockeditor-fontfamilycontrol--docs +blockeditor-headingleveldropdown--default +blockeditor-headingleveldropdown--docs +blockeditor-heightcontrol--default +blockeditor-heightcontrol--docs +blockeditor-innerblocks--default +blockeditor-innerblocks--docs +blockeditor-inserter--docs +blockeditor-inserter--full-inserter +blockeditor-inserter--library-with-patterns +blockeditor-inserter--library-with-patterns-and-reusable-blocks +blockeditor-inserter--library-without-patterns +blockeditor-inserter--quick-inserter +blockeditor-letterspacingcontrol--default +blockeditor-letterspacingcontrol--docs +blockeditor-lineheightcontrol--default +blockeditor-lineheightcontrol--docs +blockeditor-lineheightcontrol--unconstrained-width +blockeditor-plaintext--default +blockeditor-plaintext--docs +blockeditor-resolutioncontrol--default +blockeditor-resolutioncontrol--docs +blockeditor-tabbedsidebar--default +blockeditor-tabbedsidebar--docs +blockeditor-textalignmentcontrol--default +blockeditor-textalignmentcontrol--docs +blockeditor-textdecorationcontrol--default +blockeditor-textdecorationcontrol--docs +blockeditor-texttransformcontrol--default +blockeditor-texttransformcontrol--docs +blockeditor-unitcontrol--default +blockeditor-unitcontrol--docs +blockeditor-urlpopover--default +blockeditor-urlpopover--docs +blockeditor-warning--default +blockeditor-warning--docs +blockeditor-warning--with-actions +blockeditor-warning--with-secondary-actions +blockeditor-writingmodecontrol--default +blockeditor-writingmodecontrol--docs +components-actions-button-best-practices--docs +components-alignmentmatrixcontrol--default +components-alignmentmatrixcontrol--docs +components-alignmentmatrixcontrol--icon-subcomponent +components-anglepickercontrol--default +components-anglepickercontrol--docs +components-animate--appear-bottom-left +components-animate--appear-bottom-right +components-animate--appear-top-left +components-animate--appear-top-right +components-animate--default +components-animate--docs +components-animate--slide-in +components-badge--default +components-badge--docs +components-badge--error +components-badge--info +components-badge--success +components-badge--warning +components-basecontrol--default +components-basecontrol--docs +components-basecontrol--with-help-text +components-basecontrol--with-visual-label +components-borderboxcontrol--default +components-borderboxcontrol--docs +components-borderboxcontrol--without-label +components-bordercontrol--default +components-bordercontrol--docs +components-bordercontrol--is-compact +components-bordercontrol--with-multiple-origins +components-bordercontrol--with-slider +components-bordercontrol--with-slider-custom-width +components-boxcontrol--arbitrary-sides +components-boxcontrol--axial-controls +components-boxcontrol--axial-controls-with-single-side +components-boxcontrol--control-with-presets +components-boxcontrol--controlled +components-boxcontrol--default +components-boxcontrol--docs +components-boxcontrol--single-side +components-button--default +components-button--docs +components-button--grouped-icons +components-button--icon +components-button--is-destructive +components-button--link +components-button--primary +components-button--secondary +components-button--tertiary +components-buttongroup--default +components-buttongroup--docs +components-card--default +components-card--docs +components-card--full-bleed-content +components-card--padding-variations +components-checkboxcontrol--default +components-checkboxcontrol--docs +components-checkboxcontrol--indeterminate +components-checkboxcontrol--migration-guide +components-checkboxcontrol--with-custom-label +components-checkboxcontrol--with-visual +components-circularoptionpicker--as-toggle-buttons +components-circularoptionpicker--default +components-circularoptionpicker--docs +components-circularoptionpicker--with-button-action +components-circularoptionpicker--with-dropdown-link-action +components-circularoptionpicker--with-looping-disabled +components-colorindicator--default +components-colorindicator--docs +components-colorpalette--css-variables +components-colorpalette--default +components-colorpalette--docs +components-colorpalette--duplicate-colors +components-colorpalette--initial-value +components-colorpalette--multiple-origins +components-colorpicker--default +components-colorpicker--docs +components-comboboxcontrol--default +components-comboboxcontrol--docs +components-comboboxcontrol--not-expand-on-focus +components-comboboxcontrol--with-custom-render-item +components-comboboxcontrol--with-disabled-options +components-composite--default +components-composite--docs +components-composite--grid +components-composite--groups +components-composite--hover +components-composite--typeahead +components-composite--with-slot-fill +components-composite--with-tooltips +components-composite-unstable--docs +components-composite-unstable--one-dimension-with-spread-props +components-composite-unstable--one-dimension-with-state-prop +components-composite-unstable--two-dimensions-with-spread-props +components-composite-unstable--two-dimensions-with-state-prop +components-confirmdialog--default +components-confirmdialog--docs +components-confirmdialog--with-custom-button-labels +components-contenteditablecontrol--default +components-contenteditablecontrol--docs +components-contributing-guidelines--docs +components-customgradientpicker--default +components-customgradientpicker--docs +components-customselectcontrol--default +components-customselectcontrol--docs +components-customselectcontrol--with-hints +components-customselectcontrol--with-long-labels +components-datepicker--default +components-datepicker--docs +components-datepicker--with-events +components-datepicker--with-invalid-dates +components-datetimepicker--default +components-datetimepicker--docs +components-datetimepicker--with-events +components-datetimepicker--with-invalid-dates +components-disabled--content-editable +components-disabled--default +components-disabled--docs +components-divider--docs +components-divider--horizontal +components-divider--in-flex-container +components-divider--vertical +components-draggable--append-element-to-owner-document +components-draggable--default +components-draggable--docs +components-dropdown--default +components-dropdown--docs +components-dropdown--with-menu-items +components-dropdown--with-more-padding +components-dropdown--with-no-padding +components-dropdownmenu--default +components-dropdownmenu--docs +components-dropdownmenu--with-children +components-dropzone--default +components-dropzone--docs +components-duotonepicker--default +components-duotonepicker--docs +components-duotonepicker--duplicate-duotones +components-duotoneswatch--default +components-duotoneswatch--docs +components-duotoneswatch--null +components-duotoneswatch--single-color +components-elevation--default +components-elevation--docs +components-elevation--with-custom-interactive +components-elevation--with-interactive +components-externallink--default +components-externallink--docs +components-flex--default +components-flex--docs +components-flex--responsive-direction +components-focalpointpicker--default +components-focalpointpicker--docs +components-focalpointpicker--image +components-focalpointpicker--snapping +components-focalpointpicker--video +components-fontsizepicker--default +components-fontsizepicker--docs +components-fontsizepicker--with-complex-css-values +components-fontsizepicker--with-custom-sizes-disabled +components-fontsizepicker--with-more-font-sizes +components-fontsizepicker--with-slider +components-fontsizepicker--with-units +components-formfileupload--allow-multiple-files +components-formfileupload--default +components-formfileupload--docs +components-formfileupload--restrict-file-types +components-formfileupload--with-custom-render +components-formfileupload--with-icon +components-formtoggle--default +components-formtoggle--docs +components-formtokenfield--async +components-formtokenfield--default +components-formtokenfield--docs +components-formtokenfield--dropdown-selector +components-formtokenfield--validate-new-tokens +components-formtokenfield--with-custom-rendered-items +components-gradientpicker--css-variables +components-gradientpicker--default +components-gradientpicker--docs +components-gradientpicker--duplicate-gradients +components-gradientpicker--multiple-origins +components-gradientpicker--with-no-existing-gradients +components-grid--default +components-grid--docs +components-guide--default +components-guide--docs +components-heading--default +components-heading--docs +components-hstack--default +components-hstack--docs +components-icon--default +components-icon--docs +components-icon--with-a-component +components-icon--with-a-dashicon +components-icon--with-a-function +components-icon--with-an-svg +components-inputcontrol--default +components-inputcontrol--docs +components-inputcontrol--migration-guide +components-inputcontrol--show-password +components-inputcontrol--with-edge-label +components-inputcontrol--with-help-text +components-inputcontrol--with-icon-or-control +components-inputcontrol--with-prefix +components-inputcontrol--with-side-label +components-inputcontrol--with-suffix +components-introduction--docs +components-itemgroup--custom-item-size +components-itemgroup--default +components-itemgroup--docs +components-itemgroup--non-clickable-items +components-itemgroup--without-border +components-keyboardshortcuts--default +components-keyboardshortcuts--docs +components-menu--best-practices +components-menu--default +components-menu--docs +components-menu--inside-modal +components-menu--toolbar-variant +components-menu--with-checkboxes +components-menu--with-modal +components-menu--with-radios +components-menu--with-slot-fill +components-menu--with-submenu +components-menugroup--default +components-menugroup--docs +components-menugroup--with-separator +components-menuitem--default +components-menuitem--docs +components-menuitem--is-selected +components-menuitem--with-icon +components-menuitem--with-info +components-menuitem--with-suffix +components-menuitemschoice--default +components-menuitemschoice--docs +components-modal--default +components-modal--docs +components-modal--with-header-actions +components-modal--withsize-small +components-navigablemenu--default +components-navigablemenu--docs +components-navigator--default +components-navigator--docs +components-navigator--skip-focus +components-navigator--with-nested-initial-path +components-notice--default +components-notice--docs +components-notice--notice-list-subcomponent +components-notice--with-actions +components-notice--with-custom-spoken-message +components-notice--with-disabled-action +components-notice--with-jsx-children +components-numbercontrol--default +components-numbercontrol--docs +components-packages-introduction--docs +components-paletteedit--default +components-paletteedit--docs +components-paletteedit--duotones +components-paletteedit--gradients +components-panel--default +components-panel--disabled-section +components-panel--docs +components-panel--panel-row +components-panel--with-icon +components-placeholder--default +components-placeholder--docs +components-popover--all-placements +components-popover--default +components-popover--docs +components-popover--dynamic-height +components-popover--unstyled +components-popover--with-close-handlers +components-popover--with-slot-outside-iframe +components-progressbar--default +components-progressbar--docs +components-progressbar--with-custom-width +components-querycontrols--default +components-querycontrols--docs +components-querycontrols--select-single-category +components-radiocontrol--default +components-radiocontrol--docs +components-radiocontrol--migration-guide +components-radiocontrol--with-disabled-option +components-radiocontrol--with-option-descriptions +components-radiogroup--controlled +components-radiogroup--default +components-radiogroup--disabled +components-radiogroup--docs +components-rangecontrol--default +components-rangecontrol--docs +components-rangecontrol--with-any-step +components-rangecontrol--with-any-step-and-marks +components-rangecontrol--with-decimal-step-and-marks +components-rangecontrol--with-integer-step-and-marks +components-rangecontrol--with-negative-minimum-and-marks +components-rangecontrol--with-negative-range-and-marks +components-resizablebox--default +components-resizablebox--disabled-directions +components-resizablebox--docs +components-resizablebox--with-resize-tooltip +components-responsivewrapper--default +components-responsivewrapper--docs +components-responsivewrapper--with-svg +components-sandbox--default +components-sandbox--docs +components-sandbox--with-form +components-scrollable--default +components-scrollable--docs +components-scrolllock--default +components-scrolllock--docs +components-searchcontrol--default +components-searchcontrol--docs +components-selectcontrol--default +components-selectcontrol--docs +components-selectcontrol--minimal +components-selectcontrol--with-custom-children +components-selectcontrol--with-label-and-help-text +components-selectcontrol--with-prefix +components-selection-input-validated-form-controls-overview--docs +components-shortcut--default +components-shortcut--docs +components-shortcut--with-aria-label +components-slotfill--cross-document-styles +components-slotfill--default +components-slotfill--docs +components-slotfill--with-context +components-slotfill--with-fill-props +components-slotfill--with-slot-children +components-snackbar--default +components-snackbar--docs +components-snackbar--with-action-and-explicit-dismiss +components-snackbar--with-actions +components-snackbar--with-explicit-dismiss +components-snackbar--with-icon +components-snackbarlist--default +components-snackbarlist--docs +components-spacer--default +components-spacer--docs +components-spacer--nested +components-spinner--custom-size +components-spinner--default +components-spinner--docs +components-surface--default +components-surface--docs +components-tabbablecontainer--default +components-tabbablecontainer--docs +components-tabpanel--default +components-tabpanel--disabled-tab +components-tabpanel--docs +components-tabpanel--manual-activation +components-tabpanel--with-tab-icons-and-tooltips +components-tabs--best-practices +components-tabs--default +components-tabs--disabled-tab +components-tabs--docs +components-tabs--insert-custom-elements +components-tabs--manual-activation +components-tabs--size-and-overflow-playground +components-tabs--using-slot-fill +components-tabs--vertical +components-tabs--with-tab-icons-and-tooltips +components-text--default +components-text--docs +components-text--highlight +components-text--truncate +components-textareacontrol--default +components-textareacontrol--docs +components-textareacontrol--migration-guide +components-textcontrol--default +components-textcontrol--docs +components-textcontrol--with-label-and-help-text +components-texthighlight--default +components-texthighlight--docs +components-timepicker--default +components-timepicker--docs +components-timepicker--time-input +components-tip--default +components-tip--docs +components-togglecontrol--default +components-togglecontrol--docs +components-togglecontrol--migration-guide +components-togglecontrol--with-help-text +components-togglecontrol--with-visual +components-togglegroupcontrol--default +components-togglegroupcontrol--deselectable +components-togglegroupcontrol--disabled +components-togglegroupcontrol--docs +components-togglegroupcontrol--with-icons +components-togglegroupcontrol--with-tooltip +components-toolbar--default +components-toolbar--docs +components-toolbar--unstyled +components-toolbar--without-group +components-toolspanel--default +components-toolspanel--docs +components-toolspanel--with-conditional-default-control +components-toolspanel--with-conditionally-rendered-control +components-toolspanel--with-non-tools-panel-items +components-toolspanel--with-optional-items-plus-icon +components-toolspanel--with-slot-fill-items +components-tooltip--default +components-tooltip--docs +components-tooltip--keyboard-shortcut +components-tooltip--nested +components-treegrid--default +components-treegrid--docs +components-treeselect--default +components-treeselect--docs +components-truncate--character-count +components-truncate--default +components-truncate--docs +components-unitcontrol--default +components-unitcontrol--docs +components-unitcontrol--press-enter-to-change +components-unitcontrol--tweaking-the-number-input +components-unitcontrol--with-custom-units +components-unitcontrol--with-single-unit +components-validatedcontenteditablecontrol--default +components-validatedcontenteditablecontrol--docs +components-validatedcustomselectcontrol--default +components-validatedcustomselectcontrol--docs +components-validatedrangecontrol--default +components-validatedrangecontrol--docs +components-view--default +components-view--docs +components-visuallyhidden--default +components-visuallyhidden--docs +components-visuallyhidden--with-additional-class-names +components-visuallyhidden--with-forwarded-props +components-vstack--default +components-vstack--docs +components-zstack--default +components-zstack--docs +dataviews-dataform--data-adapter +dataviews-dataform--docs +dataviews-dataform--layout-card +dataviews-dataform--layout-details +dataviews-dataform--layout-mixed +dataviews-dataform--layout-panel +dataviews-dataform--layout-regular +dataviews-dataform--layout-row +dataviews-dataform--validation +dataviews-dataform--validation-panel-error-indicator +dataviews-dataform--visibility +dataviews-dataform-content--docs +dataviews-dataform-content--help-text +dataviews-dataform-content--high-level-help-text +dataviews-dataform-content--labels +dataviews-dataform-content--placeholders +dataviews-dataform-content--validation-messages +dataviews-dataviews--async-infinite-scroll +dataviews-dataviews--best-practices +dataviews-dataviews--docs +dataviews-dataviews--empty +dataviews-dataviews--free-composition +dataviews-dataviews--hierarchical-levels +dataviews-dataviews--infinite-scroll +dataviews-dataviews--layout-activity +dataviews-dataviews--layout-custom +dataviews-dataviews--layout-grid +dataviews-dataviews--layout-list +dataviews-dataviews--layout-table +dataviews-dataviews--minimal-ui +dataviews-dataviews--with-card +dataviews-dataviewspicker--default +dataviews-dataviewspicker--docs +dataviews-dataviewspicker--free-composition +dataviews-dataviewspicker--with-modal +dataviews-fieldtypes--all-component +dataviews-fieldtypes--array-component +dataviews-fieldtypes--boolean-component +dataviews-fieldtypes--color-component +dataviews-fieldtypes--date-component +dataviews-fieldtypes--date-time-component +dataviews-fieldtypes--docs +dataviews-fieldtypes--email-component +dataviews-fieldtypes--integer-component +dataviews-fieldtypes--media-component +dataviews-fieldtypes--no-type-component +dataviews-fieldtypes--number-component +dataviews-fieldtypes--password-component +dataviews-fieldtypes--telephone-component +dataviews-fieldtypes--text-component +dataviews-fieldtypes--time-component +dataviews-fieldtypes--url-component +design-system-components-alertdialog--async-confirm +design-system-components-alertdialog--controlled +design-system-components-alertdialog--custom-labels +design-system-components-alertdialog--default +design-system-components-alertdialog--docs +design-system-components-alertdialog--irreversible +design-system-components-alertdialog--menu-trigger +design-system-components-alertdialog--scrollable +design-system-components-alertdialog--with-custom-content +design-system-components-alertdialog--with-custom-z-index +design-system-components-badge--all-intents +design-system-components-badge--default +design-system-components-badge--docs +design-system-components-badge--draft +design-system-components-badge--high +design-system-components-badge--informational +design-system-components-badge--low +design-system-components-badge--medium +design-system-components-badge--none +design-system-components-badge--stable +design-system-components-badge-usage-guidelines--all-intents +design-system-components-badge-usage-guidelines--comment-status +design-system-components-badge-usage-guidelines--docs +design-system-components-badge-usage-guidelines--draft +design-system-components-badge-usage-guidelines--high +design-system-components-badge-usage-guidelines--incorrect-badge-with-icon +design-system-components-badge-usage-guidelines--informational +design-system-components-badge-usage-guidelines--low +design-system-components-badge-usage-guidelines--medium +design-system-components-badge-usage-guidelines--none +design-system-components-badge-usage-guidelines--page-status +design-system-components-badge-usage-guidelines--plugin-status +design-system-components-badge-usage-guidelines--stable +design-system-components-badge-usage-guidelines--text-only-badges +design-system-components-badge-usage-guidelines--with-adjacent-content-icon +design-system-components-breadcrumb--default +design-system-components-breadcrumb--docs +design-system-components-breadcrumb--long-labels-and-rtl +design-system-components-breadcrumb--responsive-states +design-system-components-breadcrumb--router-link-composition +design-system-components-button--all-tones-and-variants +design-system-components-button--compact +design-system-components-button--default +design-system-components-button--docs +design-system-components-button--loading +design-system-components-button--minimal +design-system-components-button--neutral +design-system-components-button--neutral-outline +design-system-components-button--outline +design-system-components-button--pressed +design-system-components-button--small +design-system-components-button--unstyled +design-system-components-button--with-icon +design-system-components-button--with-keyboard-shortcut +design-system-components-button-usage-guidelines--docs +design-system-components-button-usage-guidelines--use-button-for-actions +design-system-components-button-usage-guidelines--use-button-link-for-navigation +design-system-components-button-usage-guidelines--use-link-for-inline-navigation +design-system-components-buttonlink--all-tones-and-variants +design-system-components-buttonlink--compact +design-system-components-buttonlink--default +design-system-components-buttonlink--docs +design-system-components-buttonlink--minimal +design-system-components-buttonlink--neutral +design-system-components-buttonlink--neutral-outline +design-system-components-buttonlink--open-in-new-tab +design-system-components-buttonlink--outline +design-system-components-buttonlink--small +design-system-components-buttonlink--unstyled +design-system-components-buttonlink--with-icon +design-system-components-calendar-calendar--default +design-system-components-calendar-calendar--disabled-dates +design-system-components-calendar-calendar--docs +design-system-components-calendar-calendar--with-outside-days +design-system-components-calendar-calendar--with-selected-date-and-month +design-system-components-calendar-calendar--with-time-zone +design-system-components-calendar-rangecalendar--default +design-system-components-calendar-rangecalendar--disabled-dates +design-system-components-calendar-rangecalendar--docs +design-system-components-calendar-rangecalendar--with-outside-days +design-system-components-calendar-rangecalendar--with-range-constraints +design-system-components-calendar-rangecalendar--with-selected-range-and-month +design-system-components-calendar-rangecalendar--with-time-zone +design-system-components-calendar-usage-guidelines--docs +design-system-components-card--custom-semantics +design-system-components-card--default +design-system-components-card--docs +design-system-components-card--full-bleed-cover-only +design-system-components-card--full-bleed-cover-with-header +design-system-components-card--full-bleed-hero-only +design-system-components-card--full-bleed-hero-with-title +design-system-components-card--header-only +design-system-components-card--with-full-bleed +design-system-components-collapsible--controlled +design-system-components-collapsible--default +design-system-components-collapsible--default-open +design-system-components-collapsible--disabled +design-system-components-collapsible--docs +design-system-components-collapsible--hidden-until-found +design-system-components-collapsiblecard--compared-to-card +design-system-components-collapsiblecard--default +design-system-components-collapsiblecard--disabled +design-system-components-collapsiblecard--docs +design-system-components-collapsiblecard--full-bleed-cover-with-header +design-system-components-collapsiblecard--initially-opened +design-system-components-collapsiblecard--stacked +design-system-components-collapsiblecard--with-full-bleed +design-system-components-collapsiblecard--with-header-description +design-system-components-collapsiblecard--with-heading-element +design-system-components-dialog--all-sizes +design-system-components-dialog--default +design-system-components-dialog--docs +design-system-components-dialog--scrollable +design-system-components-dialog--with-custom-z-index +design-system-components-dialog--with-visually-hidden-title +design-system-components-dialog-usage-guidelines--alert-dialog-for-deliberate-interruption +design-system-components-dialog-usage-guidelines--dialog-for-focused-task +design-system-components-dialog-usage-guidelines--docs +design-system-components-dialog-usage-guidelines--drawer-for-contextual-editing +design-system-components-drawer--all-sides +design-system-components-drawer--controlled +design-system-components-drawer--default +design-system-components-drawer--docs +design-system-components-drawer--non-modal +design-system-components-drawer--scrollable +design-system-components-drawer--size-playground +design-system-components-drawer--with-custom-z-index +design-system-components-emptystate--default +design-system-components-emptystate--docs +design-system-components-emptystate--with-custom-visual +design-system-components-form-best-practices--docs +design-system-components-form-best-practices--labeling-composed-components +design-system-components-form-checkbox-groups--docs +design-system-components-form-checkbox-groups--grouping-checkboxes +design-system-components-form-checkbox-groups--nesting +design-system-components-form-checkboxcontrol--default +design-system-components-form-checkboxcontrol--disabled +design-system-components-form-checkboxcontrol--docs +design-system-components-form-checkboxcontrol--visually-hidden-label +design-system-components-form-checkboxcontrol--with-description +design-system-components-form-checkboxcontrol--with-details +design-system-components-form-checkboxcontrol--with-long-label +design-system-components-form-inputcontrol--date +design-system-components-form-inputcontrol--default +design-system-components-form-inputcontrol--disabled +design-system-components-form-inputcontrol--docs +design-system-components-form-inputcontrol--number +design-system-components-form-inputcontrol--number-with-steppers +design-system-components-form-inputcontrol--password +design-system-components-form-inputcontrol--visually-hidden-label +design-system-components-form-inputcontrol--with-details +design-system-components-form-inputcontrol--with-prefix +design-system-components-form-inputcontrol--with-suffix-control +design-system-components-form-primitives--overview +design-system-components-form-primitives-autocomplete--async-items +design-system-components-form-primitives-autocomplete--default +design-system-components-form-primitives-autocomplete--docs +design-system-components-form-primitives-autocomplete--grid +design-system-components-form-primitives-autocomplete--grouped +design-system-components-form-primitives-autocomplete--inline +design-system-components-form-primitives-autocomplete--inline-mention-autocomplete +design-system-components-form-primitives-autocomplete--open-only-on-match +design-system-components-form-primitives-autocomplete--with-custom-z-index +design-system-components-form-primitives-autocomplete--with-item-descriptions +design-system-components-form-primitives-autocomplete--with-search-icon-and-clear-button +design-system-components-form-primitives-autocomplete--with-separator +design-system-components-form-primitives-checkbox--checked +design-system-components-form-primitives-checkbox--default +design-system-components-form-primitives-checkbox--disabled +design-system-components-form-primitives-checkbox--disabled-checked +design-system-components-form-primitives-checkbox--docs +design-system-components-form-primitives-checkbox--indeterminate +design-system-components-form-primitives-checkboxgroup--default +design-system-components-form-primitives-checkboxgroup--docs +design-system-components-form-primitives-checkboxgroup--with-parent-checkbox +design-system-components-form-primitives-combobox--async-items +design-system-components-form-primitives-combobox--compact +design-system-components-form-primitives-combobox--creatable +design-system-components-form-primitives-combobox--default +design-system-components-form-primitives-combobox--detached-inline +design-system-components-form-primitives-combobox--docs +design-system-components-form-primitives-combobox--grouped +design-system-components-form-primitives-combobox--with-custom-trigger-and-item +design-system-components-form-primitives-combobox--with-custom-z-index +design-system-components-form-primitives-combobox--with-separator +design-system-components-form-primitives-controlwitherror--default +design-system-components-form-primitives-controlwitherror--docs +design-system-components-form-primitives-controlwitherror--with-custom-validity +design-system-components-form-primitives-field--default +design-system-components-form-primitives-field--docs +design-system-components-form-primitives-field--hidden-label +design-system-components-form-primitives-field--using-aria-labelledby +design-system-components-form-primitives-field--using-html-for +design-system-components-form-primitives-field--with-details +design-system-components-form-primitives-field--with-visual-label +design-system-components-form-primitives-fieldset--default +design-system-components-form-primitives-fieldset--docs +design-system-components-form-primitives-fieldset--hidden-legend +design-system-components-form-primitives-fieldset--with-details +design-system-components-form-primitives-input--default +design-system-components-form-primitives-input--disabled +design-system-components-form-primitives-input--docs +design-system-components-form-primitives-input--with-prefix +design-system-components-form-primitives-input--with-suffix-control +design-system-components-form-primitives-inputlayout--compact +design-system-components-form-primitives-inputlayout--default +design-system-components-form-primitives-inputlayout--docs +design-system-components-form-primitives-inputlayout--small +design-system-components-form-primitives-inputlayout--with-padded-prefix +design-system-components-form-primitives-inputlayout--with-prefix +design-system-components-form-primitives-inputlayout--with-suffix-control +design-system-components-form-primitives-radio--checked +design-system-components-form-primitives-radio--default +design-system-components-form-primitives-radio--disabled +design-system-components-form-primitives-radio--disabled-checked +design-system-components-form-primitives-radio--docs +design-system-components-form-primitives-radiogroup--checked +design-system-components-form-primitives-radiogroup--default +design-system-components-form-primitives-radiogroup--disabled +design-system-components-form-primitives-radiogroup--docs +design-system-components-form-primitives-searchablechipselect--default +design-system-components-form-primitives-searchablechipselect--docs +design-system-components-form-primitives-searchablechipselect--grouped +design-system-components-form-primitives-searchablechipselect--with-custom-chips-and-items +design-system-components-form-primitives-searchablechipselect--with-custom-empty-content +design-system-components-form-primitives-searchablechipselect--with-item-descriptions +design-system-components-form-primitives-searchablechipselect--with-separator +design-system-components-form-primitives-searchablechipselect--without-clear-button +design-system-components-form-primitives-searchableselect--default +design-system-components-form-primitives-searchableselect--docs +design-system-components-form-primitives-searchableselect--grouped +design-system-components-form-primitives-searchableselect--with-custom-empty-content +design-system-components-form-primitives-searchableselect--with-custom-placeholder +design-system-components-form-primitives-searchableselect--with-custom-trigger-and-items +design-system-components-form-primitives-searchableselect--with-item-descriptions +design-system-components-form-primitives-searchableselect--with-separator +design-system-components-form-primitives-select--compact +design-system-components-form-primitives-select--default +design-system-components-form-primitives-select--disabled +design-system-components-form-primitives-select--docs +design-system-components-form-primitives-select--grouped +design-system-components-form-primitives-select--labeling +design-system-components-form-primitives-select--minimal +design-system-components-form-primitives-select--with-custom-placeholder +design-system-components-form-primitives-select--with-custom-trigger-and-item +design-system-components-form-primitives-select--with-custom-z-index +design-system-components-form-primitives-select--with-disabled-item +design-system-components-form-primitives-select--with-item-description +design-system-components-form-primitives-select--with-long-list +design-system-components-form-primitives-select--with-null-value-option +design-system-components-form-primitives-select--with-overflow +design-system-components-form-primitives-select--with-separator +design-system-components-form-primitives-switch--checked +design-system-components-form-primitives-switch--default +design-system-components-form-primitives-switch--disabled +design-system-components-form-primitives-switch--disabled-checked +design-system-components-form-primitives-switch--docs +design-system-components-form-primitives-textarea--default +design-system-components-form-primitives-textarea--disabled +design-system-components-form-primitives-textarea--docs +design-system-components-form-primitives-textarea--with-one-row +design-system-components-form-primitives-textarea--with-overflow +design-system-components-form-primitives-validityindicator--docs +design-system-components-form-primitives-validityindicator--invalid +design-system-components-form-primitives-validityindicator--valid +design-system-components-form-primitives-validityindicator--validating +design-system-components-form-radiogroupcontrol--default +design-system-components-form-radiogroupcontrol--disabled +design-system-components-form-radiogroupcontrol--docs +design-system-components-form-radiogroupcontrol--visually-hidden-label +design-system-components-form-radiogroupcontrol--with-description +design-system-components-form-radiogroupcontrol--with-details +design-system-components-form-radiogroupcontrol--with-disabled-item +design-system-components-form-radiogroupcontrol--with-long-label +design-system-components-form-searchablechipselectcontrol--async-items +design-system-components-form-searchablechipselectcontrol--creatable +design-system-components-form-searchablechipselectcontrol--default +design-system-components-form-searchablechipselectcontrol--docs +design-system-components-form-searchablechipselectcontrol--grouped +design-system-components-form-searchablechipselectcontrol--grouped-creatable +design-system-components-form-searchablechipselectcontrol--popup-width +design-system-components-form-searchablechipselectcontrol--visually-hidden-label +design-system-components-form-searchablechipselectcontrol--with-custom-chips-and-items +design-system-components-form-searchablechipselectcontrol--with-custom-empty-content +design-system-components-form-searchablechipselectcontrol--with-custom-search-placeholder +design-system-components-form-searchablechipselectcontrol--with-details +design-system-components-form-searchablechipselectcontrol--with-disabled-option +design-system-components-form-searchablechipselectcontrol--with-item-descriptions +design-system-components-form-searchablechipselectcontrol--with-separator +design-system-components-form-searchablechipselectcontrol--without-clear-button +design-system-components-form-searchableselectcontrol--async-items +design-system-components-form-searchableselectcontrol--creatable +design-system-components-form-searchableselectcontrol--default +design-system-components-form-searchableselectcontrol--docs +design-system-components-form-searchableselectcontrol--grouped +design-system-components-form-searchableselectcontrol--grouped-creatable +design-system-components-form-searchableselectcontrol--popup-width +design-system-components-form-searchableselectcontrol--visually-hidden-label +design-system-components-form-searchableselectcontrol--with-custom-empty-content +design-system-components-form-searchableselectcontrol--with-custom-placeholder +design-system-components-form-searchableselectcontrol--with-custom-search-placeholder +design-system-components-form-searchableselectcontrol--with-custom-trigger-and-items +design-system-components-form-searchableselectcontrol--with-details +design-system-components-form-searchableselectcontrol--with-disabled-option +design-system-components-form-searchableselectcontrol--with-item-descriptions +design-system-components-form-searchableselectcontrol--with-separator +design-system-components-form-selectcontrol--default +design-system-components-form-selectcontrol--docs +design-system-components-form-selectcontrol--grouped +design-system-components-form-selectcontrol--popup-width +design-system-components-form-selectcontrol--visually-hidden-label +design-system-components-form-selectcontrol--with-custom-placeholder +design-system-components-form-selectcontrol--with-custom-trigger-and-items +design-system-components-form-selectcontrol--with-details +design-system-components-form-selectcontrol--with-disabled-option +design-system-components-form-selectcontrol--with-item-description +design-system-components-form-selectcontrol--with-items-array-and-partial-customization +design-system-components-form-selectcontrol--with-null-value-option +design-system-components-form-selectcontrol--with-separator +design-system-components-form-switchcontrol--default +design-system-components-form-switchcontrol--disabled +design-system-components-form-switchcontrol--disabled-checked +design-system-components-form-switchcontrol--docs +design-system-components-form-switchcontrol--visually-hidden-label +design-system-components-form-switchcontrol--with-description +design-system-components-form-switchcontrol--with-details +design-system-components-form-switchcontrol--with-long-label +design-system-components-form-textareacontrol--default +design-system-components-form-textareacontrol--disabled +design-system-components-form-textareacontrol--docs +design-system-components-form-textareacontrol--resize +design-system-components-form-textareacontrol--visually-hidden-label +design-system-components-form-textareacontrol--with-details +design-system-components-form-textareacontrol--with-overflow +design-system-components-form-with-validation-validatedinputcontrol--default +design-system-components-form-with-validation-validatedinputcontrol--docs +design-system-components-form-with-validation-validatedinputcontrol--password +design-system-components-form-with-validation-validatedinputcontrol--with-custom-validity +design-system-components-form-with-validation-validatedtextareacontrol--default +design-system-components-form-with-validation-validatedtextareacontrol--docs +design-system-components-form-with-validation-validatedtextareacontrol--with-custom-validity +design-system-components-icon--default +design-system-components-icon--docs +design-system-components-iconbutton--default +design-system-components-iconbutton--disabled +design-system-components-iconbutton--docs +design-system-components-iconbutton--minimal +design-system-components-iconbutton--neutral +design-system-components-iconbutton--neutral-outline +design-system-components-iconbutton--outline +design-system-components-iconbutton--pressed +design-system-components-iconbutton--with-custom-positioner +design-system-components-iconbutton--with-different-icons +design-system-components-iconbutton--with-shortcut +design-system-components-introduction--docs +design-system-components-link--all-tones-and-variants +design-system-components-link--default +design-system-components-link--docs +design-system-components-link--inline +design-system-components-link--open-in-new-tab +design-system-components-link--standalone +design-system-components-menu--checkbox-items +design-system-components-menu--default +design-system-components-menu--docs +design-system-components-menu--grouped-items +design-system-components-menu--link-item +design-system-components-menu--positioning +design-system-components-menu--radio-items +design-system-components-menu--rich-items +design-system-components-menu--submenu +design-system-components-menu--with-custom-z-index +design-system-components-menu-usage-guidelines--autocomplete-for-searchable-commands +design-system-components-menu-usage-guidelines--checked-options-within-a-command-menu +design-system-components-menu-usage-guidelines--docs +design-system-components-menu-usage-guidelines--menu-for-commands +design-system-components-menu-usage-guidelines--select-for-values +design-system-components-meter--bar-only +design-system-components-meter--brand +design-system-components-meter--custom-value-format +design-system-components-meter--default +design-system-components-meter--docs +design-system-components-meter-usage-guidelines--docs +design-system-components-notice--custom-icon +design-system-components-notice--default +design-system-components-notice--description-only +design-system-components-notice--docs +design-system-components-notice--error +design-system-components-notice--info +design-system-components-notice--non-dismissible +design-system-components-notice--success +design-system-components-notice--title-only +design-system-components-notice--warning +design-system-components-notice--without-actions +design-system-components-notice--without-icon +design-system-components-notice-announcements--docs +design-system-components-notice-announcements--polite-status +design-system-components-notice-announcements--static-notice +design-system-components-notice-announcements--urgent-error +design-system-components-popover--anchor +design-system-components-popover--collision-avoidance +design-system-components-popover--controlled +design-system-components-popover--cross-iframe +design-system-components-popover--default +design-system-components-popover--docs +design-system-components-popover--hover-trigger +design-system-components-popover--infotip +design-system-components-popover--infotip-with-link +design-system-components-popover--initial-focus +design-system-components-popover--inline +design-system-components-popover--modal +design-system-components-popover--no-arrow +design-system-components-popover--on-open-change-details +design-system-components-popover--overlay-placement +design-system-components-popover--positioning +design-system-components-popover--toolbar-variant +design-system-components-popover--trap-focus +design-system-components-popover--unstyled +design-system-components-popover--viewport-constrained-size +design-system-components-popover--with-close-button +design-system-components-popover--with-custom-z-index +design-system-components-progress--bar-only +design-system-components-progress--current-color +design-system-components-progress--custom-color +design-system-components-progress--custom-value-format +design-system-components-progress--default +design-system-components-progress--docs +design-system-components-progress--indeterminate +design-system-components-progress-usage-guidelines--docs +design-system-components-skeleton--card-placeholder +design-system-components-skeleton--circle +design-system-components-skeleton--default +design-system-components-skeleton--docs +design-system-components-skeleton--text-lines +design-system-components-spinner--current-color +design-system-components-spinner--custom-color +design-system-components-spinner--custom-size +design-system-components-spinner--default +design-system-components-spinner--docs +design-system-components-stack--default +design-system-components-stack--docs +design-system-components-stack--nested +design-system-components-tabs--default +design-system-components-tabs--docs +design-system-components-tabs--minimal +design-system-components-tabs--size-and-overflow-playground +design-system-components-tabs--vertical +design-system-components-tabs--with-disabled-tab +design-system-components-tabs--with-non-focusable-panels +design-system-components-tabs--with-panels-always-mounted +design-system-components-tabs--with-tab-icons-and-tooltips +design-system-components-tabs-best-practices--docs +design-system-components-text--all-variants +design-system-components-text--default +design-system-components-text--docs +design-system-components-text--with-render-prop +design-system-components-tooltip--default +design-system-components-tooltip--disabled +design-system-components-tooltip--docs +design-system-components-tooltip--positioning +design-system-components-tooltip--with-custom-positioner +design-system-components-tooltip--with-custom-z-index +design-system-components-tooltip--with-provider +design-system-components-tooltip-usage-guidelines--docs +design-system-components-tooltip-usage-guidelines--icon-button-with-tooltip +design-system-components-tooltip-usage-guidelines--infotip-with-popover +design-system-components-tooltip-usage-guidelines--recommended-usage +design-system-components-visuallyhidden--default +design-system-components-visuallyhidden--docs +design-system-components-visuallyhidden--with-custom-element +design-system-contributing--docs +design-system-form-with-validation-overview--async-validation +design-system-form-with-validation-overview--custom-errors-on-submit +design-system-form-with-validation-overview--docs +design-system-form-with-validation-overview--showing-errors-at-arbitrary-times +design-system-form-with-validation-overview--showing-errors-without-moving-focus +design-system-form-with-validation-overview--validate-in-modal +design-system-form-with-validation-overview--validate-on-popover-close +design-system-form-with-validation-overview--with-multiple-controls +design-system-introduction--docs +design-system-patterns-destructive-actions--docs +design-system-patterns-error-messages--docs +design-system-patterns-save-submit--docs +design-system-patterns-save-submit--save-form-layout-card +design-system-theme-introduction--docs +design-system-theme-theme-provider--default +design-system-theme-theme-provider--docs +design-system-theme-theme-provider--nesting-and-inheriting +design-system-theme-theme-provider--with-picker +design-system-theme-theme-provider-brand-color-fallbacks--default +design-system-theme-theme-provider-brand-color-fallbacks--docs +design-system-theme-theme-provider-color-scales--default +design-system-theme-theme-provider-color-scales--docs +design-system-theme-theme-provider-color-scales--sample-combinations +design-system-theme-theme-provider-example-application--docs +design-system-theme-theme-provider-example-application--example-application +design-system-tokens-introduction--docs +design-system-tokens-motion--default +design-system-tokens-motion--docs +design-system-tokens-typography--docs +editor-uploadprogresssnackbar--all-states +editor-uploadprogresssnackbar--complete +editor-uploadprogresssnackbar--docs +editor-uploadprogresssnackbar--uploading +editor-uploadprogresssnackbar--uploading-multiple +fields-base-fields--data-forms-preview +fields-base-fields--data-views-preview +fields-base-fields--docs +fields-media-fields--data-forms-preview +fields-media-fields--data-views-preview +fields-media-fields--docs +foundations-design-language-elevation--page +foundations-design-language-radius--page +foundations-layout--page +globalstylesui-globalstylesui--docs +globalstylesui-globalstylesui--global-styles-ui +grid-dashboardgrid--custom-grid-overlay-story +grid-dashboardgrid--customization +grid-dashboardgrid--default +grid-dashboardgrid--docs +grid-dashboardgrid--edit-mode +grid-dashboardgrid--fill-width +grid-dashboardgrid--full-width +grid-dashboardgrid--layered +grid-dashboardgrid--responsive +grid-dashboardgrid--row-height +grid-dashboardgrid--size-limits +grid-dashboardlanes--custom-grid-overlay-story +grid-dashboardlanes--default +grid-dashboardlanes--docs +grid-dashboardlanes--edit-mode +grid-dashboardlanes--layered +grid-dashboardlanes--responsive +grid-dashboardlanes--size-limits +grid-dashboardlanes--spanning +icons-icon--current-color +icons-icon--default +icons-icon--docs +icons-icon--library +icons-icon--stroke-scaling +imagecropper-imagecropper--default +imagecropper-imagecropper--docs +imagecropper-imagecropper--with-controls +introduction--page +mediaeditor-imageeditor--debug +mediaeditor-imageeditor--default +mediaeditor-imageeditor--docs +mediaeditor-imageeditor--with-controls +mediaeditor-rotationruler--default +mediaeditor-rotationruler--docs +playground-block-editor--box +playground-block-editor--default +playground-block-editor--docs +playground-block-editor--undo-redo +playground-block-editor--zoom-out +playground-debug-fixtures-draggable-cross-document-fallback--docs +playground-debug-fixtures-draggable-cross-document-fallback--inside-iframe-with-compat-slot +playground-debug-fixtures-popover-with-slotfill--cross-iframe-with-slot-fill +playground-debug-fixtures-popover-with-slotfill--docs +playground-debug-fixtures-wp-compat-overlay-slot--docs +playground-debug-fixtures-wp-compat-overlay-slot--inside-components-modal +playground-debug-fixtures-wp-compat-overlay-slot--inside-components-popover +widget-dashboard-anatomy--docs +widget-dashboard-introduction--docs +widget-dashboard-playground--default +widget-dashboard-playground--docs +widget-dashboard-playground--grid-settings +widget-dashboard-playground--host-links +widget-dashboard-playground--long-title +widget-dashboard-playground--policy +widget-dashboard-playground--runtime-actions +widget-dashboard-playground-debug--docs +widget-dashboard-playground-debug--staging-enforcement +widget-dashboard-policy--docs +widget-dashboard-widget-chrome--docs +widget-primitives-actions--docs +widget-primitives-anatomy--docs +widget-primitives-field-types--docs +widget-primitives-icons--docs +widget-primitives-introduction--docs +widget-primitives-system-architecture--docs +widget-primitives-widget-host--docs +widget-primitives-widgetrender--default +widget-primitives-widgetrender--docs +widget-primitives-widgetrender--with-actions +widget-primitives-widgetrender--with-field-type +widget-primitives-widgetrender--with-host-chrome +widget-primitives-widgetrender--with-host-link +widget-primitives-widgetrender--with-icon-reference +widget-primitives-widgetrender--with-relevance +widget-primitives-widgetrender--with-settings diff --git a/storybook/test/story-ids.test.ts b/storybook/test/story-ids.test.ts new file mode 100644 index 00000000000000..11af3aa87d1b3e --- /dev/null +++ b/storybook/test/story-ids.test.ts @@ -0,0 +1,59 @@ +import { globSync, readFileSync } from 'node:fs'; +import path from 'node:path'; +import { analyzeMdx } from 'storybook/internal/core-server'; +import { loadCsf } from 'storybook/internal/csf-tools'; +import { describe, expect, it } from 'vitest'; +import { storyGlobs } from '../story-globs'; + +const CONFIG_DIR = path.join( __dirname, '..' ); + +/** + * Lists every story and doc Storybook indexes. + * + * @return Paths relative to the Storybook config directory. + */ +function findStoryFiles() { + return globSync( storyGlobs, { cwd: CONFIG_DIR } ).sort(); +} + +/** + * Whether a story or doc pins its own URL with an `id`. + * + * An MDX doc attached to a CSF file with `of={ ... }` inherits that file's ID, + * so it has nothing of its own to declare. + * + * @param file Path to the story or doc, relative to the Storybook config. + * @return Resolves to `true` when the file's URL cannot move with its title. + */ +async function hasStableId( file: string ) { + const source = readFileSync( path.join( CONFIG_DIR, file ), 'utf8' ); + + if ( file.endsWith( '.mdx' ) ) { + // Storybook's own MDX analyzer, which the indexer builds docs IDs from. + const { id, of } = await analyzeMdx( source ); + return Boolean( id || of ); + } + + // Storybook's own CSF parser only fills in `id` when the meta declares one. + const csf = loadCsf( source, { + fileName: file, + makeTitle: ( title ) => title, + } ).parse(); + return Boolean( csf._meta?.id ); +} + +describe( 'story IDs', () => { + const files = findStoryFiles(); + + it( 'covers every story and doc', () => { + // Guards against the walk silently matching nothing, which would let + // the check below pass without reading a single file. + expect( files.length ).toBeGreaterThan( 250 ); + } ); + + // A story's URL is built from its `id`, falling back to its `title`. Only a + // declared `id` keeps the URL from moving when the story does. + it.each( files )( '%s declares an id', async ( file ) => { + expect( await hasStableId( file ) ).toBe( true ); + } ); +} );