Skip to content

Stability audit: F0ChipList / F0DatePicker / F0DurationInput #3885

Description

@eliseo-juan

Stability Audit — F0ChipList / F0DatePicker / F0DurationInput

Auto-generated by the sequential stability audit. Findings are grouped by severity and reviewer area.


BLOCKING

# Area File:line Description Suggested fix
1 Code Review F0ChipList/index.stories.tsx:1 Story file at component root, not in __stories__/ subfolder Move to F0ChipList/__stories__/F0ChipList.stories.tsx
2 Code Review F0ChipList/ No __tests__/ folder or test file exists Create F0ChipList/__tests__/F0ChipList.test.tsx
3 Code Review F0ChipList/index.stories.tsx No Snapshot story with withSnapshot({}) Add export const Snapshot: Story = withSnapshot({})
4 Code Review F0ChipList/index.tsx:57 visibleChips.map((chip, index) => <Chip key={index} />) — array index as key on a list that can reorder Use a stable unique ID from chip (e.g. chip.label or chip.id)
5 Code Review F0ChipList/ChipCounter.tsx:17 list.map((chip, index) => <div key={index}>) — array index as key on overflow list Use a stable unique ID from chip
6 Code Review F0DurationInput/stories/F0DurationInput.stories.tsx:39 type Story = StoryObj<typeof F0DurationInput> — should reference typeof meta Change to type Story = StoryObj<typeof meta>
7 Code Review F0DurationInput/tests/F0DurationInput.test.tsx:2 import "@testing-library/jest-dom/vitest" — FORBIDDEN Remove this import entirely
8 Code Review F0DurationInput/tests/F0DurationInput.test.tsx:4 fireEvent imported from @testing-library/react instead of @/testing/test-utils Change import to @/testing/test-utils
9 Code Review F0DatePicker/stories/F0DatePicker.stories.tsx:164 } as Story["args"] & { dataTestId: string } — forbidden as cast Remove cast; if dataTestId isn't in Story args type, fix the type upstream
10 Code Review F0DatePicker/stories/F0DatePicker.stories.tsx:104 args?.value as DatePickerValue — forbidden as cast in decorator Use a type guard or fix upstream typing
11 Code Review F0DatePicker/stories/F0DatePicker.stories.tsx:148 "day" as CalendarView — forbidden as cast Declare const view: CalendarView = "day" instead
12 Code Review F0DatePicker/ No __tests__/ folder or test file exists Create F0DatePicker/__tests__/F0DatePicker.test.tsx
13 Code Review F0DatePicker/F0DatePicker.tsx Named function F0DatePicker has no displayName set Add F0DatePicker.displayName = "F0DatePicker"
14 A11y F0ChipList/index.tsx (compact layout <div>) Plain <div> wrapping chips has no semantic list role — screen readers get no structure Add role="list" on container, role="listitem" on each chip wrapper
15 A11y F0ChipList/ChipCounter.tsx (<button>) Button wrapping counter chip has no accessible name — "+3" is announced with no context Add aria-label from useI18n() e.g. "Show 3 more"
16 A11y F0ChipList/ChipCounter.tsx Hardcoded English for button label is forbidden All ARIA label strings must come from useI18n()
17 A11y F0DatePicker/components/DateInput.tsx Input opens calendar but has no aria-haspopup="dialog" or aria-expanded — screen readers have no programmatic indication of the popup Add aria-haspopup="dialog" and aria-expanded={isOpen} to the <Input> element
18 A11y F0DatePicker/components/DateInput.tsx Calendar icon passed as icon prop — not verified to be aria-hidden="true" in the <Input> slot Confirm <Input> renders icon with aria-hidden="true" on all icon props
19 A11y F0DurationInput/F0DurationInput.tsx UNIT_LABELS ("Days", "Hours", "Minutes", "Seconds") hardcoded English used as fallback aria-label on inputs Replace with useI18n() translation keys (e.g. duration.unit.days)
20 A11y F0DurationInput/F0DurationInput.tsx required prop accepted but aria-required="true" never set on inputs or group — screen readers cannot detect required state Set aria-required="true" on each <input> segment when required is true
21 Test Coverage F0ChipList/ No __tests__/ folder or test file — zero test coverage Create F0ChipList/__tests__/F0ChipList.test.tsx covering rendering, chips, overflow, counter, and layout variants
22 Test Coverage F0DatePicker/ No __tests__/ folder or test file — zero test coverage Create F0DatePicker/__tests__/F0DatePicker.test.tsx covering rendering, date selection, onChange, and ARIA
23 Test Coverage F0DurationInput/tests/F0DurationInput.test.tsx:2 import "@testing-library/jest-dom/vitest" — FORBIDDEN Remove this import
24 Test Coverage F0DurationInput/tests/F0DurationInput.test.tsx:4 fireEvent imported from @testing-library/react Change to import { fireEvent } from "@/testing/test-utils"

SUGGESTION

# Area File:line Description Suggested fix
1 Code Review F0ChipList/index.tsx type Props instead of interface Props Change to interface F0ChipListProps and export it
2 Code Review F0ChipList/ChipCounter.tsx type Props instead of interface Props Change to interface ChipCounterProps
3 Code Review F0DatePicker/types.ts F0DatePickerProps uses type not interface Convert to export interface F0DatePickerProps
4 Code Review F0DatePicker/types.ts DatePickerValue re-exported as type alias If structurally possible, convert to interface
5 Code Review F0DatePicker/F0DatePicker.tsx:26 useEffect for syncing open prop with eslint-disable comment — incomplete deps Replace with useControllable hook; remove eslint-disable
6 Storybook F0ChipList/index.stories.tsx onClose: () => {} in story args instead of fn() Import fn from @storybook/test and replace
7 Storybook F0ChipList/index.stories.tsx layout argType missing options referencing exported const Export const chipListLayouts = ["compact", "fill"] as const and reference in argTypes
8 Storybook F0ChipList/index.stories.tsx No explicit CompactLayout story mirroring WithFillLayout Add CompactLayout story for symmetry
9 Storybook F0DatePicker/stories/F0DatePicker.stories.tsx Decorator's internal setValue state not reset when args.value changes — Args panel disconnected Add useEffect(() => setValue(args.value), [args.value]) in decorator
10 Storybook F0DurationInput/stories/F0DurationInput.stories.tsx onChange={() => {}} no-ops in Disabled and other render stories — Actions panel won't track them Replace with fn() from @storybook/test
11 Storybook F0DurationInput/stories/F0DurationInput.stories.tsx Default story's render: shadows args.onChange without forwarding it Add onChangeArg?.(nextValue) inside local handler or add comment explaining the pattern
12 A11y F0ChipList/ChipCounter.tsx Popover list <div> wrappers have no list semantics Replace with <ul role="list"> + <li role="listitem">
13 A11y F0DatePicker/components/DateInput.tsx onFocus={() => onOpenChange?.(true)} opens picker on any focus, including keyboard tab — violates WCAG 3.2.1 Open on explicit activation only (click, Enter, Space, or dedicated trigger button)
14 A11y F0DatePicker/components/DateInput.tsx Selected date displayed as plain text with no <time datetime> element Add a visually hidden <time dateTime={isoValue}> or aria-description with ISO date
15 A11y F0DurationInput/F0DurationInput.tsx STATIC_SUFFIXES (d, h, min, s) rendered as plain <span> — screen readers announce redundant unit text Add aria-hidden="true" to each suffix <span>
16 A11y F0DurationInput/F0DurationInput.tsx aria-disabled on outer <div> + native disabled on inputs — double announcement possible Remove aria-disabled from outer div; rely on native disabled on inputs only
17 Test Coverage F0DurationInput/tests/F0DurationInput.test.tsx No test for dataTestId / withDataTestId integration Add test verifying data-testid attribute is applied
18 Test Coverage F0DurationInput/tests/F0DurationInput.test.tsx No test verifying error status applies expected CSS class to container Add assertion for error CSS class alongside data-status
19 Test Coverage F0DurationInput/tests/F0DurationInput.test.tsx No test for onBlur callback after container-click-induced focus + blur cycle Add end-to-end blur normalization test asserting onBlur is called

Ready-to-run agent fix prompt

You are fixing quality issues in the F0 design system component group:
F0ChipList / F0DatePicker / F0DurationInput.

Relevant files:
- packages/react/src/components/F0ChipList/index.tsx
- packages/react/src/components/F0ChipList/ChipCounter.tsx
- packages/react/src/components/F0ChipList/index.stories.tsx  ← MOVE to __stories__/
- packages/react/src/components/F0DatePicker/F0DatePicker.tsx
- packages/react/src/components/F0DatePicker/types.ts
- packages/react/src/components/F0DatePicker/components/DateInput.tsx
- packages/react/src/components/F0DatePicker/__stories__/F0DatePicker.stories.tsx
- packages/react/src/components/F0DurationInput/F0DurationInput.tsx
- packages/react/src/components/F0DurationInput/__stories__/F0DurationInput.stories.tsx
- packages/react/src/components/F0DurationInput/__tests__/F0DurationInput.test.tsx

Load packages/react/AGENTS.md and packages/react/.skills/ before starting.

Fix ALL BLOCKING items from the audit issue (see issue for full table). Key items:

F0ChipList:
1. Move story file from root to F0ChipList/__stories__/F0ChipList.stories.tsx
2. Create F0ChipList/__tests__/F0ChipList.test.tsx with basic rendering and overflow coverage
3. Add Snapshot story to stories file
4. Replace array index keys with stable chip identifiers in index.tsx and ChipCounter.tsx
5. Add role="list" + role="listitem" on chip container in index.tsx (compact layout)
6. Add i18n-sourced aria-label to ChipCounter button

F0DatePicker:
7. Add F0DatePicker.displayName = "F0DatePicker" after function declaration
8. Fix as-casts in stories: line 104 (args?.value as DatePickerValue), line 164 (as Story["args"] & { dataTestId: string }), line 148 ("day" as CalendarView)
9. Create F0DatePicker/__tests__/F0DatePicker.test.tsx with basic rendering and onChange coverage
10. Add aria-haspopup="dialog" and aria-expanded to DateInput

F0DurationInput:
11. Remove import "@testing-library/jest-dom/vitest" from test file
12. Change fireEvent import to @/testing/test-utils in test file
13. Fix Story type: change StoryObj<typeof F0DurationInput> to StoryObj<typeof meta>
14. Replace UNIT_LABELS hardcoded English with useI18n() translations
15. Add aria-required={required || undefined} to each <input> segment
16. Add aria-hidden="true" to each suffix <span>

After fixing, run:
  pnpm --filter @factorialco/f0-react typecheck
  pnpm --filter @factorialco/f0-react test --run
  pnpm --filter @factorialco/f0-react storybook build

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions