Skip to content

Storybook: Make declared IDs the source of truth for URLs - #82123

Open
Mustafabharmal wants to merge 24 commits into
WordPress:trunkfrom
Mustafabharmal:add/storybook-stable-story-ids
Open

Mustafabharmal wants to merge 24 commits into
WordPress:trunkfrom
Mustafabharmal:add/storybook-stable-story-ids

Conversation

@Mustafabharmal

@Mustafabharmal Mustafabharmal commented Aug 27, 2026 •

Copy link
Copy Markdown
Contributor

What?

Closes #82110

Declares an explicit id on every Storybook story and doc that previously relied on an automatically generated ID.

This keeps existing Storybook URLs stable when stories are moved between sidebar categories, and adds checks to prevent IDs from being accidentally omitted or removed in the future.

Why?

A Storybook URL is based on the entry's ID.

When an entry does not declare an explicit id, Storybook derives the ID from its title. Since the title also controls the sidebar hierarchy, moving a story to a different category can change its URL.

For example:

Components/Card

can become:

Components/Containers/Card

and consequently change the generated ID from:

components-card

to:

components-containers-card

This can break existing links, bookmarks, documentation references, and other references to the Storybook page.

The existing id mechanism already provides a way to decouple the URL from the sidebar title. This change makes that behaviour explicit and consistent across the Storybook stories and docs.

How?

  • Added an explicit id to the stories and docs that were relying on generated IDs.
  • Used the IDs already generated by Storybook, so the existing URLs remain unchanged.
  • Added a unit test that checks every indexed story and doc declares an explicit ID.
  • Extracted the Storybook stories globs into storybook/story-globs.ts so the test uses the same set of files as the Storybook configuration.
  • Added a committed Storybook ID snapshot to detect published story URLs that disappear unexpectedly.
  • Documented the stable URL convention in storybook/README.md.

The IDs were obtained from Storybook's own indexer rather than being independently reconstructed, ensuring that the change does not alter the existing index.

For MDX docs attached to a CSF file with of={...}, the existing inherited ID behaviour is preserved.

Testing

Story index

The Storybook index was compared before and after the change:

Before: 1055 entries
After:  1055 entries

Added:   0
Removed: 0
Changed: 0

This confirms that declaring the IDs does not change the existing Storybook URLs.

ID enforcement

npm run test:unit -- storybook/test/story-ids

The test verifies that all indexed stories and docs declare an explicit ID.

Story ID snapshot

npm run storybook:story-id-snapshot

The snapshot is generated from the Storybook index without requiring a full Storybook build.

It also detects when a previously published story URL disappears.

Manual regression check

Changing a story title while keeping its explicit id confirms that the URL remains unchanged.

Testing Instructions

  1. Start from a clean checkout.
  2. Run the Storybook ID snapshot command:
npm run storybook:story-id-snapshot
  1. Verify that it completes without changes.
  2. Run the Storybook ID tests:
npm run test:unit -- storybook/test/story-ids
  1. Change the title of a story while keeping its explicit id.
  2. Rebuild/check the Storybook index and confirm the ID remains unchanged.
  3. Optionally remove an ID from a story and run the ID test again. The test should identify the missing ID.
  4. Remove a previously indexed story and regenerate the snapshot. The snapshot check should report the removed published URL.

Scope

The MetaProps gap is unrelated to the Gutenberg runtime change. Storybook's MDX MetaProps type currently does not expose id, although the runtime/indexer supports it. This can be addressed separately upstream.

Notes

The changes are intentionally limited to the Storybook URL stability problem. No changes are made to the generated IDs themselves, so existing Storybook URLs remain unchanged.

Use of AI Tools

Claude Code was used to implement and verify this change.

@github-actions github-actions Bot added [Package] Components /packages/components [Package] Editor /packages/editor [Package] Block editor /packages/block-editor [Package] Icons /packages/icons [Package] DataViews /packages/dataviews [Package] Fields /packages/fields [Package] Theme /packages/theme [Package] UI /packages/ui [Package] Image Cropper Related to packages/image-cropper [Package] Admin UI /packages/admin-ui [Package] Grid /packages/grid [Package] Widget primitives /packages/widget-primitives [Package] Widget Dashboard /packages/widget-dashboard labels Aug 27, 2026
@Mustafabharmal Mustafabharmal changed the title Add/storybook stable story ids Storybook: Make declared IDs the source of truth for URLs Aug 27, 2026
@simison
simison requested a review from juanmaguitar August 27, 2026 12:13

@juanmaguitar juanmaguitar left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Two small things, neither blocking:

  • icons-icon is deliberately declared in two files (packages/icons/src/icon/stories/index.story.tsx and storybook/stories/icons/library.story.tsx, which share a title and already merge into one entry). A one-line comment in each file pointing at the other would make that coupling visible, since changing only one of them would silently split the group.
  • The "Report the MetaProps type gap upstream" checkbox: fine to split it out, but let's open the follow-up issue and link it here so it doesn't get lost, since this PR closes #82110.

Overall the PR looks good to me. Nice work @Mustafabharmal ! However, given that this PR modifies several files related to the Storybook documentation, I'd like to get approval from the @WordPress/gutenberg-design team before moving forward with it.

@juanmaguitar
juanmaguitar marked this pull request as ready for review August 27, 2026 15:28
@juanmaguitar juanmaguitar removed [Package] Components /packages/components [Package] Editor /packages/editor [Package] Block editor /packages/block-editor [Package] Icons /packages/icons [Package] DataViews /packages/dataviews labels Aug 27, 2026
@github-actions github-actions Bot removed [Package] Admin UI /packages/admin-ui [Package] Grid /packages/grid [Package] Widget primitives /packages/widget-primitives [Package] Widget Dashboard /packages/widget-dashboard labels Sep 22, 2026
Comment thread storybook/test/story-ids.test.ts
Comment thread storybook/story-globs.ts
@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

🤖 PR meta 🤖

🎉 Props

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: Mustafabharmal <mustafabharmal@git.wordpress.org>
Co-authored-by: juanmaguitar <juanmaguitar@git.wordpress.org>
Co-authored-by: ciampo <mciampini@git.wordpress.org>
Co-authored-by: jasmussen <joen@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Updated as activity occurs, without notifying anyone named here. Add the props-bot label to refresh.

@github-actions github-actions Bot added the [Package] Icons /packages/icons label Oct 6, 2026

@ciampo ciampo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The "Report the MetaProps type gap upstream" checkbox: fine to split it out, but let's open the follow-up issue and link it here so it doesn't get lost, since this PR closes #82110.

This still needs to be done.

Also, it would be good to get an approval from @WordPress/gutenberg-design

Comment thread storybook/test/story-ids.test.ts Outdated
Comment thread storybook/scripts/generate-story-id-snapshot.mjs
Comment thread storybook/test/story-ids.test.ts Outdated
@ciampo
ciampo requested review from a team and mirka October 8, 2026 14:49
@jasmussen

Copy link
Copy Markdown
Contributor

I've been a bit removed from this, so it's hard for me to give too much design feedback. My understanding is that this makes it so any FUTURE structural changes we make (adding or more likely removing a few folders of nesting) don't cause broken permalinks, is that right? In that vein, sounds good to me, but I'll defer to others who have strong opinions or additional nuance.

@mirka mirka left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

My honest impression is that we're doing too much than is warranted at this time.

"Unintentional deletion" has not really been a thing. The enforcement of an explicit id already catches unintentional permalink changes. I don't think deletion protection is necessary on top of that.

And even if we did want snapshots, it isn't clear to me why we'd prefer a specifically generated and committed snapshot instead of reading storybook/build/index.json, which is already generated by Storybook.

But in any case, I would recommend starting with just the per-file check for the id.

Comment on lines +48 to +52
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 );
} );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not sure the magic number is necessary. Would a simple check for no matches suffice?

Comment on lines +53 to 56
// 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',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This doesn't seem like a good pattern. Normally if we wanted to split the library story into its own file, we would just put the code (e.g. just the render function) in a separate file and import it in the main story file. Having two duplicated meta objects like this is not recommended.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

At first glance this feels like something that should be done in eslint, but I guess that wouldn't handle mdx files. Just noting that decision here explicitly for posterity, in case the mdx concerns disappears in the future.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Design System Issues related to the system of combining components according to best practices. [Package] Icons /packages/icons [Package] Theme /packages/theme Storybook Storybook and its stories for components [Type] Developer Documentation Documentation for developers

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Storybook: Make declared IDs the source of truth for URLs

5 participants