feat(chip): promote OneChip to stable F0Chip component#3817
feat(chip): promote OneChip to stable F0Chip component#3817eliseo-juan wants to merge 3 commits intomainfrom
Conversation
Integrate @storybook/addon-mcp so AI agents can access F0 component documentation, preview stories, and run tests via the MCP protocol. - Upgrade Storybook from 10.1.11 to 10.3.3 - Install and configure @storybook/addon-mcp (v0.4.2) - Enable all toolsets (docs, dev, test) for local dev; docs-only for public builds - Add CORS headers for MCP POST requests in staticwebapp.config.json - Add .mcp.json for agent auto-discovery of local MCP server - Document MCP server usage in AGENTS.md
… from Storybook 10.3 upgrade
Move experimental OneChip to stable src/components/F0Chip with F0 naming
conventions, native button semantics for a11y, and full test coverage.
- Add F0Chip component with types, stories (7 stories + Snapshot), and 20 unit tests
- Use native <button> element for clickable chips (no more div role=button)
- Fix close button: contextual aria-label (Remove {label}), no redundant tabIndex
- Fix nested interactive element issue (button inside button) by sibling layout
- Add aria-disabled support for deactivated+clickable chips
- Replace F0Icon className with color prop
- Update all internal consumers (F0ChipList, F0Select, OneFilterPicker) to import from stable path
- Deprecate experimental/OneChip with aliased re-exports pointing to F0Chip
- Remove old experimental OneChip stories and MDX docs
✅ No New Circular DependenciesNo new circular dependencies detected. Current count: 0 |
📦 Alpha Package Version PublishedUse Use |
🔍 Visual review for your branch is published 🔍Here are the links to: |
There was a problem hiding this comment.
Pull request overview
This PR promotes the previously experimental OneChip into a stable, publicly exported F0Chip component in packages/react, updates internal consumers to use the stable import path, and adds Storybook/testing support. It also introduces Storybook MCP server configuration/docs and bumps several Storybook-related dev dependencies.
Changes:
- Added new stable
src/components/F0Chip/implementation + types, with Storybook stories and unit tests. - Deprecated
experimental/OneChipby re-exportingF0Chip/types/variants, and updated internal consumers to importF0Chip. - Updated Storybook tooling/config (addon versions,
@storybook/addon-mcp, CORS headers) and documented MCP usage (rootAGENTS.md,.mcp.json).
Reviewed changes
Copilot reviewed 20 out of 21 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/react/src/experimental/OneChip/index.tsx | Replaces experimental implementation with deprecated aliases to F0Chip and its types/variants. |
| packages/react/src/experimental/OneChip/index.stories.tsx | Removes old experimental stories (replaced by stable stories). |
| packages/react/src/experimental/OneChip/index.mdx | Removes old experimental MDX docs (migrated to stable stories/docs flow). |
| packages/react/src/experimental/exports.ts | Re-exports deprecated OneChip from experimental barrel for backwards compatibility. |
| packages/react/src/components/OneFilterPicker/components/FilterChipButton.tsx | Updates consumer import to stable F0Chip. |
| packages/react/src/components/F0Select/components/ActiveFiltersChips.tsx | Updates consumer import to stable F0Chip. |
| packages/react/src/components/F0ChipList/index.tsx | Updates consumer import/types to stable F0Chip/F0ChipProps. |
| packages/react/src/components/F0ChipList/ChipCounter.tsx | Updates consumer import/types to stable F0Chip/F0ChipProps. |
| packages/react/src/components/F0Chip/types.ts | Introduces chipVariants, chipVariantValues, and public F0ChipProps typing. |
| packages/react/src/components/F0Chip/index.tsx | Public barrel export for F0Chip + types/variants. |
| packages/react/src/components/F0Chip/F0Chip.tsx | New F0Chip implementation (button semantics, close button, aria-label changes). |
| packages/react/src/components/F0Chip/tests/F0Chip.test.tsx | Adds unit tests for rendering, variants, and interactions. |
| packages/react/src/components/F0Chip/stories/F0Chip.stories.tsx | Adds stable Storybook stories including Snapshot coverage. |
| packages/react/src/components/exports.ts | Exports F0Chip from the stable components barrel. |
| packages/react/package.json | Updates Storybook-related dependencies and adds @storybook/addon-mcp. |
| packages/react/.storybook/static/staticwebapp.config.json | Adds additional CORS headers for the Storybook static deployment. |
| packages/react/.storybook/main.ts | Enables @storybook/addon-mcp with public-build toolset limiting. |
| package.json | Updates eslint-plugin-storybook and pins React via pnpm overrides. |
| AGENTS.md | Documents Storybook MCP server usage and toolsets. |
| .mcp.json | Adds MCP server auto-discovery config for local Storybook MCP endpoint. |
| return ( | ||
| <div | ||
| className={cn( | ||
| chipVariants({ variant }), | ||
| onClose && "pr-1.5", | ||
| avatar && "pl-0.5", | ||
| avatar && avatar.type !== "person" && "rounded-sm", | ||
| icon && !avatar && "pl-1.5" | ||
| )} | ||
| > | ||
| {onClick ? ( | ||
| <button | ||
| type="button" | ||
| className={cn( | ||
| "flex items-center gap-1 bg-transparent p-0 font-[inherit]", | ||
| "cursor-pointer", | ||
| focusRing() | ||
| )} | ||
| onClick={onClick} | ||
| aria-disabled={deactivated || undefined} | ||
| > | ||
| {labelContent} | ||
| </button> | ||
| ) : ( | ||
| <div className="flex items-center gap-1">{labelContent}</div> | ||
| )} |
There was a problem hiding this comment.
When onClick is provided, only the inner <button> is clickable, but the chip’s padding/border live on the outer <div>. This leaves a non-interactive “dead zone” around the label and also means the focus ring is applied to the inner content rather than the visible chip shape. Consider making the clickable element cover the full chip area (e.g. render the root as a <button> when there’s no onClose, or move the padding/border classes onto the button and make it stretch).
| "cursor-pointer", | ||
| focusRing() | ||
| )} | ||
| onClick={onClick} |
There was a problem hiding this comment.
aria-disabled is set when deactivated is true, but the onClick handler will still fire and the button still presents as interactive (cursor-pointer). If the chip is meant to be “disabled” for assistive tech, it should also be non-actionable (e.g. guard onClick when deactivated, and/or add disabled-like styling/behavior such as pointer-events/tabIndex adjustments consistent with other aria-disabled usages in the codebase).
| "cursor-pointer", | |
| focusRing() | |
| )} | |
| onClick={onClick} | |
| deactivated ? "cursor-default" : "cursor-pointer", | |
| focusRing() | |
| )} | |
| onClick={(event) => { | |
| if (deactivated) { | |
| event.preventDefault() | |
| event.stopPropagation() | |
| return | |
| } | |
| onClick(event) | |
| }} |
| export { chipVariants } | ||
|
|
There was a problem hiding this comment.
This module re-exports chipVariants, but the public barrel already re-exports chipVariants from ./types. Keeping this extra re-export in the implementation file makes it unclear which import path should be used and is easy to forget during refactors. Consider removing this re-export and keeping chipVariants exported only from types.ts/index.tsx.
| export { chipVariants } |
| import { F0Chip } from "../index" | ||
| import { chipVariantValues } from "../types" | ||
|
|
There was a problem hiding this comment.
The story imports chipVariantValues from the internal types module even though it’s already exported from the component’s public barrel (../index). Importing from the barrel reduces coupling to internal file structure and keeps stories aligned with the public API surface.
| import { F0Chip } from "../index" | |
| import { chipVariantValues } from "../types" | |
| import { F0Chip, chipVariantValues } from "../index" |
🚪 Why?
Problem
The
Chipcomponent lived insrc/experimental/OneChip/but was used by multiple stable components (F0ChipList,F0Select,OneFilterPicker). It had no public export from the experimental barrel, its accessible semantics were broken (clickable<div>with nested<button>, Close button label was always "Close" regardless of which chip), and had 0% test coverage.🔑 What?
Changes
src/components/F0Chip/withF0Chipcomponent,F0ChipPropstype,chipVariantsCVA config, andchipVariantValuesarray<div role="button">with a native<button>for clickable chips — eliminates nested interactive element a11y violationaria-labelfrom generic"Close"to contextual`Remove ${label}`so AT users know which chip they're removingaria-disabledsupport for deactivated+clickable chipsF0Icon className=usage with thecolorpropF0Chipfromsrc/components/exports.ts(stable public API)@/components/F0Chipexperimental/OneChipwith aliased re-exports toF0Chipfor backwards compatibility__stories__/F0Chip.stories.tsx)Migration Type
SINGLE —
OneChip→F0Chipin one PR; internal consumers updated in the same change.✅ Verification
Tests
src/components/F0Chip/__tests__/F0Chip.test.tsxpnpm tsc --noEmit)pnpm lint:fix && pnpm lint, 0 warnings, 0 errors)