Skip to content

Vue3: Support TypeScript enum props in vue-component-meta docgen - #35684

Merged
valentinpalkovic merged 3 commits into
nextfrom
valentin/vue-component-meta-ts-enums
Jul 31, 2026
Merged

valentinpalkovic merged 3 commits into
nextfrom
valentin/vue-component-meta-ts-enums

Conversation

@valentinpalkovic

Copy link
Copy Markdown
Contributor

What I did

Closes the TS-enum gap that #35565 had to leave open.

vue-component-meta stringifies a TypeScript enum member to its qualified name (Severity.Info), which says nothing about the value the component actually receives. That is why #35565, when it enabled schema: true, had to explicitly keep TS enums out of Controls (86a1ea5): an enum sbType would have rendered a dropdown that injects the literal string "Severity.Info" instead of 'info'.

vuejs/language-tools#6131 - merged and released in vue-component-meta@3.3.9 - adds a literal schema node that carries the runtime value alongside the member name:

{ kind: 'enum', type: 'Severity', schema: [
  { kind: 'literal', type: 'Severity.Info',    value: '"info"' },
  { kind: 'literal', type: 'Severity.Warning', value: '"warning"' },
] }

This PR bumps to ^3.3.9 and consumes it.

Before / after

// enum Severity { Info = 'info', Warning = 'warning', Error = 'error' }
// props: { severity: Severity }

// before (#35565): documented, but not selectable
{ type: { name: 'other', value: 'Severity' } }

// after
{
  type:    { name: 'enum', value: ['info', 'warning', 'error'], required: true },
  options: ['info', 'warning', 'error'],
  control: { labels: { info: 'Severity.Info', warning: 'Severity.Warning', error: 'Severity.Error' } },
  table:   { type: { summary: 'Severity' } },
}

The dropdown shows Severity.Info and passes 'info'. Numeric enums (enum Level { Low, High }) pass 0 and 1 under the labels Level.Low / Level.High. table.type.summary keeps the enum name in both cases, so the docs table is unchanged.

Notes on the implementation

  • control.type is deliberately not set. inferControls already picks radio vs select by option count, and combineParameters merges it with the labels set here. TS enums therefore behave exactly like plain literal unions instead of being force-fed a select.
  • Mixed unions work: Severity | 'custom' resolves to ['info', 'warning', 'error', 'custom'], with labels only on the enum members.
  • The dotted-string guard stays, narrowed to what it actually catches now. I verified against 3.3.9 that unresolved qualified type names can still produce all-dotted schemas (typeof Config.alpha | typeof Config.beta) - those carry no value and must not become selectable. TS enum members no longer reach that branch.
  • removeNestedSchemas in the vite plugin gained an explicit terminal case for literal nodes (they have nothing nested, and the previous delete schema.schema no longer type-checks against the widened PropertyMetaSchema).
  • The MyEnum fixture in meta-components.ts was a hand-maintained capture of checker output still in the pre-3.3.9 shape; it is updated so the mock matches what the dependency now produces.

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

Unit tests in code/renderers/vue3/src/extractArgTypes.test.ts cover string enums, numeric enums, mixed enum/literal unions, the qualified-name guard, and the options + control.labels output. The docgen harness (#35574) re-records props-ts-enum and shows the change as a reviewed baseline diff - it is the only fixture that moves, and the harness comparator certifies it as an improvement rather than a regression.

Manual testing

  1. yarn task sandbox --template vue3-vite/default-ts --start-from auto
  2. Set docgen: 'vue-component-meta' in the framework options
  3. Add a component with a TS enum prop and open its Controls panel: the dropdown lists the member names and sets the member values.

Documentation

  • Add or update documentation reflecting your changes
  • If you are deprecating/removing a feature, make sure to update
    MIGRATION.MD

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: bug

🤖 Generated with Claude Code

https://claude.ai/code/session_015gvCXfY6v5hGRDVNL11JXS

vue-component-meta stringifies a TS enum member to its qualified name
("Severity.Info"), which says nothing about what gets passed to the
component. #35565 therefore had to keep enums out of Controls entirely:
an enum sbType would have made a select inject the literal string
"Severity.Info" instead of 'info'.

vuejs/language-tools#6131, released in vue-component-meta 3.3.9, adds a
"literal" schema node carrying the runtime value next to the member name.
Consume it: TS enums now resolve to an enum sbType of their runtime values,
with control.labels mapping each value back to the member name it is
written as. Picking "Severity.Info" in the dropdown passes 'info'; numeric
enums pass 0 and 1. table.type.summary keeps the enum name either way.

The dotted-string guard stays, narrowed to what it actually catches now:
unresolved qualified type names like "typeof Config.alpha", which
stringify with a dot but stand for no value.
@valentinpalkovic valentinpalkovic added bug ci:normal Run our default set of CI jobs (choose this for most PRs). qa:needed Pull Requests that will need manual QA prior to release. labels Jul 31, 2026
The labels are only useful if they survive next to the control type
inferControls picks. Assert the merged result directly, so the radio/select
treatment a TS enum shares with a literal union cannot silently break.
@coderabbitai

coderabbitai Bot commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 3 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6308fd53-283a-4a78-9aa0-6629f17c9aa0

📥 Commits

Reviewing files that changed from the base of the PR and between c7e79fd and ae4eb16.

⛔ Files ignored due to path filters (1)
  • code/renderers/vue3/src/__snapshots__/extractArgTypes.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (4)
  • code/renderers/vue3/src/extractArgTypes.test.ts
  • code/renderers/vue3/template/stories_vue3-vite-default-ts/component-meta/TsEnumProps.stories.ts
  • code/renderers/vue3/template/stories_vue3-vite-default-ts/component-meta/ts-enum-props/component.vue
  • code/renderers/vue3/template/stories_vue3-vite-default-ts/component-meta/ts-enum-props/severity.ts

Walkthrough

The Vue 3 docgen integration now uses vue-component-meta 3.3.9, preserves literal schemas, and extracts TypeScript enum values and labels into argType controls. Tests and fixtures cover numeric, string, mixed, and unresolved enum types.

Changes

Vue enum extraction

Layer / File(s) Summary
Preserve literal schemas and update vue-component-meta
code/frameworks/vue3-vite/package.json, code/frameworks/vue3-vite/src/plugins/vue-component-meta.ts, code/lib/docgen-harness/package.json, code/lib/docgen-harness/src/vue3/vue3-component-meta-baselines.test.ts, code/lib/docgen-harness/README.md
The integration uses vue-component-meta 3.3.9. removeNestedSchemas preserves literal schema data. Documentation and baseline coverage use the updated behavior.
Represent enum members as runtime-valued schemas
code/lib/docgen-harness/src/vue3/__testfixtures__/props-ts-enum/*, code/renderers/vue3/src/docs/tests-meta-components/meta-components.ts
Enum fixtures and metadata mocks represent numeric and string members as literal schemas with runtime values and structured enum metadata.
Extract enum labels and control options
code/renderers/vue3/src/extractArgTypes.ts, code/renderers/vue3/src/extractArgTypes.test.ts
Prop extraction recognizes enum members and mixed literal unions, preserves qualified labels, and excludes unresolved qualified references from selectable options. Tests cover extraction and control inference.

Sequence Diagram(s)

sequenceDiagram
  participant VueComponentMeta
  participant ExtractArgTypes
  participant InferControls
  VueComponentMeta->>ExtractArgTypes: provide literal enum schemas
  ExtractArgTypes->>ExtractArgTypes: map enum labels to runtime values
  ExtractArgTypes->>InferControls: provide enum options and labels
  InferControls-->>ExtractArgTypes: return inferred controls
Loading

Possibly related PRs

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

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

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
code/renderers/vue3/src/extractArgTypes.ts (1)

341-346: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove non-maintenance references from comments.

Keep the local behavior rationale only.

  • code/renderers/vue3/src/extractArgTypes.ts#L341-L346: remove the external pull-request reference.
  • code/lib/docgen-harness/src/vue3/__testfixtures__/props-ts-enum/TsEnumProps.vue#L2-L4: remove the cm-argtypes.snapshot cross-file reference.

As per coding guidelines: “Comments should explain maintenance rationale, not investigation history; do not include internal ticket or acceptance codes, provenance claims, or cross-file line references.”

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.

Inline comments:
In `@code/renderers/vue3/src/extractArgTypes.ts`:
- Line 347: Remove the duplicate LiteralSchema type declaration in the module,
retaining a single Extract<PropertyMetaSchema, { kind: 'literal' }> alias for
all existing usages.
🪄 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: 7dce3da8-f2ac-444a-91ba-ea92110ab8b8

📥 Commits

Reviewing files that changed from the base of the PR and between 53d9e1f and c7e79fd.

⛔ Files ignored due to path filters (2)
  • code/renderers/vue3/src/__snapshots__/extractArgTypes.test.ts.snap is excluded by !**/*.snap
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (10)
  • code/frameworks/vue3-vite/package.json
  • code/frameworks/vue3-vite/src/plugins/vue-component-meta.ts
  • code/lib/docgen-harness/README.md
  • code/lib/docgen-harness/package.json
  • code/lib/docgen-harness/src/vue3/__testfixtures__/props-ts-enum/TsEnumProps.vue
  • code/lib/docgen-harness/src/vue3/__testfixtures__/props-ts-enum/cm-argtypes.snapshot
  • code/lib/docgen-harness/src/vue3/vue3-component-meta-baselines.test.ts
  • code/renderers/vue3/src/docs/tests-meta-components/meta-components.ts
  • code/renderers/vue3/src/extractArgTypes.test.ts
  • code/renderers/vue3/src/extractArgTypes.ts

Comment thread code/renderers/vue3/src/extractArgTypes.ts
… engine

The story renders under both docgen engines and shows the difference
directly: vue-component-meta gives severity/level a labelled option set,
vue-docgen-api falls back to a JSON editor. It also carries the numeric
enum and a plain literal union, so a regression in either shows up next to
its unaffected neighbour.

Both describe blocks in extractArgTypes.test.ts were named
"(vue-docgen-api)" while the first one drives vue-component-meta fixtures.
Sharing a name put their snapshots in one namespace, and since both spell
"should extract props for component", the two engines' props snapshots were
told apart only by a trailing 1/2 - so reordering or renaming a test would
silently swap which engine each snapshot belonged to.
Base automatically changed from valentin/vue3-component-meta-baselines to next July 31, 2026 08:56
@valentinpalkovic
valentinpalkovic requested a review from a team July 31, 2026 08:56
@storybook-app-bot

Copy link
Copy Markdown
Contributor

Package Benchmarks

Commit: ae4eb16, ran on 31 July 2026 at 09:38:53 UTC

The following packages have significant changes to their size or dependencies:

storybook

Before After Difference
Dependency count 73 73 0
Self size 21.39 MB 21.39 MB 🚨 +426 B 🚨
Dependency size 36.75 MB 31.22 MB 🎉 -5.53 MB 🎉
Bundle Size Analyzer Link Link

@storybook/angular-vite

Before After Difference
Dependency count 36 36 0
Self size 33.41 MB 33.41 MB 🎉 -42 B 🎉
Dependency size 21.34 MB 15.81 MB 🎉 -5.53 MB 🎉
Bundle Size Analyzer Link Link

@storybook/cli

Before After Difference
Dependency count 205 205 0
Self size 827 KB 827 KB 0 B
Dependency size 91.60 MB 86.07 MB 🎉 -5.53 MB 🎉
Bundle Size Analyzer Link Link

@storybook/codemod

Before After Difference
Dependency count 198 198 0
Self size 32 KB 32 KB 🎉 -36 B 🎉
Dependency size 90.08 MB 84.55 MB 🎉 -5.53 MB 🎉
Bundle Size Analyzer Link Link

create-storybook

Before After Difference
Dependency count 74 74 0
Self size 1.09 MB 1.09 MB 🚨 +66 B 🚨
Dependency size 58.14 MB 52.60 MB 🎉 -5.53 MB 🎉
Bundle Size Analyzer node node

@valentinpalkovic
valentinpalkovic merged commit 2c2f492 into next Jul 31, 2026
123 of 128 checks passed
@valentinpalkovic
valentinpalkovic deleted the valentin/vue-component-meta-ts-enums branch July 31, 2026 10:20
@github-actions github-actions Bot mentioned this pull request Jul 31, 2026
2 tasks done
@JReinhold JReinhold added qa:skip Pull Requests that do not need any QA. (e.g. documentation) and removed qa:needed Pull Requests that will need manual QA prior to release. labels Aug 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor
Fails
🚫

node failed.

Log

Details
Error:  Error: Could not find the Dangerfile at scripts/dangerfile.ts - if it is local, perhaps you have a typo? If it's using a remote file, it doesn't have a repo reference.
    at /usr/src/danger/dist/platforms/GitHub.js:161:27
    at step (/usr/src/danger/dist/platforms/GitHub.js:44:23)
    at Object.next (/usr/src/danger/dist/platforms/GitHub.js:25:53)
    at /usr/src/danger/dist/platforms/GitHub.js:19:71
    at new Promise (<anonymous>)
    at __awaiter (/usr/src/danger/dist/platforms/GitHub.js:15:12)
    at Object.executeRuntimeEnvironment (/usr/src/danger/dist/platforms/GitHub.js:144:88)
    at /usr/src/danger/dist/commands/danger-runner.js:101:47
    at step (/usr/src/danger/dist/commands/danger-runner.js:34:23)
    at Object.next (/usr/src/danger/dist/commands/danger-runner.js:15:53)
danger-results://tmp/danger-results-e7a7edc5.json

Generated by 🚫 dangerJS against ae4eb16

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. (e.g. documentation)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants