Skip to content

feat: add Storybook stories for pure components#7100

Open
talissoncosta wants to merge 13 commits intomainfrom
feat/storybook-component-stories
Open

feat: add Storybook stories for pure components#7100
talissoncosta wants to merge 13 commits intomainfrom
feat/storybook-component-stories

Conversation

@talissoncosta
Copy link
Copy Markdown
Contributor

@talissoncosta talissoncosta commented Apr 2, 2026

  • I have read the Contributing Guide.
  • I have added information to docs/ if required so people know about the feature.
  • I have filled in the "Changes" section below.
  • I have filled in the "How did you test this code" section below.

Changes

Closes #7179
Contributes to #6882

Add Storybook stories for the design system. 45 components organised across 8 categories. Two small design-system consolidations bundled in (Banner / ModalAlert removed, ColorSwatch + BooleanDotIndicator unified).

Coverage

  • 94% (16/17) of public reusable base primitives under web/components/base/ have stories
  • Not covered: Popover (blocked by FocusMonitor + jQuery); 5 react-select / routing internals not meant for direct consumer use
  • 45 stories total across 8 sidebar groups

Categories

Category Components
(top-level) Button, ButtonDropdown, Icons
Data Display BarChart, BooleanDotIndicator, ColorSwatch, DropdownMenu, IdentifierString, LabelWithTooltip, Logo, SettingTitle, StatItem, ToggleChip, Tooltip, VCSProviderTag
Forms Checkbox, CheckboxGroup, ChipInput, FormGroup, GhostInput, MultiSelect, Radio, SearchableSelect, Switch
Feedback EmptyState, ErrorMessage, InfoMessage, Loader, PasswordRequirements, Skeleton, WarningMessage
Patterns AccordionCard, Card, PageTitle, Panel, SettingRow
Navigation Breadcrumb, OverflowNav, SidebarLink, Tabs
Layout Column, Flex, Row
Modals InlineModal, Modal

Story conventions

  • Args + component: for primitives (autodocs props table, working Controls panel)
  • render: for stories needing state, composition, or layout demos
  • play functions on Modal, Tooltip, ButtonDropdown, DropdownMenu, OverflowNav so Chromatic captures the open / hovered / expanded state
  • Component descriptions on heavyweight stories
  • Shared withRouter decorator at documentation/components/_decorators.tsx

Component changes (non-story)

Mostly Storybook plumbing. Two design-system consolidations.

Change Why Risk
BooleanDotIndicator rewritten as <ColorSwatch shape='circle' size='lg' /> Same primitive, same shape — unify and tokenise Low — visual: 14px → 16px in 2 permission tooltips
ColorSwatch gains shape: 'square' | 'circle' Backs BooleanDotIndicator None (additive)
Banner + ModalAlert removed Both had zero consumers None
New close icon in Icon Used by ChipInput None (additive)
IonIcon → Icon in InfoMessage, AccordionCard, IdentifierString, ChipInput Stories would render placeholder spans otherwise Low — icon swap
PasswordRequirements token utilities (text-success/text-danger/fs-small) Drop inline styles Low — spacing/colour shift
Loader.tsx extracted from project-components.js Loader story imports it directly None
Webpack mocks for @ionic/react, dompurify, code-help, _data Real modules break Storybook's bundler None
Storybook Utils stub at .storybook/stubs/utils.js Real common/utils/utils has circular dep with account-store that crashes bundler. Stub may drift from prod — tracked in #7360 to drop once the cycle is broken. Low

How did you test this code?

  • npm run storybook — all 45 stories render in light and dark mode
  • play-driven stories (Modal Open, Tooltip Hovered/Placements, ButtonDropdown Open, DropdownMenu Open, OverflowNav OverflowOpen) capture the open state
  • npm run test:unit -- --testPathPatterns="convertToPConfidence|fromParam" passes
  • npx eslint --fix clean on all touched files

QA — smoke test before merge

P1 — visual changes worth a look

  • BooleanDotIndicator (14→16px circle) — Project Settings → Permissions; hover a row, check the dot
  • PasswordRequirements — Sign-up / password change; verify row spacing, icon colour, dark mode
  • InfoMessage close button — Announcement banner with isClosable; close icon should be currentColor, not red
  • ChipInput close icon (16px) — Tag inputs (segments, identity traits)
  • AccordionCard chevron — Feature Summary card open/close
  • IdentifierString info icon — Identities page, identity name with a space

P2 — should be no-op

  • Click through all 45 stories in Storybook (light + dark)
  • Spot-check Chromatic baselines vs main

Follow-ups

@talissoncosta talissoncosta requested a review from a team as a code owner April 2, 2026 03:47
@talissoncosta talissoncosta requested review from kyle-ssg and removed request for a team April 2, 2026 03:47
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

⚠️ Code review skipped — your organization's overage spend limit has been reached.

Code review is billed via overage credits. To resume reviews, an organization admin can raise the monthly limit at claude.ai/admin-settings/claude-code.

Once credits are available, reopen this pull request to trigger a review.

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 2, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

3 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs Ignored Ignored Preview Apr 28, 2026 7:52pm
flagsmith-frontend-preview Ignored Ignored Preview Apr 28, 2026 7:52pm
flagsmith-frontend-staging Ignored Ignored Preview Apr 28, 2026 7:52pm

Request Review

@talissoncosta talissoncosta marked this pull request as draft April 2, 2026 03:47
@talissoncosta talissoncosta marked this pull request as draft April 2, 2026 03:47
@github-actions github-actions Bot added front-end Issue related to the React Front End Dashboard feature New feature or request labels Apr 2, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 2, 2026

Docker builds report

Image Build Status Security report
ghcr.io/flagsmith/flagsmith-api-test:pr-7100 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith-e2e:pr-7100 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith-api:pr-7100 Finished ✅ Results
ghcr.io/flagsmith/flagsmith:pr-7100 Finished ✅ Results
ghcr.io/flagsmith/flagsmith-private-cloud:pr-7100 Finished ✅ Results
ghcr.io/flagsmith/flagsmith-frontend:pr-7100 Finished ✅ Results

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 2, 2026

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  10 passed

Details

stats  10 tests across 7 suites
duration  23.9 seconds
commit  20167e3
info  🔄 Run: #15628 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  10 passed

Details

stats  10 tests across 7 suites
duration  29.4 seconds
commit  20167e3
info  🔄 Run: #15628 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  16 passed

Details

stats  16 tests across 13 suites
duration  34.7 seconds
commit  20167e3
info  🔄 Run: #15628 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  2 passed

Details

stats  2 tests across 2 suites
duration  53.6 seconds
commit  20167e3
info  🔄 Run: #15628 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  10 passed

Details

stats  10 tests across 7 suites
duration  46.7 seconds
commit  b347360
info  🔄 Run: #15652 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  10 passed

Details

stats  10 tests across 7 suites
duration  28.4 seconds
commit  b347360
info  🔄 Run: #15652 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  2 passed

Details

stats  2 tests across 2 suites
duration  52.1 seconds
commit  b347360
info  🔄 Run: #15652 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  53.5 seconds
commit  b347360
info  🔄 Run: #15652 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  11 passed

Details

stats  11 tests across 8 suites
duration  43.8 seconds
commit  a1746f2
info  🔄 Run: #16404 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  11 passed

Details

stats  11 tests across 8 suites
duration  50.6 seconds
commit  a1746f2
info  🔄 Run: #16404 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  2 passed

Details

stats  2 tests across 2 suites
duration  1 minute, 3 seconds
commit  a1746f2
info  🔄 Run: #16404 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  53.4 seconds
commit  a1746f2
info  🔄 Run: #16404 (attempt 1)

@talissoncosta talissoncosta marked this pull request as draft April 2, 2026 14:14
@talissoncosta talissoncosta force-pushed the chore/design-system-tokens branch 2 times, most recently from c33def5 to 33e4dee Compare April 3, 2026 13:30
Base automatically changed from chore/design-system-tokens to main April 7, 2026 12:54
@talissoncosta talissoncosta force-pushed the feat/storybook-component-stories branch from b347360 to d9f7b74 Compare April 28, 2026 12:24
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Apr 28, 2026
@github-actions github-actions Bot removed the feature New feature or request label Apr 28, 2026
talissoncosta and others added 11 commits April 28, 2026 16:03
Wire up the missing pieces for Storybook to render real app components
in isolation:

- Add webpack mocks for @ionic/react and ionicons/icons so components
  using IonIcon render a placeholder span instead of failing
- Stub helper utilities (escapeHtml, isSaas) used transitively by
  things like code-help so stories don't break on init
- Configure preview.js with a global ReactSelect wrapper that mirrors
  the app's project-components.js Select shim (className=react-select,
  classNamePrefix), shared tooltip/row/form-group window globals, and
  enable autodocs globally so we don't repeat tags per story

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Remove implicit dependencies on window globals, the legacy Flux stores,
and project-wide singletons so components can render in Storybook
without bootstrapping the full app:

- IonIcon → Icon in InfoMessage, AccordionCard, IdentifierString
  (the icon library shim is good for legacy code but stories should
  render with the new Icon system)
- Add explicit Row/Tooltip imports where components relied on window
  globals (Panel, PageTitle, LabelWithTooltip, ToggleChip)
- Inline Utils.colour usage (ToggleChip) and remove dead Utils
  references where possible
- Inline Utils.GUID() with Math.random() in Checkbox and inline
  fromParam with URLSearchParams in Tabs
- Drop the dead enabledButton path and Constants import from
  WarningMessage
- Inline convertToPConfidence into utils.tsx and add unit tests
  (convertToPConfidence.test.ts, fromParam.test.ts)
- Extract the chart/loader SVG from project-components.js into a
  standalone Loader.tsx component
- Extract tagColors from common/constants.ts to a dedicated
  common/constants/tag-colours.ts and update the six callers
  (ColourSelect, CreateEditTag, AddEditTags, StaleFlagWarning,
  EnvironmentSettingsPage, InlineModal)
- Refactor PasswordRequirements to use token utility classes
  (text-success / text-danger, list-unstyled, fs-small) instead of
  inline styles
- SettingRow: relax HTMLAttributes intersection so onChange types
  cleanly through

These changes are no-ops in the running app — same render output —
but unlock isolated rendering in Storybook.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ChipInput previously rendered the chip-remove button with an
ionicon (close) which doesn't follow the new design system Icon
contract. Add a close icon to the design-system Icon component
(matching the existing close-circle SVG paths but standalone) and
update ChipInput to use it at 16px to match production sizing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Banner shipped in #6883 as design-system foundation but has zero
consumers and its current shape (variant + icon + children) is too
thin to replace InfoMessage / WarningMessage / ErrorMessage — those
carry title, isClosable + close, collapseId localStorage memory,
action buttons, and API-error parsing that Banner doesn't.

ModalAlert was an internal modal variant with no remaining call
sites after recent refactors.

Drop both rather than carry unused primitives that would be redesigned
when the migration is actually planned.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ColorSwatch and BooleanDotIndicator are the same primitive (a
decorative coloured shape used as a key) — one square, one circle.
Unify them:

- Add \`shape: 'square' | 'circle'\` to ColorSwatch (default 'square')
- Rewrite BooleanDotIndicator as a thin wrapper that maps \`enabled\`
  to colorSurfaceAction / colorSurfaceMuted tokens and delegates to
  ColorSwatch with shape='circle' size='lg'
- Tokenise the previously hardcoded #6837fc (enabled) and #dbdcdf
  (disabled)

Visual change: the boolean dot grows from 14px to 16px (lg). It only
renders inside permission tooltips, so the diff is barely visible.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add stories for 40 components across the design system, organised into
the following categories:

- Data Display: BarChart, BooleanDotIndicator, ColorSwatch,
  DropdownMenu, IdentifierString, LabelWithTooltip, Logo, SettingTitle,
  StatItem, ToggleChip, Tooltip, VCSProviderTag
- Forms: Button, ButtonDropdown, Checkbox, CheckboxGroup, ChipInput,
  FormGroup, GhostInput, MultiSelect, Radio, SearchableSelect, Switch
- Feedback: EmptyState, ErrorMessage, InfoMessage, Loader,
  PasswordRequirements, Skeleton, WarningMessage
- Patterns: AccordionCard, Card, PageTitle, Panel, SettingRow
- Layout: Column, Flex, Row
- Modals: InlineModal, Modal

Conventions used:

- Args-driven (\`component:\` + \`args:\`) for simple primitives so the
  Controls panel and autodocs props table light up; \`render:\` for
  stories that need state, composition, or layout demonstrations
- Component descriptions on heavyweight stories (Modal, InfoMessage,
  ErrorMessage, WarningMessage, Tooltip, AccordionCard, Panel,
  PageTitle, Card, ColorSwatch, PasswordRequirements)
- Interactive states covered with \`play\` functions on Modal,
  Tooltip, ButtonDropdown, DropdownMenu so Chromatic captures the
  open dialog / hovered tooltip / open menu rather than only the
  trigger
- Source overrides via \`docs.source.code\` where the render function
  doesn't reflect the consumer call (PasswordRequirements)
- Token utility classes (\`d-flex\`, \`gap-*\`, etc.) instead of inline
  styles where Bootstrap utilities exist; chart palette tokens
  (\`colorChart*\`, \`colorSurfaceAction\`, etc.) instead of hardcoded
  hex
- Setting.stories.tsx renamed to SettingRow.stories.tsx to match the
  component name

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The four navigation components (Breadcrumb, OverflowNav, SidebarLink,
Tabs) all need a MemoryRouter context to render in isolation. Extract
the wrapper as \`withRouter\` in documentation/components/_decorators.tsx
so each story imports it instead of inlining the helper.

Stories cover:

- Breadcrumb: default trail, single-level
- OverflowNav: default ProjectNavbar, forced overflow trigger, and
  an Open variant with a play function that clicks the overflow
  trigger so Chromatic captures the popover
- SidebarLink: default and AllStates list
- Tabs: default theme and pill theme (uncontrolled)

Component descriptions added on Tabs explaining the in-page tabs
pattern and when to use it vs other navigation components.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… variant gaps

DRY pass on the layout wrappers used inside story render functions.
The same \`style={{ display: 'flex', alignItems: 'center', gap: 12 }}\`
pattern appeared 14 times across Button, Checkbox, Radio, Switch, and
Skeleton. Replaced with \`d-flex align-items-center gap-2\` and friends.

Variant gaps filled while we were here:

- Button.Variants: add the missing \`icon\` (icon-only buttons) and
  \`project\` (avatar-style picker buttons) themes
- Tooltip: add a Placements story showing top / right / bottom / left

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Button isn't a form input — it's a general-purpose trigger used in
toolbars, page headers, modals, empty states, and CTAs across the app.
Same for ButtonDropdown. Promoting them to top level (alongside Icons)
keeps the Forms category focused on input controls and select pickers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The earlier extraction of \`tagColors\` to \`common/constants/tag-colours.ts\`
wasn't required for any story — no story imports tagColors directly,
and \`Constants.tagColors\` resolves cleanly inside Storybook with the
existing code-help mock.

Revert to reduce PR scope: \`tagColors\` lives back on the Constants
object, the six callers (StaleFlagWarning, EnvironmentSettingsPage,
AddEditTags, ColourSelect, CreateEditTag, utils.tsx) go back to
\`Constants.tagColors\`, and \`common/constants/tag-colours.ts\` is
deleted.

Net: 7 fewer modified files, no behaviour change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ils stub

Several "decoupling" edits in the earlier refactor commit weren't
actually required for stories to render — \`Tooltip\`, \`Row\`,
\`FormGroup\`, etc. are declared globally in \`global.d.ts\` and bound at
runtime by both \`project-components.js\` (app) and \`.storybook/preview.js\`
(stories). Same goes for \`Utils.colour\`, \`Utils.GUID\`, and
\`Utils.fromParam\` — easier to widen the Storybook Utils stub than to
inline these in production code.

Reverted in production:
- LabelWithTooltip: drop explicit Tooltip import
- Panel, PageTitle, InlineModal: drop explicit Row import
- ToggleChip: drop Color import; restore Utils.colour() call
- Checkbox: restore Utils.GUID() call (left ReactMarkdown dead-import
  cleanup intact)
- Tabs: restore Utils.fromParam() call
- EnvironmentSettingsPage: revert leftover whitespace-only formatting

Storybook Utils stub gains \`colour\`, \`GUID\`, \`fromParam\` so the above
calls resolve.

Net: 9 production component files now match origin/main exactly; only
the storybook stub grows by 11 lines.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
talissoncosta and others added 2 commits April 28, 2026 16:50
The close icon was added to Icon.tsx for ChipInput but never registered
in the Icons story, so it didn't show up in the catalogue. Added under
Actions next to close-circle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lint-staged re-applied a Prettier collapse of a multi-line callback
each time the file got staged. Restore origin/main exactly so the file
no longer shows up in the PR diff.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

⚠️ Code review skipped — your organization's overage spend limit has been reached.

Code review is billed via overage credits. To resume reviews, an organization admin can raise the monthly limit at claude.ai/admin-settings/claude-code.

Once credits are available, reopen this pull request to trigger a review.

@github-actions
Copy link
Copy Markdown
Contributor

Visual Regression

16 screenshots compared. See report for details.
View full report

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

Labels

feature New feature or request front-end Issue related to the React Front End Dashboard

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Storybook stories for pure components Spike: Storybook component coverage — findings and blockers

1 participant