| 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" |
Stability Audit — F0ChipList / F0DatePicker / F0DurationInput
BLOCKING
__stories__/subfolderF0ChipList/__stories__/F0ChipList.stories.tsx__tests__/folder or test file existsF0ChipList/__tests__/F0ChipList.test.tsxSnapshotstory withwithSnapshot({})export const Snapshot: Story = withSnapshot({})visibleChips.map((chip, index) => <Chip key={index} />)— array index as key on a list that can reorderchip(e.g.chip.labelorchip.id)list.map((chip, index) => <div key={index}>)— array index as key on overflow listchiptype Story = StoryObj<typeof F0DurationInput>— should referencetypeof metatype Story = StoryObj<typeof meta>import "@testing-library/jest-dom/vitest"— FORBIDDENfireEventimported from@testing-library/reactinstead of@/testing/test-utils@/testing/test-utils} as Story["args"] & { dataTestId: string }— forbiddenascastdataTestIdisn't in Story args type, fix the type upstreamargs?.value as DatePickerValue— forbiddenascast in decorator"day" as CalendarView— forbiddenascastconst view: CalendarView = "day"instead__tests__/folder or test file existsF0DatePicker/__tests__/F0DatePicker.test.tsxF0DatePickerhas nodisplayNamesetF0DatePicker.displayName = "F0DatePicker"<div>)<div>wrapping chips has no semantic list role — screen readers get no structurerole="list"on container,role="listitem"on each chip wrapper<button>)aria-labelfromuseI18n()e.g. "Show 3 more"useI18n()aria-haspopup="dialog"oraria-expanded— screen readers have no programmatic indication of the popuparia-haspopup="dialog"andaria-expanded={isOpen}to the<Input>elementCalendaricon passed asiconprop — not verified to bearia-hidden="true"in the<Input>slot<Input>renders icon witharia-hidden="true"on all icon propsUNIT_LABELS("Days","Hours","Minutes","Seconds") hardcoded English used as fallbackaria-labelon inputsuseI18n()translation keys (e.g.duration.unit.days)requiredprop accepted butaria-required="true"never set on inputs or group — screen readers cannot detect required statearia-required="true"on each<input>segment whenrequiredis true__tests__/folder or test file — zero test coverageF0ChipList/__tests__/F0ChipList.test.tsxcovering rendering, chips, overflow, counter, and layout variants__tests__/folder or test file — zero test coverageF0DatePicker/__tests__/F0DatePicker.test.tsxcovering rendering, date selection, onChange, and ARIAimport "@testing-library/jest-dom/vitest"— FORBIDDENfireEventimported from@testing-library/reactimport { fireEvent } from "@/testing/test-utils"SUGGESTION
type Propsinstead ofinterface Propsinterface F0ChipListPropsand export ittype Propsinstead ofinterface Propsinterface ChipCounterPropsF0DatePickerPropsusestypenotinterfaceexport interface F0DatePickerPropsDatePickerValuere-exported astypealiasinterfaceuseEffectfor syncingopenprop witheslint-disablecomment — incomplete depsuseControllablehook; removeeslint-disableonClose: () => {}in story args instead offn()fnfrom@storybook/testand replacelayoutargType missingoptionsreferencing exported constconst chipListLayouts = ["compact", "fill"] as constand reference in argTypesCompactLayoutstory mirroringWithFillLayoutCompactLayoutstory for symmetrysetValuestate not reset whenargs.valuechanges — Args panel disconnecteduseEffect(() => setValue(args.value), [args.value])in decoratoronChange={() => {}}no-ops in Disabled and other render stories — Actions panel won't track themfn()from@storybook/testDefaultstory'srender:shadowsargs.onChangewithout forwarding itonChangeArg?.(nextValue)inside local handler or add comment explaining the pattern<div>wrappers have no list semantics<ul role="list">+<li role="listitem">onFocus={() => onOpenChange?.(true)}opens picker on any focus, including keyboard tab — violates WCAG 3.2.1<time datetime>element<time dateTime={isoValue}>oraria-descriptionwith ISO dateSTATIC_SUFFIXES(d,h,min,s) rendered as plain<span>— screen readers announce redundant unit textaria-hidden="true"to each suffix<span>aria-disabledon outer<div>+ nativedisabledon inputs — double announcement possiblearia-disabledfrom outer div; rely on nativedisabledon inputs onlydataTestId/withDataTestIdintegrationdata-testidattribute is applieddata-statusonBlurcallback after container-click-induced focus + blur cycleonBluris calledReady-to-run agent fix prompt