Skip to content

Controls: Fix select controls for optional unions in Flow and nested options#34495

Open
MastadoonPrime wants to merge 2 commits intostorybookjs:nextfrom
MastadoonPrime:fix/select-controls-optional-union
Open

Controls: Fix select controls for optional unions in Flow and nested options#34495
MastadoonPrime wants to merge 2 commits intostorybookjs:nextfrom
MastadoonPrime:fix/select-controls-optional-union

Conversation

@MastadoonPrime
Copy link
Copy Markdown

@MastadoonPrime MastadoonPrime commented Apr 8, 2026

What

Closes #12641

This PR addresses two remaining issues with Storybook Controls of type "select" that were not fully resolved by #33200.

Why

1. Flow converter missing void filtering

PR #33200 fixed the TypeScript converter to filter out undefined from union types before checking if all elements are literals (for enum/select detection). However, the Flow converter has the same bug — optional props in Flow add void to the union type, causing the enum detection to fail. This results in optional union props displaying as "object" controls instead of "select" dropdowns.

2. Options nested inside control object

Users commonly write control: { type: "select", options: [...] } instead of the documented control: { type: "select" }, options: [...]. The Storybook documentation shows options at the argType level, but many users (and even some examples) nest it inside control. Previously this silently failed — the control would render as a select but with no options. Now normalizeInputType extracts options from the control object to the top level when no top-level options are already specified.

How

Flow converter (convert/flow/convert.ts)

normalizeInputTypes (normalizeInputTypes.ts)

  • When processing a control object, check if it contains an options property
  • If options exists inside control and no top-level options is set, extract it to the top level
  • If top-level options already exists, leave control.options as-is (top-level takes precedence)

Tests (normalizeInputTypes.test.ts)

  • Added test: extracts options from inside control object to top level
  • Added test: does not override existing top-level options with control.options

How to test

Flow optional union fix

  1. Create a Flow component with optional union type props
  2. Verify the Controls panel shows select/radio controls instead of object

Nested options fix

  1. Create a story with argTypes: { size: { control: { type: "select", options: ["sm", "md", "lg"] } } }
  2. Verify the select control shows the options correctly
  3. Also verify that argTypes: { size: { control: { type: "select" }, options: ["sm", "md", "lg"] } } still works

Checklist

  • Fix tested locally
  • Minimal, focused changes
  • Backward compatible
  • Tests added

Summary by CodeRabbit

  • Bug Fixes

    • Fixed handling of void types during Flow type-to-enum conversion to avoid incorrect enum generation.
    • Improved input-type normalization so control-provided options are promoted to top-level options only when appropriate, preserving existing top-level options.
  • Tests

    • Added tests covering options extraction behavior in input type normalization.

…options

Closes storybookjs#12641

This commit addresses two remaining issues with Storybook Controls of type "select":

1. Flow converter: Optional props in Flow add `void` to the union type, causing
   the enum detection to fail (same bug that was fixed for TypeScript in storybookjs#33200).
   The fix filters out `void` elements before checking if all remaining elements
   are literals, allowing optional Flow unions to correctly display as select controls.

2. Options nested inside control: Users commonly write
   `control: { type: "select", options: [...] }` instead of the correct
   `control: { type: "select" }, options: [...]`. The normalizeInputType function
   now extracts options from the control object to the top level when no top-level
   options are already specified, making both syntaxes work.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 8, 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: e5569b0a-0c93-4156-9333-bae31205589d

📥 Commits

Reviewing files that changed from the base of the PR and between 7de069d and b6b29a0.

📒 Files selected for processing (1)
  • code/core/src/preview-api/modules/store/csf/normalizeInputTypes.test.ts
✅ Files skipped from review due to trivial changes (1)
  • code/core/src/preview-api/modules/store/csf/normalizeInputTypes.test.ts

📝 Walkthrough

Walkthrough

Flow union-to-enum conversion now ignores void members when deciding enum eligibility; input type normalization can promote control.options to top-level options when no top-level options exist.

Changes

Cohort / File(s) Summary
Flow Type Conversion
code/core/src/docs-tools/argTypes/convert/flow/convert.ts
Added isVoid guard and changed union handling to filter out void elements before determining/converting a union to an enum; maps only non-void literal elements to enum options.
Input Type Normalization
code/core/src/preview-api/modules/store/csf/normalizeInputTypes.ts, code/core/src/preview-api/modules/store/csf/normalizeInputTypes.test.ts
normalizeInputType now elevates control.options to top-level options when control is an object and no top-level options exist; tests added to verify extraction and preservation behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs


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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
code/core/src/preview-api/modules/store/csf/normalizeInputTypes.test.ts (1)

127-158: Good test coverage for the new options extraction feature.

The tests comprehensively cover both scenarios: extracting nested options when no top-level options exist, and preserving top-level options when both are present.

Minor style note: This new test block uses double quotes (") while the rest of the file uses single quotes ('). Consider aligning with the existing file style for consistency.

,

🧹 Optional: Align quote style with rest of file
-describe("normalizeInputType - options extraction", () => {
-  it("extracts options from inside control object to top level", () => {
+describe('normalizeInputType - options extraction', () => {
+  it('extracts options from inside control object to top level', () => {
     expect(
       normalizeInputType(
         {
-          control: { type: "select", options: ["a", "b", "c"] },
+          control: { type: 'select', options: ['a', 'b', 'c'] },
         },
-        "arg"
+        'arg'
       )
     ).toEqual({
-      name: "arg",
-      options: ["a", "b", "c"],
-      control: { type: "select", disable: false },
+      name: 'arg',
+      options: ['a', 'b', 'c'],
+      control: { type: 'select', disable: false },
     });
   });

-  it("does not override existing top-level options with control.options", () => {
+  it('does not override existing top-level options with control.options', () => {
     expect(
       normalizeInputType(
         {
-          options: ["x", "y"],
-          control: { type: "select", options: ["a", "b", "c"] },
+          options: ['x', 'y'],
+          control: { type: 'select', options: ['a', 'b', 'c'] },
         },
-        "arg"
+        'arg'
       )
     ).toEqual({
-      name: "arg",
-      options: ["x", "y"],
-      control: { type: "select", options: ["a", "b", "c"], disable: false },
+      name: 'arg',
+      options: ['x', 'y'],
+      control: { type: 'select', options: ['a', 'b', 'c'], disable: false },
     });
   });
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/src/preview-api/modules/store/csf/normalizeInputTypes.test.ts`
around lines 127 - 158, The test block in normalizeInputTypes.test.ts uses
double quotes instead of the file's single-quote style; update all string
literals in this new describe/it block (e.g., the describe title, it titles, the
call to normalizeInputType and expected object keys like "arg", "select", and
array elements) to use single quotes so the test file's quoting is consistent
with the rest of the file.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@code/core/src/preview-api/modules/store/csf/normalizeInputTypes.test.ts`:
- Around line 127-158: The test block in normalizeInputTypes.test.ts uses double
quotes instead of the file's single-quote style; update all string literals in
this new describe/it block (e.g., the describe title, it titles, the call to
normalizeInputType and expected object keys like "arg", "select", and array
elements) to use single quotes so the test file's quoting is consistent with the
rest of the file.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1bcc81a2-6bab-44ab-9ea4-b44ca1375f82

📥 Commits

Reviewing files that changed from the base of the PR and between 370524f and 7de069d.

📒 Files selected for processing (3)
  • code/core/src/docs-tools/argTypes/convert/flow/convert.ts
  • code/core/src/preview-api/modules/store/csf/normalizeInputTypes.test.ts
  • code/core/src/preview-api/modules/store/csf/normalizeInputTypes.ts

Address CodeRabbit nit: align quote style in normalizeInputType
options extraction tests with the rest of the file.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Problem with storybook-controls of type "select"

1 participant