Skip to content

Controls: Load controls for composed-ref stories on direct deep-link#35402

Merged
ndelangen merged 2 commits into
nextfrom
sdl/fix-composed-ref-controls-argtypes
Jul 8, 2026
Merged

Controls: Load controls for composed-ref stories on direct deep-link#35402
ndelangen merged 2 commits into
nextfrom
sdl/fix-composed-ref-controls-argtypes

Conversation

@ndelangen

@ndelangen ndelangen commented Jul 7, 2026

Copy link
Copy Markdown
Member

Closes #34553

What I did

Composed-ref Controls still didn't work when a ref story is opened via a direct deep-link (the exact way it gets QA'd): the panel shows "This story has no controls" even for stories that clearly have args.

Background / history

#34553 reported that Controls never load for a story served from a composed Storybook ref. It went through a couple of rounds:

During a secondary QA pass on v10.5.0-alpha.11, composed-ref Controls still failed — but with a different symptom. #35050 correctly cleared the loading skeleton, so instead of "loading forever" the panel now renders the "No controls" empty state. Because the user still never sees controls, it read as "the same bug" and didn't pass QA. This was never a regression — it's a second, independent bug that was masked by the loading-skeleton bug and is present in 10.4.x too.

Root cause

A composed ref's story entries are enriched at runtime from the ref preview's STORY_PREPARED event (args, argTypes, parameters, prepared). That enrichment is not part of the ref's static story index. Two things then conspire:

  1. setRef rebuilds the ref's index from the static story index (which has no argTypes), wiping any runtime enrichment on every (re)composition.
  2. On a direct deep-link, the ref preview emits STORY_PREPARED before the ref index has been composed, so updateStory's if (index && index[storyId]) guard fails and the enrichment is dropped outright.

Since the ref preview already rendered the story, it doesn't re-emit STORY_PREPARED, so the enrichment never comes back → useArgTypes() returns {} → "No controls". (In-app navigation happened to work because the ref index was already composed before opening the story.)

The fix

Cache the runtime enrichment on the ref itself (ref.storyUpdates) and re-apply it whenever the ref index is (re)built, so it survives index rebuilds and works regardless of whether it arrives before or after the index is composed:

  • updateStory / updateDocs (ref branch) merge each update into ref.storyUpdates (in addition to the live index entry when present).
  • setRef re-applies ref.storyUpdates onto the freshly built index/filteredIndex.
  • New type API_RefStoryRuntimeData + API_LoadedRefData.storyUpdates.

The ControlsPanel fix from #35050 is left intact — it correctly handles the loading half. This change completes the other half.

Checklist for Contributors

Testing

The changes in this PR are covered in the following automated tests:

  • stories
  • unit tests
  • integration tests
  • end-to-end tests

Added a regression test in refs.test.ts (cached runtime enrichment is re-applied when setRef rebuilds the index) and updated the STORY_ARGS_UPDATED assertion in stories.test.ts.

Manual testing

  1. Serve a Storybook that composes another Storybook via refs (e.g. the composed-ref repro from [Bug]: Controls never load with two Storybooks (refs) if initial page is a ref story #34553, or point the internal Storybook UI — which composes the Icons ref — at these changes).
  2. Open the host Storybook and deep-link directly to a composed-ref story that has args, e.g. http://localhost:6006/?path=/story/icons_icons-arrowtoplefticon--default (hard reload).
  3. Open the Controls tab: it should show the story's controls instead of "This story has no controls".
  4. Navigate to a host story and back to confirm both keep working.

Checklist for Maintainers

  • When this PR is ready for testing, make sure to add ci:normal, ci:merged or ci:daily GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in code/lib/cli-storybook/src/sandbox-templates.ts

  • Declare whether manual QA will be needed for this PR during the next release, through qa:needed or qa:skip

  • Make sure this PR contains one of the labels below:

    Available labels
    • bug: Internal changes that fixes incorrect behavior.
    • maintenance: User-facing maintenance tasks.
    • dependencies: Upgrading (sometimes downgrading) dependencies.
    • build: Internal-facing build tooling & test updates. Will not show up in release changelog.
    • cleanup: Minor cleanup style change. Will not show up in release changelog.
    • documentation: Documentation only changes. Will not show up in release changelog.
    • feature request: Introducing a new feature.
    • BREAKING CHANGE: Changes that break compatibility in some way with current major version.
    • other: Changes that don't fit in the above categories.

🦋 Canary release

This PR does not have a canary release associated. You can request a canary release of this pull request by mentioning the @storybookjs/core team here.

core team members can create a canary release here or locally with gh workflow run --repo storybookjs/storybook publish.yml --field pr=<PR_NUMBER>

Made with Cursor

Summary by CodeRabbit

  • Bug Fixes
    • Preserved runtime “prepared” story/docs details (including args/argTypes) when ref indexes are rebuilt, keeping metadata consistent after updates.
    • Fixed scenarios where enriched story information could be lost during refreshes or when runtime updates arrived before the initial index composition.
  • New Features
    • Added caching and propagation of runtime story/docs enrichment for composed refs, ensuring consistent story/docs presentation across ref changes.

Fixes composed-ref Controls showing "No controls" on a direct deep-link.
A ref's runtime story enrichment (args, argTypes, parameters, prepared)
arrives via STORY_PREPARED but is not part of the ref's static story index,
so `setRef` rebuilding the index wiped it — and on a deep-link the enrichment
can arrive before the index is composed, so `updateStory` dropped it entirely.

Cache the enrichment per ref (`ref.storyUpdates`) and re-apply it whenever the
ref index is (re)built, making it order-independent.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ndelangen ndelangen added bug ci:normal Run our default set of CI jobs (choose this for most PRs). qa:skip Pull Requests that do not need any QA. labels Jul 7, 2026
@ndelangen ndelangen self-assigned this Jul 7, 2026
@ndelangen ndelangen marked this pull request as ready for review July 7, 2026 18:11
@ndelangen ndelangen requested review from Sidnioulz and Copilot July 7, 2026 18:11

Copilot AI 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.

Pull request overview

Fixes composed-ref Controls not showing when a ref story is opened via a direct deep-link by preserving and reapplying runtime story/doc enrichment (from STORY_PREPARED / DOCS_PREPARED) across ref index (re)composition.

Changes:

  • Introduces storyUpdates on composed refs to cache runtime enrichment (args, argTypes, etc.) and reapply it when setRef rebuilds the ref index.
  • Updates ref-side story/docs update flows to persist runtime updates even if they arrive before the ref index exists.
  • Adds/updates unit tests to cover the cached enrichment behavior and the updated updateRef payload shape.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
code/core/src/types/modules/api.ts Adds storyUpdates to API_LoadedRefData / composed ref update type surface.
code/core/src/types/modules/api-stories.ts Introduces API_RefStoryRuntimeData type for cached runtime enrichment.
code/core/src/manager-api/modules/stories.ts Caches runtime enrichment into ref.storyUpdates when updates arrive for ref stories/docs.
code/core/src/manager-api/modules/refs.ts Re-applies cached runtime enrichment onto freshly rebuilt ref indexes inside setRef.
code/core/src/manager-api/tests/stories.test.ts Updates expectations to include storyUpdates in updateRef payload.
code/core/src/manager-api/tests/refs.test.ts Adds regression test ensuring cached runtime enrichment survives index rebuilds.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread code/core/src/types/modules/api.ts
Comment thread code/core/src/types/modules/api-stories.ts
Comment thread code/core/src/manager-api/modules/stories.ts
Comment thread code/core/src/manager-api/modules/stories.ts
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0cdb19e2-248e-40da-8a0b-272cbc7d7752

📥 Commits

Reviewing files that changed from the base of the PR and between b733e29 and d6a1789.

📒 Files selected for processing (1)
  • code/core/src/types/modules/api.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • code/core/src/types/modules/api.ts

📝 Walkthrough

Walkthrough

Adds API_RefStoryRuntimeData for cached runtime story/docs enrichment on composed refs. updateStory and updateDocs now store updates in ref.storyUpdates and pass them through updateRef. refs.ts reapplies cached updates when rebuilding index and filteredIndex, and tests cover the new persistence path.

Changes

Runtime Story Enrichment Persistence

Layer / File(s) Summary
Runtime data type and API surface
code/core/src/types/modules/api-stories.ts, code/core/src/types/modules/api.ts
Adds API_RefStoryRuntimeData and exposes storyUpdates on loaded ref data and composed ref updates.
Cache runtime updates on ref during updateStory/updateDocs
code/core/src/manager-api/modules/stories.ts, code/core/src/manager-api/tests/stories.test.ts
Caches story/docs updates on ref.storyUpdates and forwards them in fullAPI.updateRef; updates the ref-update test expectation.
Reapply cached updates during ref index rebuild
code/core/src/manager-api/modules/refs.ts, code/core/src/manager-api/tests/refs.test.ts
Adds applyRefStoryUpdates and applies it while recomposing index and filteredIndex; new test checks cached enrichment survives rebuild.

Estimated code review effort: 3 (Moderate) | ~25 minutes

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

…mment

Align the API_LoadedRefData.storyUpdates doc comment with the actual cached
shape, which also includes initialArgs, per PR review feedback.

Co-authored-by: Cursor <cursoragent@cursor.com>

@Sidnioulz Sidnioulz 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.

Tested, it works!

When navigating to host story first, and then to composed story, we see a flash of "This story has no controls" whilst controls are loading, but I find that acceptable. I'd only suggest showing a loading state for composed stories' controls if we can reliably detect whether the loading mechanism introduced here has completed yet or not.

@Sidnioulz Sidnioulz added the upgrade:10.5 Issues/PRs found during 10.5 upgrade QA and post-release regressions label Jul 8, 2026
@ndelangen ndelangen merged commit 5911d2f into next Jul 8, 2026
127 of 165 checks passed
@ndelangen ndelangen deleted the sdl/fix-composed-ref-controls-argtypes branch July 8, 2026 14:26
@github-actions github-actions Bot mentioned this pull request Jul 8, 2026
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug ci:normal Run our default set of CI jobs (choose this for most PRs). qa:skip Pull Requests that do not need any QA. upgrade:10.5 Issues/PRs found during 10.5 upgrade QA and post-release regressions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Controls never load with two Storybooks (refs) if initial page is a ref story

3 participants