Skip to content

Core: Infer select/radio controls for TypeScript literal union types#34892

Closed
wbobbynmworley wants to merge 1 commit into
storybookjs:nextfrom
wbobbynmworley:fix/union-type-control-inference
Closed

Core: Infer select/radio controls for TypeScript literal union types#34892
wbobbynmworley wants to merge 1 commit into
storybookjs:nextfrom
wbobbynmworley:fix/union-type-control-inference

Conversation

@wbobbynmworley
Copy link
Copy Markdown

@wbobbynmworley wbobbynmworley commented May 24, 2026

Problem

Fixes #33779

When a component prop is typed as a union of string literals (e.g. size: 'S' | 'M' | 'L'), Storybook 10 no longer automatically generates select/radio controls for those props.

Root Cause

inferControls() only handled type.name === 'enum' for generating select/radio controls. However, TypeScript literal union types appear in docgen output as:

{ name: 'union', value: [{ name: 'literal', value: 'S' }, { name: 'literal', value: 'M' }, ...] }

Since 'union' wasn't handled, it fell through to the default case returning an 'object' control.

Fix

Added a case 'union' in inferControl() that:

  1. Checks if ALL union members are string literals (name === 'literal' && typeof value === 'string')
  2. If yes, generates radio (<=5 options) or select (>5 options) with the extracted string values
  3. If no, falls through to default (object control) — e.g. string | number unions

Manual testing

  1. Create a React component with a prop typed as a literal union:
type Size = 'S' | 'M' | 'L';
export const Button = ({ size }: { size: Size }) => ...
  1. Write a Storybook story for it
  2. Open the Controls panel — the size prop should show as radio buttons with 'S', 'M', 'L' options
  3. For 6+ options, it should show as select dropdown

Test Plan

Added 4 new tests in inferControls.test.ts:

  • Union of 3 string literals → radio control with options
  • Union of 6+ string literals → select control with options
  • Union with non-literal member → object control (no crash)
  • Union with number literals → object control (no crash)

All 6 tests pass.

When a component prop is typed as a union of string literals
(e.g. size: 'S' | 'M' | 'L'), Storybook's control inference
now correctly generates a radio (<=5 options) or select (>5) control.

Previously, only the 'enum' type name was handled, but TypeScript
literal unions appear as 'union' with 'literal' children in docgen output.

Fixes storybookjs#33779
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 24, 2026

Fails
🚫

PR is not labeled with one of: ["cleanup","BREAKING CHANGE","feature request","bug","documentation","maintenance","build","dependencies"]

🚫

PR is not labeled with one of: ["ci:normal","ci:merged","ci:daily","ci:docs"]

Generated by 🚫 dangerJS against 2931460

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 24, 2026

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: 212e7074-0173-4b73-9cbf-72649d0a3df8

📥 Commits

Reviewing files that changed from the base of the PR and between 1411339 and 2931460.

📒 Files selected for processing (2)
  • code/core/src/preview-api/modules/store/inferControls.test.ts
  • code/core/src/preview-api/modules/store/inferControls.ts

📝 Walkthrough

Walkthrough

This PR extends Storybook's argument control inference to handle union types containing string literals, inferring radio controls for small unions (≤5 options) and select for larger ones, with corresponding test coverage validating the new behavior and existing functionality.

Changes

Union Type Control Inference

Layer / File(s) Summary
Type imports for union handling
code/core/src/preview-api/modules/store/inferControls.ts
Updated storybook/internal/types imports to include SBUnionType and SBLiteralType for union and literal type detection.
Union type inference core logic
code/core/src/preview-api/modules/store/inferControls.ts
Added union case in inferControl that detects string-literal unions, derives option values, and infers radio (≤5 options) or select (>5); non-string-literal unions fall back to default inference.
Integration with control enhancer
code/core/src/preview-api/modules/store/inferControls.ts
inferControls function maintains filtering and control-mapping flow while wiring the updated inferControl logic through the enhancer.
Test coverage for union inference and existing tests
code/core/src/preview-api/modules/store/inferControls.test.ts
New test group validating radio/select inference for string-literal unions and fallback to object for unsupported cases, with preserved tests for custom matchers and include/exclude filtering.

🎯 2 (Simple) | ⏱️ ~12 minutes


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@wbobbynmworley wbobbynmworley changed the title fix: Infer select/radio controls for TypeScript literal union types Core: Infer select/radio controls for TypeScript literal union types May 24, 2026
@wbobbynmworley
Copy link
Copy Markdown
Author

Hi maintainers, the Danger JS check is failing because this PR is missing required labels. Could someone please add: bug (or appropriate type label) and ci:normal? Thanks!

@valentinpalkovic
Copy link
Copy Markdown
Contributor

Superseded by #34887

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

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[Bug]: Storybook 10 no longer infers controls from Typescript literal union types

2 participants