Docs: Route ArgTypes and Controls through docgen service when flag enabled#35109
Docs: Route ArgTypes and Controls through docgen service when flag enabled#35109JReinhold wants to merge 16 commits into
Conversation
…abled Add Legacy/DocgenService splits for docs blocks and the manager Controls panel, mergeServiceArgTypes for layering custom argTypes at read time, and export getService for preview/manager consumers. Gated behind experimentalDocgenServer. Co-authored-by: Cursor <cursoragent@cursor.com>
Package BenchmarksCommit: No significant changes detected, all good. 👏 |
Replace useState/useEffect with useSyncExternalStore for tear-free, concurrent-safe reads of the core/docgen runtime. getDocgen reads state by reference, so snapshots stay Object.is-stable without an extra equality layer. Co-authored-by: Cursor <cursoragent@cursor.com>
…bled When experimentalDocgenServer is on, the docs blocks now consume React metadata from the docgen service, so stop react-docgen from injecting __docgenInfo into the preview bundle. Pairs the injection-off change with the service consumption in this PR so the swap lands atomically (moved here from the plumbing PR). Co-authored-by: Cursor <cursoragent@cursor.com>
The docgen service does not yet work in production builds, so keep the flag off for now. With it disabled, the docs blocks and controls panel route through the legacy extraction path and presets keep injecting __docgenInfo, so the normal non-experimental case is not regressed. Co-authored-by: Cursor <cursoragent@cursor.com>
- Use play-context canvas type in ArgTypes.stories instead of value-importing within - Assert service controls via rendered radio input, not arg name label text - Update StoryStore inline snapshots after argTypes fixture additions Co-authored-by: Cursor <cursoragent@cursor.com>
📝 WalkthroughWalkthroughThis PR implements a feature-flagged docgen-server integration: adds service consumers and merging utilities for argTypes, wires open-service getService exports, refactors ArgTypes/Controls to support legacy and service-backed rendering, updates ControlsPanel/manager wiring, adds docs/stories/tests, and gates preview/plugin injection via the experimentalDocgenServer flag. ChangesArgTypes and Controls Docgen Service Integration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
✨ Finishing Touches📝 Generate docstrings
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (3)
code/core/src/preview-api/index.ts (1)
91-92: ⚡ Quick winUpdate the open-service API banner comment.
Line 91 still says "register only", but Line 92 now re-exports
getServiceas well. Keeping that banner stale makes the public preview API surface harder to reason about.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@code/core/src/preview-api/index.ts` around lines 91 - 92, The banner comment above the export is stale — it says "register only" but the module now re-exports both getService and registerService; update the comment to accurately describe the public preview API surface (e.g., remove "register only" or change to "register and get" / "register and retrieve") so it matches the exported symbols getService and registerService in index.ts.code/core/src/preview-api/modules/preview-web/docs-context/DocsContext.ts (1)
289-296: ⚖️ Poor tradeoffConsider caching the unique CSF file set if called frequently.
The
new Set(this.exportsToCSFFile.values())deduplication on every call could be optimized by maintaining a cached set that's updated whenexportsToCSFFilechanges. However, since this is called during docs rendering (not a hot path), the current implementation is acceptable.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@code/core/src/preview-api/modules/preview-web/docs-context/DocsContext.ts` around lines 289 - 296, The getComponentId method currently builds a new Set from this.exportsToCSFFile.values() on every call which deduplicates CSF files repeatedly; to optimize, maintain a cached Set (or array) of unique CSF files updated whenever exportsToCSFFile is mutated and have getComponentId iterate that cached collection instead of recreating the Set each time—update cache mutation points where exportsToCSFFile is modified and ensure getComponentId uses the cached collection (referencing getComponentId and exportsToCSFFile).code/addons/docs/src/blocks/blocks/ArgTypes.tsx (1)
71-71: Story ID parsing (componentId--storyName) looks stable; avoid duplication. Lines 71 and 81 useid.split('--')[0]; this samecomponentIdderivation pattern is used across the repo (includingcode/core/src/common/utils/component-id.tsand other docs/controls/addons code). Consider reusing the existing component-id utility inArgTypes.tsxto keep parsing logic centralized.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@code/addons/docs/src/blocks/blocks/ArgTypes.tsx` at line 71, Replace the ad-hoc id.split('--')[0] componentId derivation in ArgTypes.tsx with the shared component-id parsing utility used across the repo: import the central utility and call it where componentId is computed inside the ArgTypes component (the spots currently using id.split('--')[0]). This centralizes parsing, removes duplication, and keeps behavior consistent with other docs/controls/addons code; update imports accordingly and remove the inline split usage.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@code/addons/docs/src/blocks/blocks/ArgTypes.mdx`:
- Around line 27-29: The OfStoryUnattached section currently points at
ExampleStories.NoParameters so it renders the same attached target as OfStory;
either change the ArgTypes component in the OfStoryUnattached block to reference
an actually unattached story (e.g., replace ExampleStories.NoParameters with the
correct unattached story identifier) or remove the OfStoryUnattached heading and
its <ArgTypes of={...} /> line to avoid misleading docs; locate the block by the
OfStoryUnattached heading and the ArgTypes usage to apply the fix.
In `@code/addons/docs/src/blocks/blocks/ArgTypes.tsx`:
- Around line 18-21: Update the relative imports in ArgTypes.tsx to include
explicit file extensions per repo guidelines: change the imports for
DocsContext, useOf, utils, and with-mdx-component-override to reference their
actual filenames (e.g., './DocsContext.ts' or .tsx as appropriate), ensuring
getComponentName and withMdxComponentOverride usages still resolve; verify each
target file's real extension (.ts vs .tsx) and update the import paths for
DocsContext, useOf, getComponentName (./utils.<ext>), and
withMdxComponentOverride to match.
In `@code/addons/docs/src/blocks/blocks/Controls.stories.tsx`:
- Around line 133-143: The input reset is currently inside the returned cleanup
function from beforeEach so it runs after the story instead of before play; move
the logic that finds the input (using canvasElement and the input variable),
clears it via userEvent.clear and types 'b' via userEvent.type directly into the
beforeEach body so it runs immediately, and only return a cleanup function (if
needed) that does not perform the reset; update the beforeEach async ({
canvasElement }) => { ... } handler accordingly and keep references to
canvas/queryByDisplayValue, input, userEvent.clear, and userEvent.type to locate
the code to change.
In
`@code/addons/docs/src/blocks/components/ArgsTable/TabbedArgsTable.stories.tsx`:
- Around line 98-107: The beforeEach currently returns an async cleanup function
so the input-reset logic never runs before play; move the body out of the
returned function and execute it directly in beforeEach: locate the beforeEach
block that uses the canvasElement and within to find the input (uses
within(canvasElement), canvas.queryByDisplayValue('hellox')/('hello')), and call
await userEvent.clear(input) and await userEvent.type(input, 'hello') inside the
beforeEach itself (not inside the returned function). Remove the returned async
cleanup wrapper so the input normalization runs before each story play.
In `@code/core/src/controls/components/ControlsPanel.tsx`:
- Around line 161-186: The panel currently gates all rows and isLoading on
docgenPayload; instead compute rows from useArgTypes() immediately and only
merge in docgenService data when docgenPayload exists. Concretely, build a
baseRows object from customArgTypes/initialArgs/storyData.parameters (so
prepared custom argTypes always render), then if docgenPayload is defined call
mergeServiceArgTypes({ payload: docgenPayload, storyId: storyData.id,
parameters: storyData.parameters, initialArgs, customArgTypes }) to augment
baseRows; finally pass isLoading based on whether the story is still preparing
(not on absence of docgenPayload) when rendering ControlsPanelTable. Ensure you
reference useArgTypes, useServiceQuery/docgenPayload, mergeServiceArgTypes, rows
and ControlsPanelTable while making these changes.
---
Nitpick comments:
In `@code/addons/docs/src/blocks/blocks/ArgTypes.tsx`:
- Line 71: Replace the ad-hoc id.split('--')[0] componentId derivation in
ArgTypes.tsx with the shared component-id parsing utility used across the repo:
import the central utility and call it where componentId is computed inside the
ArgTypes component (the spots currently using id.split('--')[0]). This
centralizes parsing, removes duplication, and keeps behavior consistent with
other docs/controls/addons code; update imports accordingly and remove the
inline split usage.
In `@code/core/src/preview-api/index.ts`:
- Around line 91-92: The banner comment above the export is stale — it says
"register only" but the module now re-exports both getService and
registerService; update the comment to accurately describe the public preview
API surface (e.g., remove "register only" or change to "register and get" /
"register and retrieve") so it matches the exported symbols getService and
registerService in index.ts.
In `@code/core/src/preview-api/modules/preview-web/docs-context/DocsContext.ts`:
- Around line 289-296: The getComponentId method currently builds a new Set from
this.exportsToCSFFile.values() on every call which deduplicates CSF files
repeatedly; to optimize, maintain a cached Set (or array) of unique CSF files
updated whenever exportsToCSFFile is mutated and have getComponentId iterate
that cached collection instead of recreating the Set each time—update cache
mutation points where exportsToCSFFile is modified and ensure getComponentId
uses the cached collection (referencing getComponentId and exportsToCSFFile).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 0bccbfc5-453d-486f-84bc-ab5b2b26f9cb
📒 Files selected for processing (28)
code/.storybook/main.tscode/addons/docs/src/blocks/blocks/ArgTypes.mdxcode/addons/docs/src/blocks/blocks/ArgTypes.stories.tsxcode/addons/docs/src/blocks/blocks/ArgTypes.tsxcode/addons/docs/src/blocks/blocks/Controls.mdxcode/addons/docs/src/blocks/blocks/Controls.stories.tsxcode/addons/docs/src/blocks/blocks/Controls.tsxcode/addons/docs/src/blocks/blocks/argTypesShared.tscode/addons/docs/src/blocks/blocks/useServiceDocgen.tscode/addons/docs/src/blocks/components/ArgsTable/TabbedArgsTable.stories.tsxcode/core/src/controls/components/ControlsPanel.stories.tsxcode/core/src/controls/components/ControlsPanel.tsxcode/core/src/controls/manager.tsxcode/core/src/docs-tools/argTypes/docgenServiceArgTypes.tscode/core/src/docs-tools/argTypes/index.tscode/core/src/manager-api/index.mock.tscode/core/src/manager-api/index.tscode/core/src/manager/globals/exports.tscode/core/src/preview-api/index.tscode/core/src/preview-api/modules/preview-web/docs-context/DocsContext.tscode/core/src/preview-api/modules/store/StoryStore.test.tscode/core/src/preview-api/modules/store/inferArgTypes.tscode/core/src/preview-api/modules/store/inferControls.tscode/core/src/shared/open-service/manager.tscode/core/src/shared/open-service/preview.tscode/core/src/types/modules/docs.tscode/frameworks/react-vite/src/preset.tscode/presets/react-webpack/src/framework-preset-react-docs.ts
Make service-query snapshots pure for React, remove the server snapshot argument, and ensure service ControlsPanel still renders prepared custom argTypes when no service docgen payload exists. Also tidy ArgTypes docs/imports from review. Co-authored-by: Cursor <cursoragent@cursor.com>
ObjectControl initialized raw mode when it first mounted without data and never switched back after args/docgen arrived. Track the no-data to data transition and return to rich mode unless the user explicitly requested raw editing via Set object. Co-authored-by: Cursor <cursoragent@cursor.com>
When an object control first mounted without data it initialized raw mode and stayed there after args/docgen arrived. Switch back to rich mode for the automatic no-data to data transition, and cover the delayed-data case. Co-authored-by: Cursor <cursoragent@cursor.com>
The service ControlsPanel could render docgen rows before STORY_PREPARED updated custom argTypes/parameters, causing a brief full-table flicker before include or exclude filters arrived. Keep the ArgsTable skeleton visible until both the story is prepared and the docgen query has settled. Co-authored-by: Cursor <cursoragent@cursor.com>
Hand-written formatting frequently diverges from oxfmt, so make running `yarn fmt:write` from `code/` a mandatory post-edit step. Co-authored-by: Cursor <cursoragent@cursor.com>
Component comments parsed via the docgen service were collapsed onto a single line because comment-parser defaults to `compact` spacing, so multi-paragraph Markdown rendered as one heading. Parse the block description with `preserve` spacing (matching react-docgen's legacy `__docgenInfo`) while keeping a separate `compact` parse for tag values, and let the Description block fall back to the service description. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
code/addons/docs/src/blocks/blocks/Description.tsx (1)
6-6: Add explicit.tsextensions to relative imports inDescription.tsx.
Description.tsximports./DocsContextand./useServiceDocgenwithout the.tsextension. Even though the codebase hasaddExtensionsToRelativeImportsfor Node ESM compatibility, keeping imports explicit matches the stated guideline and existing explicit imports elsewhere.Suggested change
-import { DocsContext } from './DocsContext'; +import { DocsContext } from './DocsContext.ts';-import { useServiceDocgen } from './useServiceDocgen'; +import { useServiceDocgen } from './useServiceDocgen.ts';🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@code/addons/docs/src/blocks/blocks/Description.tsx` at line 6, Update the two relative imports in Description.tsx to include explicit .ts extensions: change the import of DocsContext (import { DocsContext } from './DocsContext') to './DocsContext.ts' and the import of useServiceDocgen (import { useServiceDocgen } from './useServiceDocgen') to './useServiceDocgen.ts' so they match the project's explicit-extension guideline and Node ESM compatibility handled by addExtensionsToRelativeImports.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@code/addons/docs/src/blocks/blocks/Description.tsx`:
- Line 6: Update the two relative imports in Description.tsx to include explicit
.ts extensions: change the import of DocsContext (import { DocsContext } from
'./DocsContext') to './DocsContext.ts' and the import of useServiceDocgen
(import { useServiceDocgen } from './useServiceDocgen') to
'./useServiceDocgen.ts' so they match the project's explicit-extension guideline
and Node ESM compatibility handled by addExtensionsToRelativeImports.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 53eadc97-dc6b-4958-a6ed-d3ba61e95f8d
📒 Files selected for processing (12)
AGENTS.mdcode/addons/docs/src/blocks/blocks/ArgTypes.mdxcode/addons/docs/src/blocks/blocks/ArgTypes.tsxcode/addons/docs/src/blocks/blocks/Description.tsxcode/addons/docs/src/blocks/blocks/useServiceDocgen.tscode/addons/docs/src/blocks/controls/Object.stories.tsxcode/addons/docs/src/blocks/controls/Object.tsxcode/core/src/controls/components/ControlsPanel.stories.tsxcode/core/src/controls/components/ControlsPanel.tsxcode/core/src/shared/open-service/use-service-query.tscode/renderers/react/src/componentManifest/jsdocTags.test.tscode/renderers/react/src/componentManifest/jsdocTags.ts
💤 Files with no reviewable changes (1)
- code/addons/docs/src/blocks/blocks/ArgTypes.mdx
✅ Files skipped from review due to trivial changes (1)
- AGENTS.md
🚧 Files skipped from review as they are similar to previous changes (4)
- code/addons/docs/src/blocks/blocks/useServiceDocgen.ts
- code/core/src/controls/components/ControlsPanel.stories.tsx
- code/core/src/controls/components/ControlsPanel.tsx
- code/addons/docs/src/blocks/blocks/ArgTypes.tsx
|
|
||
| if (!hasSubcomponents) { | ||
| if (!(Object.keys(filteredArgTypes).length > 0 || Object.keys(args).length > 0)) { | ||
| function renderControlsTables({ |
There was a problem hiding this comment.
Any reason why this isn't a React component?
Closes #
What I did
Routes docs ArgTypes and Controls blocks (and the manager Controls panel) through the
core/docgenopen service whenfeatures.experimentalDocgenServeris enabled. Custom argTypes stay on the prepared-story path and are merged at read time viamergeServiceArgTypes.Completes the docgen swap started in #35108 (now merged to
next):__docgenInfoinjection is disabled in the react-vite and react-webpack presets so preview argTypes stay docgen-free; the UI reads server-extracted metadata from the service instead.Key changes:
Legacy*/DocgenService*splits in ArgTypes, Controls, and ControlsPanel (gated onglobalThis.FEATURES.experimentalDocgenServer).argTypesShared.ts; preview hookuseServiceDocgenbacked byuseSyncExternalStore.mergeServiceArgTypes/getServiceSubcomponentArgTypesin docs-tools; callableinferArgTypes/inferControlsfor reuse outside the enhancer pipeline.DocsContext.getComponentIdfor bareof={Component}service lookups.getServicere-exported from preview/manager open-service entrypoints.Internal Storybook:
experimentalDocgenServeris explicitly set tofalseincode/.storybook/main.tsso CI and local dev exercise the non-experimental path without regressions (the service does not yet work in production builds).Checklist for Contributors
Testing
The changes in this PR are covered in the following automated tests:
Manual testing
Caution
This section is mandatory for all contributions. If you believe no manual test is necessary, please state so explicitly. Thanks!
experimentalDocgenServer: false), runcd code && yarn storybook:uiand verify ArgTypes, Controls, and Description block stories render as onnext(no missing props, descriptions, or controls).features.experimentalDocgenServer: truelocally and confirm ArgTypes/Controls blocks and the Controls panel read from the docgen service (subcomponent tabs, custom argTypes merged with service extraction).ControlsPanel → Service Docgen Controls Loadstory.Documentation
MIGRATION.MD
Checklist for Maintainers
When this PR is ready for testing, make sure to add
ci:normal,ci:mergedorci:dailyGH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found incode/lib/cli-storybook/src/sandbox-templates.tsDeclare whether manual QA will be needed for this PR during the next release, through
qa:neededorqa:skipMake 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 pull request has been released as version
0.0.0-pr-35109-sha-176296ff. Try it out in a new sandbox by runningnpx storybook@0.0.0-pr-35109-sha-176296ff sandboxor in an existing project withnpx storybook@0.0.0-pr-35109-sha-176296ff upgrade.More information
0.0.0-pr-35109-sha-176296ffsplit/docgen-ui-consumption176296ff1781264209)To request a new release of this pull request, mention the
@storybookjs/coreteam.core team members can create a new canary release here or locally with
gh workflow run --repo storybookjs/storybook publish.yml --field pr=35109Made with Cursor
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Chore