Skip to content

FRD: Controls Quality Audit

FieldValue
IDFRD-041
OwnerDavid Holmes
StatusComplete ✅
PriorityP1 — Developer Experience
SizeM (Refactor)
Target Releasev2.0.0
Last Updated2026-05-26

Document Summary

Audit and fix every Storybook story’s controls (argTypes) to use the correct control type, include descriptions, and hide internal props. Two-option props must use boolean controls; short enums (2-5 values) must use inline-radio; numeric props must use range with sensible min/max/step; event handler props must have control: false; internal/private props must be hidden from the controls panel entirely. The audit script enforces these rules in CI.


Introduction

Overview

Storybook controls are the primary interactive mechanism for developers to explore component behavior. Poorly configured controls — wrong control types, missing descriptions, exposed internal props — make the exploration experience frustrating and confusing. This FRD standardizes control quality across all story files.

Goals

  • Every prop with exactly two values (true/false, on/off) uses a boolean control.
  • Every prop with 2-5 enum values uses an inline-radio control.
  • Every numeric prop uses a range control with appropriate min, max, and step values.
  • Every event handler prop (on*) has control: false so it does not appear as a text input.
  • Every internal prop (prefixed with _ or documented as internal) is hidden via table.disable: true.
  • Every visible control has a description string in its argType.
  • pnpm run dx:audit enforces all rules with zero tolerance.

Non-Goals

  • Adding new props to components.
  • Changing component APIs or behavior.
  • Custom Storybook addons for controls rendering.
  • Documenting controls behavior in MDX pages (covered by FRD-040).

Scope

In Scope

ItemDescription
ArgTypes auditReview every story file’s argTypes configuration against the control type rules
Control type fixesUpdate argTypes to use correct control types per the rules
Description authoringAdd description strings to all visible argTypes entries
Internal prop hidingSet table.disable: true on props that are internal implementation details
Event handler controlsSet control: false on all on* callback props
Audit script rulesAdd control quality detection to scripts/checks/audit-story-dx.mjs

Out of Scope

ItemReason
Custom control renderersStandard Storybook control types are sufficient
Component prop refactoringControl configuration is independent of prop design
Visual theming of controls panelOut of scope for this DX effort

Users and Pain Points

UserPain Point
Application developerBoolean prop shows as a text input instead of a toggle — confusing and error-prone
Application developerEnum prop shows as a freeform text field — no discoverability of valid values
Application developerInternal props clutter the controls panel — unclear which props are part of the public API
Application developerEvent handler props show as text inputs — misleading since typing does nothing useful
Application developerNo description on controls — must read source to understand what a prop does

Definitions

TermDefinition
ArgTypesStorybook metadata that configures how each prop appears in the controls panel
Inline-radioA control type that renders radio buttons inline, ideal for short enum lists
Range controlA slider control for numeric values with configurable min, max, and step
Internal propA prop intended for use by the design system internally, not by consumer applications

Current State

  • Many stories rely on Storybook’s auto-inferred control types, which often choose text for enums and object for complex types.
  • Event handler props appear as editable text fields in the controls panel.
  • Internal props (e.g., _internalVariant, ref) are visible to developers.
  • Few stories have description strings on their argTypes.
  • The audit script does not currently check control type correctness.

Proposed Solution

Phase 1: Extend the audit script

Add rules to scripts/checks/audit-story-dx.mjs:

  1. Detect boolean props without control: { type: "boolean" }.
  2. Detect short enum props without control: { type: "inline-radio" }.
  3. Detect numeric props without control: { type: "range" }.
  4. Detect on* props without control: false.
  5. Detect props prefixed with _ that are not hidden.
  6. Detect visible props without a description.

Phase 2: Fix violations

For each story file, update argTypes to comply with the rules. This requires:

  • Reading the component’s TypeScript interface to identify prop types.
  • Mapping each prop to the correct control type.
  • Writing a brief description for each public prop.
  • Hiding internal props.

Phase 3: CI enforcement

Ensure pnpm run dx:audit blocks merge on control quality violations.


Requirements

IDRequirementPriority
REQ-01Two-value props use boolean controlMust
REQ-02Short enum props (2-5 values) use inline-radio controlMust
REQ-03Numeric props use range control with min/max/stepMust
REQ-04Event handler props have control: falseMust
REQ-05Internal props are hidden via table.disable: trueMust
REQ-06All visible controls have a descriptionMust
REQ-07Long enum props (6+ values) use select controlShould
REQ-08Range controls have sensible defaults (e.g., step=1 for integers, step=0.1 for decimals)Should

Functional Requirements

IDDescriptionAcceptance
FR-01Audit script detects wrong control typesLists file, prop name, current type, expected type
FR-02Audit script detects missing descriptionsLists file and prop name
FR-03Audit script detects exposed internal propsLists file and prop name
FR-04Audit script detects event handlers without control: falseLists file and prop name
FR-05Controls panel for each component shows only public props with correct typesVisual verification on 10 sample components

Non-Functional Requirements

IDDescriptionTarget
NFR-01Audit script execution timeUnder 10 seconds (combined with other DX audit rules)
NFR-02No runtime performance impactArgTypes are static metadata; no effect on component rendering

API/Interface Requirements

InterfaceRequirement
argTypes configurationMust follow the control type mapping rules defined in this FRD
scripts/checks/audit-story-dx.mjsNew rules: wrong-control-type, missing-description, exposed-internal-prop, event-handler-visible

Accessibility Requirements

IDRequirement
A11Y-01No direct accessibility impact — this FRD targets Storybook authoring, not rendered UI
A11Y-02Control descriptions should mention accessibility implications where relevant (e.g., “Visually hides the label but keeps it available to screen readers”)

Content and Documentation Requirements

IDRequirement
DOC-01Add a “Controls Style Guide” section to the Storybook contributing guide
DOC-02Document the control type mapping rules (boolean, inline-radio, range, select) in the guide
DOC-03Provide examples of well-configured argTypes for common prop patterns

Dependencies

DependencyTypeRisk
Component TypeScript interfacesInternalProp types must be readable by the audit script or documented in argTypes
scripts/checks/audit-story-dx.mjsInternalMust be extended before bulk fixes begin
Storybook controls addonExternalControl type names must match Storybook’s expected values

Risks and Tradeoffs

RiskImpactMitigation
Automated detection of prop types may be impreciseFalse positives in auditAllow // dx:ignore comments for edge cases; manual review of flagged items
Large number of stories to updateSlow deliveryBatch by component directory; M-size scope is manageable in 2-3 sprints
Range control min/max guessingIncorrect slider boundsDefault to 0-100/step 1; component authors can override

Open Questions

#QuestionStatus
1Should the audit script parse TypeScript interfaces to auto-detect prop types, or rely on argTypes declarations?Open
2Should ref props be hidden by default?Open
3Should className and style props have controls or be hidden?Open

Acceptance Criteria

  • pnpm run dx:audit reports zero control quality violations.
  • Every event handler prop (on*) has control: false or table: { disable: true } in argTypes.
  • Every internal prop prefixed with _ is hidden from the controls panel.
  • Audit rules (Rule 9, Rule 10) are integrated into scripts/checks/audit-story-dx.mjs and enforced in CI via dx:audit.
  • CI pipeline (validate, ci:parallel, check:prepush) blocks merge on any new control quality violation.

LLM Handoff Instructions

When an LLM agent picks up this FRD:

  1. Run pnpm run dx:audit to get the current violation report.
  2. For each violating story file, read the corresponding component’s TypeScript interface (props type) to understand each prop’s type and purpose.
  3. Apply the control type mapping:
    • boolean type or exactly 2 string literals → control: { type: "boolean" }
    • 2-5 string literal union → control: { type: "inline-radio" }, options: [...]
    • number type → control: { type: "range", min: 0, max: 100, step: 1 }
    • on* callback → control: false
    • _-prefixed or internal → table: { disable: true }
  4. Write a description for each visible prop: one sentence explaining what it does.
  5. After fixing a batch, run pnpm run dx:audit and pnpm typecheck.
  6. Do not modify component implementations — only story files and the audit script.

Decision Log

DateDecisionRationale
2026-05-26Use inline-radio for short enums rather than radioInline layout is more compact and scannable for 2-5 options
2026-05-26Require descriptions on all visible controlsDescriptions are the primary documentation surface in the controls panel

Document History

DateVersionAuthorChanges
2026-05-260.1David HolmesInitial draft