Skip to content

feat(counter): add counter application with increment, decrement and reset - #2

Open
vidyashreebv wants to merge 5 commits into
devfrom
feature/counter-component
Open

vidyashreebv wants to merge 5 commits into
devfrom
feature/counter-component

Conversation

@vidyashreebv

Copy link
Copy Markdown
Collaborator

What does this PR do?

  • Adds Button atom component with primary, secondary, and danger variants
  • Adds Display atom component to show counter value with accessibility support
  • Adds Counter organism with increment, decrement, and reset functionality
  • Integrates Counter component in main App with green theme
  • Adds comprehensive unit tests for all components (27 tests total)

What steps must a reviewer take to test the PR manually?

  1. Checkout feature/counter-component branch
  2. Run npm install
  3. Run npm run dev - verify app starts at http://localhost:5173
  4. Test counter functionality:
    • Click + button to increment
    • Click - button to decrement
    • Click Reset to return to 0
    • Verify smooth animations and green theme
  5. Run npm run test - verify all 27 tests pass
  6. Test accessibility with keyboard navigation (Tab, Enter)
  7. Verify responsive design

Screenshots

Screenshot 2026-01-07 at 4 45 20 PM Screenshot 2026-01-07 at 4 45 31 PM Screenshot 2026-01-07 at 4 45 46 PM

Definition of done Checklist

Code Quality

  • I have written clean, readable code that has been refactored to the best of my ability
  • I have created clean and proper types instead of any
  • I have fixed all console warning arising due to my newly added code, ran linting and code formatting on every file
  • I have deleted all non-descriptive comments and dead code from the files I have touched

Testing

  • I have written unit tests for all foundational component files I have worked on

onClick={onClick}
disabled={disabled}
aria-label={ariaLabel}
className={clsx(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Good use of clsx here.

Can you define it at the top and assign it as a variable here? it gives more readability and clean-code.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Great start with test cases, we'll plan more learning in testing in future.

Comment on lines +12 to +13
aria-label={label ? `${label}: ${value}` : `counter value: ${value}`}
>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

 const cardAriaLabel = label ? `${label}: ${value}` : `counter value: ${value}`

we can use this variable for "aria-label" attribute.


<div className="flex gap-4 mb-6">
<Button
onClick={decrement}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

these are handle functions, we have give a convention in function namings

Comment on lines +8 to +10
const increment = () => setCount(count + step)
const decrement = () => setCount(count - step)
const reset = () => setCount(initialValue)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Handle click function should have function conventions.

Suggested change
const increment = () => setCount(count + step)
const decrement = () => setCount(count - step)
const reset = () => setCount(initialValue)
const handleClickIncrement = () => setCount(count + step)
const handleClickDecrement = () => setCount(count - step)
const handleClickReset = () => setCount(initialValue)

</div>

<Button
onClick={reset}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
onClick={reset}
onClick={() => setCount(initialValue)}

since it's one-liner this could work.

import Button from '../../atoms/Button/Button'
import Display from '../../atoms/Display/Display'

const Counter = ({ initialValue = 0, step = 1 }) => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@ashik-shaji

Copy link
Copy Markdown
Collaborator

@cw-pr-agent review

@mergemitra

mergemitra Bot commented Jan 14, 2026

Copy link
Copy Markdown

Change Summary

This PR implements a Counter feature composed of new Button and Display atomic components and a Counter organism. It integrates the Counter into App with a green gradient theme, updates Tailwind import, and adds unit tests plus a Testing Library devDependency.

Note: PR description mentions "27 tests total" but the diff includes three new test files (Button, Display, Counter); additional tests may exist outside shown diffs.

File Changes

File Summary
package.json Adds @testing-library/dom to devDependencies to support additional DOM assertions in unit tests.
src/App.jsx Integrates Counter into App and updates layout to a green gradient background with padding.
src/index.css Replaces Tailwind directive blocks with a single @import "tailwindcss" statement.
src/components/atoms/Button/Button.jsx Adds Button atom with primary, secondary, danger variants, disabled state, aria-label, and clsx-based classes.
src/components/atoms/Button/Button.test.jsx Adds unit tests for Button covering clicks, disabled behavior, variants, aria-label, and custom className.
src/components/atoms/Display/Display.jsx Adds Display atom showing counter value with aria-live, aria-label, optional label, and gradient text styles.
src/components/atoms/Display/Display.test.jsx Adds tests for Display validating rendering, labels, aria attributes, negative/large numbers, and custom class.
src/components/organisms/Counter/Counter.jsx Adds Counter organism with stateful increment, decrement, reset, and props for initialValue and step.
src/components/organisms/Counter/Counter.test.jsx Adds comprehensive tests for Counter: initial value, custom initial/step, increments, decrements, and reset.

@mergemitra

mergemitra Bot commented Jan 14, 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

  • ✅ Title follows conventional commits format with clear scope and concise summary
  • ✅ Description includes clear what/why, manual test steps, screenshots, and a done checklist
  • ❌ Button.test expects 'bg-blue-600' but Button component uses 'bg-green-600', causing a test mismatch
  • ❌ Description omits notable changes: package.json devDependency and index.css update aren't mentioned
  • ❌ Some tests use invalid queries: Display.test uses queryByRole('paragraph'), which is not a standard ARIA role

PR Size & Scope

  • ✅ PR size is ideal: 440 lines added across 10 files, focused on a single feature with tests
  • ✅ Adds atoms, organism, integration, and 27 unit tests, so the larger footprint is justified

Commit Messages

  • ✅ All 5 commits follow conventional commits format with clear scopes
  • ✅ Commit messages are descriptive and map cleanly to added components and styling

Notes

Code Correctness & Design Quality

  • 🟠 Missing explicit button type at src/components/atoms/Button/Button.jsx:22 could trigger unintended form submissions when used inside a
  • 🟠 Unvalidated variant lookup at src/components/atoms/Button/Button.jsx:28 can yield undefined classes and silently render unstyled buttons
  • 🔴 Primary variant class assertion expects blue at src/components/atoms/Button/Button.test.jsx:35 but component uses green, causing test failure and blocking CI
  • 🟠 Using setCount(count ± step) at src/components/organisms/Counter/Counter.jsx:8 can apply stale state under React batching and produce incorrect counts

Test Quality & Coverage

  • 💬 Using getByText for button selection at src/components/atoms/Button/Button.test.jsx:17 is brittle versus role/name queries and may miss accessibility regressions
  • 🟠 Querying non-existent 'paragraph' role at src/components/atoms/Display/Display.test.jsx:19 makes the label-absence test ineffective and likely to miss regressions

Code Readability & Maintainability

  • 💬 Handler names increment/decrement/reset at src/components/organisms/Counter/Counter.jsx:8 deviate from handle* convention, reducing scanability in JSX event wiring

const disabledStyles = 'opacity-50 cursor-not-allowed'

return (
<button

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
Add an explicit type="button" default and guard unknown variant values to avoid accidental form submits and silent styling failures.

Suggested Change:

const Button = ({
  children,
  onClick,
  variant = 'primary',
  disabled = false,
  ariaLabel,
  className = '',
  type = 'button',
}) => {
  // ...
  const variantClass = variants[variant] ?? variants.primary

  return (
    <button
      type={type}
      onClick={onClick}
      disabled={disabled}
      aria-label={ariaLabel}
      className={clsx(baseStyles, variantClass, disabled && disabledStyles, className)}
    >
      {children}
    </button>
  )
}

const Counter = ({ initialValue = 0, step = 1 }) => {
const [count, setCount] = useState(initialValue)

const increment = () => setCount(count + step)

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
Use functional state updates (and handle* naming) so rapid/batched updates can't apply stale count values.

Suggested Change:

const handleIncrement = () => setCount((current) => current + step)
const handleDecrement = () => setCount((current) => current - step)
const handleReset = () => setCount(initialValue)

// ...
<Button onClick={handleDecrement} ... />
<Button onClick={handleIncrement} ... />
<Button onClick={handleReset} ... />

expect(handleClick).not.toHaveBeenCalled()
})

it('renders with primary variant by default', () => {

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: 🔴 Critical
Fix the default-variant assertion to match the component styles and prefer role/name queries for resilient, a11y-aligned tests.

Suggested Change:

it('renders with primary variant by default', () => {
  render(<Button>Click me</Button>)
  const button = screen.getByRole('button', { name: /click me/i })
  expect(button).toHaveClass('bg-green-600')
})

expect(screen.getByText('10')).toBeInTheDocument()
})

it('renders without label', () => {

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
Replace the invalid queryByRole('paragraph') check with an assertion that actually verifies no label element is rendered.

Suggested Change:

it('renders without label', () => {
  const { container } = render(<Display value={0} />)
  expect(container.querySelector('p')).toBeNull()
  expect(screen.getByText('0')).toBeInTheDocument()
})

@mergemitra

mergemitra Bot commented Jan 14, 2026

Copy link
Copy Markdown

PR Overview

PR Type: Feature

Focus Areas for Architect Review

Confirm the Tailwind CSS v4 @import "tailwindcss"; approach matches the project build setup, since it replaces the classic @tailwind directives.

Decide whether Counter’s initialValue is intentionally “initial only” or should sync on prop changes for embedding in more dynamic flows.

Consider standardizing atom APIs (e.g., forwarding native button props and aria-*) for consistency and long-term reuse across the component library.

PR Insights

Potential PR Improvements

  • Correctness: Use functional state updates to avoid stale values.
  • Testing: Keep tests synced with style and variant changes.
  • Testing: Prefer queries that match actual accessible roles.
  • Robustness: Validate numeric props like step and initialValue inputs.
  • Code Maintainability: Extract shared style strings into reusable constants.

Strengths

  • Description Quality: PR includes clear manual steps, checklist, and screenshots.
  • Best Practices: Components follow atoms and organisms separation.
  • Testing: Tests cover interactions, variants, and accessibility attributes.
  • Robustness: UI includes aria-labels and aria-live for assistive tech.
  • Code Maintainability: Uses clsx for readable conditional class composition.

@vidya-cw vidya-cw added the Closed and Can't merge Changes are made in another branch/PR or directly merging the latest PR label Jan 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Closed and Can't merge Changes are made in another branch/PR or directly merging the latest PR resolved PR comments

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants