Skip to content

Stability audit: F0BigNumber #3881

Description

@eliseo-juan

Stability Audit — F0BigNumber

Auto-generated by the sequential stability audit. Covers Code Review, A11y, Storybook, and Test Coverage.


BLOCKING

Severity Area File:line Description Suggested fix
BLOCKING Code Review types.ts:12 Public prop shapes use type instead of interface — violates AGENTS.md convention Change type F0BigNumberPropsinterface F0BigNumberProps
BLOCKING Code Review types.ts:17 comparison is required but Default story omits it; 5 tests use unknown as number cast Make comparison?: ... optional
BLOCKING Code Review F0BigNumber.tsx:11 Relative cross-component import "../tags/F0TagBalance" bypasses public @/components/ alias and imports pre-HOC component Replace with import { F0TagBalance } from "@/components/tags/F0TagBalance"
BLOCKING Code Review F0BigNumber.tsx:11 Imports raw F0TagBalance (no withDataTestId HOC) — dataTestId on the inner tag is silently non-functional Import via public barrel or apply HOC
BLOCKING Code Review F0BigNumber.tsx:49-55 Duplicate !comparisonValue guard in useMemo creates dead code — second branch is unreachable Remove duplicate guard; keep a single early-return
BLOCKING A11y F0BigNumber.tsx:61-62 Label rendered in a bare <div> with no programmatic association to the value <span> — WCAG 1.3.1 Wrap in <figure>/<figcaption> or add role="group" + aria-labelledby
BLOCKING A11y F0BigNumber.tsx:64 <span> for formattedValue has no programmatic context for screen readers — WCAG 1.3.1/1.3.2 Associate via aria-labelledby pointing to the label element
BLOCKING A11y F0BigNumber.tsx:78-88 Skeleton has no aria-busy="true", aria-live="polite", or accessible loading text — WCAG 4.1.3 Add aria-busy="true" aria-live="polite" to skeleton root; add <span className="sr-only">{t("loading")}</span>
BLOCKING A11y F0BigNumber.tsx:80 cursor-progress conveys loading state to sighted users only — WCAG 1.3.3, 4.1.2 Add sr-only loading text via useI18n()
BLOCKING A11y F0TagBalance.tsx:90 Trend icon is decorative but not aria-hidden="true" — exposed to AT — WCAG 1.1.1 Add aria-hidden="true" to the trend icon
BLOCKING Storybook F0BigNumber.stories.tsx:12 const meta: Meta<...> must be satisfies Meta<typeof F0BigNumber> — violates story conventions Change to const meta = { ... } satisfies Meta<typeof F0BigNumber>
BLOCKING Storybook F0BigNumber.stories.tsx:78 StoryObj<typeof F0BigNumber> must be StoryObj<typeof meta> Change every StoryObj<typeof F0BigNumber>StoryObj<typeof meta>
BLOCKING Storybook F0BigNumber.stories.tsx:80-89 Default story omits the required comparison prop — story renders incorrectly Add comparison arg to Default story (or make prop optional first)
BLOCKING Storybook F0BigNumber.stories.tsx:169-176 Snapshot story uses withSnapshot({}) but also renders normal content — causes double Chromatic capture Move snapshot-only content inside withSnapshot; remove duplicate rendering
BLOCKING Test Coverage F0BigNumber.test.tsx:1 screen imported from @testing-library/react — must come from @/testing/test-utils import { screen, zeroRender } from "@/testing/test-utils"
BLOCKING Test Coverage F0BigNumber.test.tsx:2 Forbidden import "@testing-library/jest-dom/vitest" — globally provided via test setup Remove the import entirely
BLOCKING Test Coverage F0BigNumber.test.tsx:810-813 container.querySelector with CSS class selector — fragile and forbidden Replace with screen.getByRole / screen.getByTestId
BLOCKING Test Coverage F0BigNumber.test.tsx:204-208 container.querySelectorAll("div") for absence check — vacuous logic (always finds divs) Use screen.queryByRole or screen.queryByTestId with correct assertion

SUGGESTION

Severity Area File:line Description Suggested fix
SUGGESTION Code Review F0BigNumber.tsx:48 useMemo wrapping primitive arithmetic — no measurable benefit Remove useMemo; compute inline
SUGGESTION Code Review index.tsx:2 export * leaks NumberWithFormatter internal type into the public API Export only the specific public symbols
SUGGESTION Code Review F0BigNumber.tsx:29 No forwardRef — ref accepted by HOC wrapper silently resolves to null Add forwardRef support
SUGGESTION Code Review F0BigNumber.tsx:4-5 Deep-path imports from @/lib/numeric/utils/… instead of barrel export Import from the nearest barrel
SUGGESTION Code Review F0BigNumber.tsx:64 Hardcoded Tailwind classes font-bold text-2xl instead of F0Text / design tokens Use <F0Text> with appropriate variant
SUGGESTION A11y F0TagBalance.tsx:86 Hardcoded English string in ARIA attribute ("positive balance") — not i18n-safe Replace with useI18n() translation key
SUGGESTION A11y F0BigNumber.tsx:62 Label bare <div> lacks semantic element for screen reader grouping Use <figcaption> or semantic heading element
SUGGESTION A11y skeleton Any added sr-only loading text must use useI18n(), not hardcoded English Pass translated string from useI18n()
SUGGESTION Storybook stories play function inline type annotation is redundant — inferred from StoryObj Remove explicit play: (args: ...) => type annotation
SUGGESTION Storybook Snapshot story as BigNumberProps cast in Snapshot story masks potential type error Remove cast; fix the underlying type mismatch
SUGGESTION Storybook stories No story for comparisonHint without trend — untested combination Add a dedicated story
SUGGESTION Test Coverage F0BigNumber.test.tsx:27-33 Double vi.mock for same component — symptom of wrong import path Fix import path; remove duplicate mock
SUGGESTION Test Coverage tests animated prop on NumberWithFormatter never tested Add test for animated number transitions
SUGGESTION Test Coverage tests trend: null path in normalizeTrend is never exercised Add unit test for null trend case
SUGGESTION Test Coverage tests expect(container).toBeInTheDocument() is always-true assertion Remove or replace with a meaningful assertion
SUGGESTION Test Coverage tests unknown as number cast used in 5 tests to work around required comparison Resolved once comparison is made optional
SUGGESTION Code Review types types.ts — making comparison optional would eliminate 5 unknown as number casts in tests Make comparison?: ... optional

Ready-to-run agent fix prompt

You are fixing stability issues in `packages/react/src/components/F0BigNumber/`.
Read `packages/react/AGENTS.md` and `packages/react/.skills/f0-quality-gate/SKILL.md` before making any changes.

Fix ALL of the following in one PR:

### Code Review
1. `types.ts:12` — Change `type F0BigNumberProps` → `interface F0BigNumberProps`
2. `types.ts:17` — Make `comparison` optional: `comparison?: ...`
3. `F0BigNumber.tsx:11` — Replace relative `"../tags/F0TagBalance"` import with `@/components/tags/F0TagBalance` public alias
4. `F0BigNumber.tsx:49-55` — Remove duplicate `!comparisonValue` guard in `useMemo`
5. `F0BigNumber.tsx:29` — Add `forwardRef` support
6. `F0BigNumber.tsx:64` — Replace hardcoded Tailwind `font-bold text-2xl` with `<F0Text>` variant
7. `index.tsx:2` — Replace `export *` with explicit named exports

### A11y
8. `F0BigNumber.tsx:61-64` — Wrap label+value in `<figure>`/`<figcaption>` (or `role="group"` + `aria-labelledby`) for WCAG 1.3.1
9. `F0BigNumber.tsx:78-88` — Add `aria-busy="true" aria-live="polite"` to skeleton root; add `<span className="sr-only">{t("loading")}</span>` via `useI18n()`
10. `F0TagBalance.tsx:90` — Add `aria-hidden="true"` to the decorative trend icon
11. `F0TagBalance.tsx:86` — Replace hardcoded English ARIA string with `useI18n()` translation

### Storybook
12. `F0BigNumber.stories.tsx:12` — Change `const meta: Meta<...>` → `const meta = { ... } satisfies Meta<typeof F0BigNumber>`
13. `F0BigNumber.stories.tsx:78` — Change all `StoryObj<typeof F0BigNumber>` → `StoryObj<typeof meta>`
14. `F0BigNumber.stories.tsx:80-89` — Add missing `comparison` arg to Default story (or omit once optional)
15. `F0BigNumber.stories.tsx:169-176` — Fix `Snapshot` story to avoid double Chromatic capture

### Tests
16. `F0BigNumber.test.tsx:1` — Change `screen` import to `@/testing/test-utils`; use `zeroRender` instead of `render`
17. `F0BigNumber.test.tsx:2` — Remove forbidden `import "@testing-library/jest-dom/vitest"`
18. `F0BigNumber.test.tsx:810-813` — Replace `container.querySelector` with `screen.getByRole` / `screen.getByTestId`
19. `F0BigNumber.test.tsx:204-208` — Replace `container.querySelectorAll("div")` with `screen.queryByRole` / `screen.queryByTestId`
20. Remove double `vi.mock` caused by wrong import path (item 3 above fixes root cause)
21. Add test for `animated` prop
22. Add test for `trend: null` path in `normalizeTrend`

Run `pnpm test --filter @factorialco/react -- F0BigNumber` and `pnpm storybook build` to verify all changes pass before opening the PR.

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