[CLOV-119] Migrate bpk-component-progress JS to TypeScript#4486
Conversation
Convert the bpk-component-progress package from JavaScript/Flow to
TypeScript. Replace prop-types and Flow types with a typed `Props`
export and rename all source/test files (.js -> .ts/.tsx). No
business-logic, JSX, or rendering changes - existing snapshots remain
valid.
- Rename index.js -> index.ts and re-export `BpkProgressProps` type
- Rename BpkProgress.js -> BpkProgress.tsx; type `Props` extends
`Omit<HTMLAttributes<HTMLDivElement>, 'className'>` so native div
props flow through `...rest` while keeping `className?: string | null`
- Replace `propTypes` + `defaultProps` with TS `static defaultProps`
- Destructure `onComplete`/`onCompleteTransitionEnd` out of `rest`
(drops the legacy `delete rest.x` workaround)
- `tabIndex="0"` -> `tabIndex={0}` so the prop type-checks; rendered
DOM (`tabindex="0"`) is unchanged and snapshots still match
- Type both class containers in the stories file with explicit
`Props`/`State` generics
- Rename themeAttributes and test files; remove `@flow strict` headers
- Rename snapshot file to BpkProgress-test.tsx.snap so Jest finds it
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR migrates bpk-component-progress from Flow/JavaScript to TypeScript while preserving the component’s runtime behavior and snapshots.
Changes:
- Converts progress component, tests, stories, theme attributes, and package entry point to TypeScript.
- Replaces Flow/PropTypes definitions with exported TypeScript props.
- Renames and preserves Jest snapshots for the TypeScript test file.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
packages/bpk-component-progress/src/BpkProgress.tsx |
Adds TypeScript props, defaults, and typed helper signatures for the progress component. |
packages/bpk-component-progress/src/BpkProgress.stories.tsx |
Adds explicit props/state types to story containers. |
packages/bpk-component-progress/src/BpkProgress-test.tsx |
Removes Flow annotation from migrated test file. |
packages/bpk-component-progress/src/accessibility-test.tsx |
Removes Flow annotation from migrated accessibility test. |
packages/bpk-component-progress/src/themeAttributes.ts |
Removes Flow annotation from theme attributes module. |
packages/bpk-component-progress/src/themeAttributes-test.ts |
Removes Flow annotation from migrated theme attributes test. |
packages/bpk-component-progress/src/__snapshots__/BpkProgress-test.tsx.snap |
Adds renamed snapshot file for the TypeScript test. |
packages/bpk-component-progress/index.ts |
Re-exports the component and its TypeScript props type. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Visit https://backpack.github.io/storybook-prs/4486 to see this build running in a browser. |
kerrie-wu
left a comment
There was a problem hiding this comment.
🤖 Generated with Claude Code
| role="progressbar" | ||
| aria-valuetext={getValueText ? getValueText(value, min, max) : null} | ||
| aria-valuetext={ | ||
| getValueText ? (getValueText(value, min, max) as string) : undefined |
There was a problem hiding this comment.
as string masks an a11y contract gap (recurring pattern)
getValueText is typed (...) => unknown at L58, so as string here only silences TS. At runtime a consumer returning a non-string (number, object, ReactNode) is coerced to "[object Object]" and announced to assistive tech — visually invisible, but an a11y regression.
Same anti-pattern flagged on #4475 (BpkRadio): "This cast only satisfies TypeScript; it still sends non-string React nodes to aria-label at runtime, producing names like [object Object]."
Fix — narrow the source type instead of casting at the use site:
// L58
getValueText?: ((value: number, min: number, max: number) => string) | null;
// L127
aria-valuetext={getValueText ? getValueText(value, min, max) : undefined}The README example already returns a string, so no break for valid usage; bad callers like () => 42 now fail at compile time instead of producing [object Object] at runtime.
There was a problem hiding this comment.
Fixed in c54f20e — narrowed getValueText return type to string and dropped the as string cast. Non-string returns now fail at compile time.
| stepped: boolean, | ||
| small: boolean, | ||
| className: ?string, | ||
| tabIndex: ?number, |
There was a problem hiding this comment.
Nit: tabIndex no longer accepts null.
Old Flow type was tabIndex: ?number (accepts null); the new TS Props inherits tabIndex?: number from HTMLAttributes, so tabIndex={null} is now a TS error. Unlikely to bite in practice — tabIndex={null} is uncommon — but worth a one-liner in the PR description for completeness
There was a problem hiding this comment.
Good catch — added a note to the PR description under "Notable type narrowings". Decided not to widen Props.tabIndex back to number | null so the type stays consistent with the inherited HTMLAttributes<HTMLDivElement> shape (matches BpkRadio etc.). Runtime behavior is unchanged.
Address review feedback: the previous `as string` cast satisfied TypeScript but still let consumers return non-string values, which the DOM stringifies to "[object Object]" — a poor accessible name announced by screen readers. Tighten `getValueText`'s return type from `unknown` to `string` so non-conforming callers fail at compile time instead of producing runtime a11y regressions. Drop the now-unnecessary cast at the `aria-valuetext` use site. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Visit https://backpack.github.io/storybook-prs/4486 to see this build running in a browser. |
Summary
bpk-component-progresspackage from JavaScript/Flow to TypeScript.js→.ts/.tsx) and the snapshot file so Jest still finds itpropTypes+ FlowPropswith a typedPropsexport, re-exported asBpkProgressPropsPropsasOmit<HTMLAttributes<HTMLDivElement>, 'className'> & { ... }so native div attrs flow through...restwhile keepingclassName?: string | nullpropTypes+defaultPropswithstatic defaultProps; destructureonComplete/onCompleteTransitionEndout ofrestinstead of the legacydelete rest.xworkaroundtabIndex="0"→tabIndex={0}so the prop type-checks; rendered DOM is unchanged (tabindex="0")Props/StategenericsgetValueTextreturn type fromunknowntostring(addresses review feedback) — non-string returns would have been DOM-stringified to"[object Object]"and announced by screen readersNotable type narrowings (theoretical breaking, runtime unchanged)
tabIndex={null}no longer type-checks — the newPropsinheritstabIndex?: numberfromHTMLAttributes<HTMLDivElement>(was Flow?number). Passundefinedor omit the prop instead. Runtime DOM behavior is unchanged.getValueTextmust now returnstring(was Flowmixed). Existing valid usages already do.Test plan
npm run jest -- packages/bpk-component-progress(3 suites, 10 tests, 5 snapshots all pass)npx tsc --noEmit— no errors inbpk-component-progressnpx eslint packages/bpk-component-progress— no errors, no warnings🤖 Generated with Claude Code