Skip to content

feat(spinner): add resuable spinner component with size variant - #8

Open
LikithaYadavG wants to merge 2 commits into
feat/card-componentfrom
feat/spinner
Open

LikithaYadavG wants to merge 2 commits into
feat/card-componentfrom
feat/spinner

Conversation

@LikithaYadavG

Copy link
Copy Markdown
Collaborator

PR Template & Definition of Done

What does this PR do?

  • Adds accessible Spinner component with size variants (sm, md, lg)

What steps does your reviewer have to take to test this PR manually?

  1. Import and use the Spinner component with different sizes (sm, md, lg)

Pull Request standards checklist - Please check off

  • This Branch will be carrying one single responsibility - feature/bugfix/style/refactor...
  • I have followed conventional commit messages and descriptive Branch naming.
  • My PR has descriptive folder/file names that I have worked on.

Testing checklist - Please check off

  • I have performed manual testing on my local to validate all changes.

If you have not followed and completed any of the above, please explain why below.
N/A

Definition of Done - Please check off

  • My code is well tested and I have confidence my code works as I expect in a variety of situations.
  • I have lint and format code is enabled when the file is saved and I have fixed all errors highlighted by lint.
  • I have deleted all non-descriptive comments and dead code from the files I've touched.
  • I have rebased my branch with the base branch I want to merge into and all the commits in this PR are my own.

@mergemitra

mergemitra Bot commented Jan 19, 2026

Copy link
Copy Markdown

Change Summary

This PR adds an accessible, reusable Spinner component with three size variants (sm, md, lg) and corresponding unit tests. The Spinner exposes SpinnerProps, accepts a custom className, and uses Tailwind CSS utility classes for sizing, borders, and spin animation to provide a consistent loading indicator.

File Changes

File Summary
src/components/spinner/Spinner.test.tsx Adds tests verifying accessibility, default medium size, small/large variants, and custom className.
src/components/spinner/Spinner.tsx Adds Spinner component with SpinnerProps, size variants (sm, md, lg), Tailwind classes, and aria-label.

@mergemitra

mergemitra Bot commented Jan 19, 2026

Copy link
Copy Markdown

PR Scorecard

Score

Communication Quality Code Correctness & Design Quality Test Quality & Coverage Code Readability & Maintainability
Scoring Methodology

Communication Scoring Framework

The overall communication score is a weighted average:

Dimension Weight Evaluates
PR Description Quality 60% Title format (conventional commits) + Description clarity (what changed & why)
PR Size & Scope 25% Appropriate sizing, scope cohesion, and justification for size
Commit Messages 15% Conventional commits format, atomic & descriptive changes

Formula: (Description x 0.6) + (PR Size x 0.25) + (Commits x 0.15)

Code Scoring Framework

The scorecard evaluates code using 3 key reviewer questions:

Reviewer Question Category
Is this the right solution, implemented the right way? Code Correctness
Would this catch bugs if the code broke tomorrow? Test Quality
Can someone new understand and safely modify this in 6 months? Maintainability
PR Communication Notes

Description Quality

  • ✅ Uses PR template and a clear 'What does this PR do?' section describing the spinner and size variants.
  • ✅ Description includes a simple manual test step to import and use the Spinner with sizes.
  • ❌ Title has a typo: 'resuable' should be 'reusable', and 'variant' vs 'variants' is inconsistent; fix for clarity.
  • ❌ Description omits the new test file src/components/spinner/Spinner.test.tsx and lacks instructions for running unit tests.

PR Size & Scope

  • ✅ Small, focused change: 81 lines added across 2 files, within ideal size and scope.
  • ✅ Component and its tests are colocated under src/components/spinner, keeping related changes together.

Commit Messages

  • ✅ Commit message follows conventional commits format: 'feat(spinner): add accessible spinner component with size variants'.
  • ✅ Single commit is focused and descriptive, making review straightforward.

Notes

Code Correctness & Design Quality

  • 🟠 border-3 class at src/components/spinner/Spinner.tsx:9 may not exist in Tailwind defaults, causing inconsistent or missing spinner border styling

Test Quality & Coverage

  • 🟠 Default-size test at src/components/spinner/Spinner.test.tsx:33 always passes size="md", so it doesn’t verify the component’s defaulting behavior
  • 💬 toHaveClass("w-8 h-8") usage at src/components/spinner/Spinner.test.tsx:36 is less explicit than checking individual class tokens

Code Readability & Maintainability

  • 💬 Unused userEvent setup at src/components/spinner/Spinner.test.tsx:16 adds noise and can confuse future readers about interaction coverage
  • 🟠 Hardcoded aria-label and no prop forwarding at src/components/spinner/Spinner.tsx:6 reduces reusability for i18n, testing, and custom accessibility needs
  • 💬 sizeStyles object created per render at src/components/spinner/Spinner.tsx:7 is minor avoidable churn and harder to share across components

className?: string;
}

export const Spinner = ({ size = "md", className = "" }: SpinnerProps) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: 🟠 Major
Make the spinner more reusable/accessibility-friendly by fixing the border-3 class, forwarding standard DOM props, and allowing the accessible label to be customized.

Suggested Change:

import type { HTMLAttributes } from "react";

const SIZE_STYLES = {
  sm: "w-4 h-4 border-2",
  md: "w-8 h-8 border-[3px]", // or a Tailwind-supported border width
  lg: "w-12 h-12 border-4",
} as const;

export type SpinnerProps = {
  size?: keyof typeof SIZE_STYLES;
  ariaLabel?: string;
  className?: string;
} & Omit<HTMLAttributes<HTMLSpanElement>, "aria-label">;

export const Spinner = ({
  size = "md",
  ariaLabel = "Loading",
  className = "",
  ...rest
}: SpinnerProps) => {
  const sizeClass = SIZE_STYLES[size] ?? SIZE_STYLES.md;

  return (
    <span
      role="status"
      aria-live="polite"
      aria-label={ariaLabel}
      className={`inline-block ${sizeClass} border-gray-200 border-t-blue-600 rounded-full animate-spin ${className}`.trim()}
      {...rest}
    />
  );
};

@@ -0,0 +1,62 @@
import { render, screen } from "@testing-library/react";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: 🟠 Major
Update tests to actually verify the component’s default size behavior and remove the unused userEvent setup.

Suggested Change:

import { render, screen } from "@testing-library/react";
import { describe, it, expect } from "vitest";
import { Spinner, type SpinnerProps } from "./Spinner";

const renderSpinner = (props: Partial<SpinnerProps> = {}) => {
  render(<Spinner {...props} />);
  return screen.getByRole("status", { name: /loading/i });
};

describe("Spinner", () => {
  it("should render medium size spinner by default", () => {
    const spinner = renderSpinner(); // no size prop
    expect(spinner).toHaveClass("w-8", "h-8");
  });

  it("should render small size spinner when size is sm", () => {
    const spinner = renderSpinner({ size: "sm" });
    expect(spinner).toHaveClass("w-4", "h-4");
  });

  it("should render large size spinner when size is lg", () => {
    const spinner = renderSpinner({ size: "lg" });
    expect(spinner).toHaveClass("w-12", "h-12");
  });
});

Comment thread src/components/spinner/Spinner.test.tsx Outdated
it("should render medium size spinner by default", () => {
const { spinner } = renderSpinner();

expect(spinner).toHaveClass("w-8 h-8");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: 💬 Minor [nitpick]
Prefer asserting individual class tokens in toHaveClass to avoid ambiguity if class ordering/spacing changes.

Suggested Change:

expect(spinner).toHaveClass("w-8", "h-8");

@mergemitra

mergemitra Bot commented Jan 19, 2026

Copy link
Copy Markdown

PR Overview

PR Type: Feature

Focus Areas for Architect Review

Confirm the Spinner’s intended accessibility contract (explicit role="status", aria-live, and customizable label) so usage is consistent across the design system.

Validate Tailwind border-width tokens (e.g., border-3 vs border-[3px]) are supported in your config to prevent styling regressions in production builds.

PR Insights

Potential PR Improvements

  • Code Maintainability: Consider moving static style maps outside the component.
  • Testing: Avoid unused test setup to keep tests focused.
  • Testing: Add tests for aria-label text to prevent regressions.

Strengths

  • Correctness: Size variants map cleanly to consistent CSS classes.
  • Robustness: Component includes an accessible status role and label.
  • Testing: Tests cover default, variants, and custom className behavior.
  • Description Quality: PR description gives clear steps for manual testing.
  • PR Size: Change scope is small and focused on one feature.

@mergemitra

mergemitra Bot commented Feb 4, 2026

Copy link
Copy Markdown

Tip

Need another review?

Tag me and say rereview for re-analysis after you have fixed all the issues.

@cw-pr-agent rereview

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.

1 participant