Skip to content

feat: implement spinner component - #59

Merged
Sher-Bakhodirov merged 1 commit into
mainfrom
cdx-525-create-spinner-in-shared-components-ui
Nov 3, 2026
Merged

Sher-Bakhodirov merged 1 commit into
mainfrom
cdx-525-create-spinner-in-shared-components-ui

Conversation

@Sher-Bakhodirov

@Sher-Bakhodirov Sher-Bakhodirov commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

[CDX-525] Create Spinner in Shared Components UI

Adds a shared Spinner

Changes

  • src/components/spinner.tsx - shadcn single-arc spinner adapted to library conventions
  • src/assets/icons/SpinnerIcon.tsx - hand-rolled SVG, like the other icons
  • Exported with its types from src/index.ts; componentOverrides supported
  • Tests (spec/components/Spinner/) and a Storybook story incl. a "centre it yourself" example

API

interface SpinnerProps extends React.ComponentProps<'svg'>, IncludeComponentOverrides<SpinnerOverrides> {
  size?: 'sm' | 'md' | 'lg';   // 16 / 32 / 64px, default md
  label?: string;              // aria-label, default 'Loading…'
}

role="status", colour via currentColor, respects prefers-reduced-motion. No positioning inside the component — consumers centre it themselves.

Pull Request Checklist

Before you submit a pull request, please make sure you have to following:

  • I have added or updated TypeScript types for my changes, ensuring they are compatible with the existing codebase.
  • I have added JSDoc comments to my TypeScript definitions for improved documentation.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have added any necessary documentation (if appropriate).
  • I have made sure my PR is up-to-date with the main branch.

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no API changes)
  • Documentation content changes
  • TypeScript type definitions update
  • Other... Please describe:

Copilot AI lite review requested due to automatic review settings September 16, 2026 11:55
@Sher-Bakhodirov
Sher-Bakhodirov requested a review from a team as a code owner September 16, 2026 11:55

@constructor-claude-bedrock constructor-claude-bedrock Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This PR adds a well-structured Spinner component following the established library conventions, with good accessibility, test coverage, and Storybook documentation.

Inline comments: 5 discussions added

Overall Assessment: ⚠️ Needs Work

Comment thread src/components/spinner.tsx Resolved
Comment thread src/components/spinner.tsx Resolved
Comment thread src/assets/icons/SpinnerIcon.tsx Resolved

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Primitive component overrides are accepted by the type but ignored by RenderPropsWrapper, causing the default SVG to render.

Pull request overview

Adds a reusable, accessible Spinner component with size variants, animation support, overrides, exports, tests, and Storybook examples.

Changes:

  • Added Spinner component and SVG icon.
  • Added public exports and type definitions.
  • Added tests and Storybook documentation.
File summaries
File Summary
src/stories/components/Spinner/Spinner.stories.tsx Storybook examples
src/index.ts Public exports
src/components/spinner.tsx Spinner implementation and API
src/assets/icons/SpinnerIcon.tsx Spinner SVG icon
spec/components/Spinner/Spinner.test.tsx Component behavior tests
Review details

Suppressed comments (1)

src/components/spinner.tsx:49

  • reactNode is typed as ReactNode, so this accepts primitive overrides such as a string, number, or false, but RenderPropsWrapper only recognizes function/object values (src/utils/RenderPropsWrapper.tsx:24-34). Passing one of those valid overrides to Spinner therefore silently renders the default SVG instead. Update the shared wrapper to treat any explicitly defined override as an override, or narrow the override type consistently.
    <RenderPropsWrapper props={renderProps} override={componentOverrides?.reactNode}>
  • Files reviewed: 5/5 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@niizom niizom left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me

@Alexey-Pavlov Alexey-Pavlov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

import SpinnerIcon from '@/assets/icons/SpinnerIcon';

const spinnerVariants = cva(
'cio-components cio-spinner cio:block cio:shrink-0 cio:animate-spin cio:motion-reduce:animate-none',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the ticket's "Verified in PLP UI" step isn't mentioned here, so I asked Claude to run this test: packed this branch, installed it into a PLP checkout and rendered <Spinner size='lg' /> inside PLP's .cio-plp-loading wrapper, the spot where CioPlpGrid renders spinner || <Spinner />. The API side is fine: the types compile against PLP's React 18 types, and the spinner is centred and spins. One thing to be aware of, though: the hook class cio-spinner is the same one PLP's legacy .cio-spinner rule styles for the four-ring spinner. That rule is unlayered, while our utilities are emitted into a cascade layer (layer(utilities)), so at equal specificity PLP's rule wins. With PLP's stylesheet loaded the svg comes out 70px wide, inline-block and position: relative. At lg inside the absolute wrapper it still looks right, because the arc keeps its aspect ratio, but sm and md sit in a 70px-wide box (the spacing in the first screenshot), and any customer CSS written against .cio-spinner lands on the svg too.

I don't think anything needs to change here, since cio-spinner follows the cio-badge / cio-chip naming. How about we note on CDX-630 that PLP has to drop its .cio-spinner rules in the same change?

Image Image

export type SpinnerOverrides = ComponentOverrideProps<SpinnerProps>;

export interface SpinnerProps
extends React.ComponentProps<'svg'>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: children is part of SpinnerProps through React.ComponentProps<'svg'>, but whatever a consumer passes is dropped, because SpinnerIcon renders its own arc path as the only child. So <Spinner>Loading…</Spinner> type-checks and renders without the text. I know the ticket's contract has the wider type, so this is a tweak to it: ChipProps already does this with Omit<React.ComponentProps<'div'>, 'children'>. What do you think about doing the same here, so the type says what the component does?

Suggested change
extends React.ComponentProps<'svg'>,
extends Omit<React.ComponentProps<'svg'>, 'children'>,

@Sher-Bakhodirov
Sher-Bakhodirov merged commit 9b8e9d0 into main Nov 3, 2026
14 checks passed
@Sher-Bakhodirov
Sher-Bakhodirov deleted the cdx-525-create-spinner-in-shared-components-ui branch November 3, 2026 10:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants