Skip to content

Conversation

@anmolsinghbhatia
Copy link
Collaborator

@anmolsinghbhatia anmolsinghbhatia commented Sep 9, 2025

Description

This PR introduces the animated counter component in Propel.

Media

Media
media

Summary by CodeRabbit

  • New Features

    • Added an AnimatedCounter component with smooth slide-in/out transitions and size options (sm, md, lg).
  • Documentation

    • Added Storybook examples with controls and a demo showing increment/decrement behavior and default values.
  • Style

    • Added new keyframe animations for vertical slide and fade effects used by the component.
  • Chores

    • Export and build configuration updated to make the new component available to consumers.

@anmolsinghbhatia anmolsinghbhatia self-assigned this Sep 9, 2025
Copilot AI review requested due to automatic review settings September 9, 2025 03:47
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 9, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Warning

Rate limit exceeded

@anmolsinghbhatia has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 14 minutes and 16 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 8d0fa9d and e449009.

📒 Files selected for processing (1)
  • packages/propel/src/animated-counter/animated-counter.tsx (1 hunks)

Walkthrough

Adds a new AnimatedCounter React component, Storybook stories, CSS keyframe animations, package exports and build entries to expose the component from the propel package.

Changes

Cohort / File(s) Summary
Component implementation
packages/propel/src/animated-counter/animated-counter.tsx
New React AnimatedCounter component and AnimatedCounterProps interface. Manages display state, animation direction, and a 250ms slide transition between numeric values; uses size classes and cn helper.
Barrel exports
packages/propel/src/animated-counter/index.ts
New re-exports: AnimatedCounter and AnimatedCounterProps from the component file.
Package exports
packages/propel/package.json
Adds "./animated-counter": "./dist/animated-counter/index.js" to the package exports map.
Build config
packages/propel/tsdown.config.ts
Adds src/animated-counter/index.ts to the tsdown build entry list to produce dist output.
Storybook
packages/propel/src/animated-counter/animated-counter.stories.tsx
New Storybook stories module: meta export, Default story rendering a demo wrapper with increment/decrement controls, size argType, default args (count: 5, size: "md").
Global styles (Tailwind config)
packages/tailwind-config/global.css
Adds keyframe animations: slideInFromBottom, slideInFromTop, slideOut, slideOutDown, and fadeOut used for vertical slide/fade transitions.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant App as Consumer
  participant Counter as AnimatedCounter
  participant CSS as CSS/DOM
  participant Timer as setTimeout(250ms)

  User->>App: triggers count change (increment/decrement)
  App->>Counter: props { count, size }
  Counter->>Counter: compute direction (up/down), set displayCount, isAnimating, animationKey++
  Counter->>CSS: render prev/current spans with direction classes
  CSS-->>Counter: visual slide in/out (250ms)
  Counter->>Timer: start 250ms timeout
  Timer-->>Counter: timeout fires
  Counter->>Counter: clear isAnimating, commit state
  Counter->>CSS: render stable number
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

🌟enhancement

Suggested reviewers

  • prateekshourya29
  • vamsikrishnamathala

Pre-merge checks (2 passed, 1 warning)

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR description only provides a brief overview and a media attachment but omits several required sections from the repository template, including “Type of Change,” “Test Scenarios,” and “References,” and the media heading does not match the expected “Screenshots and Media (if applicable).” Update the PR description to fully follow the template by adding the “Type of Change” checklist, “Test Scenarios,” and “References” sections, and rename the media heading to “Screenshots and Media (if applicable)” with appropriate content.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title concisely references the ticket and clearly summarizes the primary change of adding the animated counter component to Propel, making its purpose immediately understandable to reviewers.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

Poem

A carrot-coded hop, the numbers leap,
I nibble bugs and count in sleep.
Slide up, slide down, a rhythmic beat,
Tiny paws keep every tick neat.
Animated joy — a rabbit's feat. 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev-propel-animated-counter

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@makeplane
Copy link

makeplane bot commented Sep 9, 2025

Pull Request Linked with Plane Work Items

Comment Automatically Generated by Plane

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces a new AnimatedCounter component to the propel package that provides smooth visual transitions when a numeric count value changes.

  • Adds a new AnimatedCounter React component with configurable size variants
  • Includes animation logic for sliding effects when count values increase or decrease
  • Provides Storybook documentation and interactive demos for the component

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
packages/propel/tsdown.config.ts Adds build entry point for animated-counter component
packages/propel/src/animated-counter/index.ts Exports AnimatedCounter component and types
packages/propel/src/animated-counter/animated-counter.tsx Core component implementation with animation logic
packages/propel/src/animated-counter/animated-counter.stories.tsx Storybook stories for component documentation
packages/propel/package.json Adds package export path for animated-counter

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (11)
packages/propel/src/animated-counter/animated-counter.tsx (6)

24-42: Avoid timer drift: end animation via onAnimationEnd instead of setTimeout(250)

Hard-coding 250 ms can desync if CSS timing changes. Prefer onAnimationEnd.

Apply:

   useEffect(() => {
     if (count !== prevCount) {
       setDirection(count > prevCount ? "up" : "down");
       setIsAnimating(true);
       setAnimationKey((prev) => prev + 1);

       // Update the display count immediately, animation will show the transition
       setDisplayCount(count);

-      // End animation after CSS transition
-      const timer = setTimeout(() => {
-        setIsAnimating(false);
-        setDirection(null);
-        setPrevCount(count);
-      }, 250);
-
-      return () => clearTimeout(timer);
+      // End handled by onAnimationEnd on the current span.
+      return;
     }
   }, [count, prevCount]);
+
+  const handleAnimationEnd = () => {
+    setIsAnimating(false);
+    setDirection(null);
+    setPrevCount(count);
+  };

And wire the handler as shown in a later comment.


52-66: Duplicate animation sources (class vs inline style) — consolidate to one

You set animation via both Tailwind's arbitrary animate-[...] and inline style; inline style wins, making the class redundant and confusing.

Apply:

-            "animate-[slideOut_0.25s_ease-out_forwards]",
+            // animation driven via style only
-          isAnimating && "animate-[slideIn_0.25s_ease-out_forwards]",
+          // animation driven via style only

And add onAnimationEnd on the current span:

-      <span
+      <span
+        onAnimationEnd={isAnimating ? handleAnimationEnd : undefined}

Also applies to: 75-76


10-14: Type-safety nit: lock keys with satisfies

Prevents accidental key typos and preserves literal types.

-const sizeClasses = {
+const sizeClasses = {
   sm: "text-xs h-4 w-4",
   md: "text-sm h-5 w-5",
   lg: "text-base h-6 w-6",
-};
+} as const satisfies Record<"sm" | "md" | "lg", string>;

44-48: Reduce duplication: apply sizing to container only; move className to container

Size is set on container; repeating sizeClass on children is redundant. Also pass className to the container for flexibility.

-  const sizeClass = sizeClasses[size];
+  const sizeClass = sizeClasses[size];

   return (
-    <div className={cn("relative inline-flex items-center justify-center overflow-hidden", sizeClass)}>
+    <div
+      role="status"
+      aria-live="polite"
+      aria-atomic="true"
+      className={cn("relative inline-flex items-center justify-center overflow-hidden tabular-nums", sizeClass, className)}
+    >
...
-            sizeClass,
+            // size from container
...
-          sizeClass,
-          className
+          // size from container

Also applies to: 57-58, 74-79


47-47: Multi-digit counts risk clipping with fixed w-/h-; confirm usage or enable auto width

With w-4/5/6 the container will clip 2+ digits (or negative values). If counts can exceed 9, consider auto width + tabular-nums or a prop to switch sizing modes.

I can propose an auto-width variant (min-w based on digits) if needed.


4-8: Optional: expose duration and reduced-motion handling

Consider a durationMs prop and honoring prefers-reduced-motion to disable animations for accessibility.

I can supply a small hook (usePrefersReducedMotion) and wire it to skip animation styles when true.

packages/propel/src/animated-counter/animated-counter.stories.tsx (5)

23-25: Sync local state with Controls; avoid stale args.count.

Changing count via Storybook Controls won’t reflect after first render. Also prefer nullish coalescing over ||.

-const AnimatedCounterDemo = (args: React.ComponentProps<typeof AnimatedCounter>) => {
-  const [count, setCount] = useState(args.count || 0);
+const AnimatedCounterDemo = (args: React.ComponentProps<typeof AnimatedCounter>) => {
+  const [count, setCount] = useState<number>(args.count ?? 0);
+  // Keep in sync with Controls
+  useEffect(() => {
+    setCount(args.count ?? 0);
+  }, [args.count]);

1-1: Import useEffect for the sync fix.

-import { useState } from "react";
+import { useState, useEffect } from "react";

35-37: Announce count updates for screen readers.

Expose the live-updating value via role="status" and aria-live="polite". If AnimatedCounter already handles this, skip.

-        <div className="flex items-center justify-center min-w-[60px] h-12 bg-gray-50 border border-gray-200 rounded-lg">
+        <div
+          className="flex items-center justify-center min-w-[60px] h-12 bg-gray-50 border border-gray-200 rounded-lg"
+          role="status"
+          aria-live="polite"
+        >

3-3: Prefer importing from the barrel to exercise the published API.

-import { AnimatedCounter } from "./animated-counter";
+import { AnimatedCounter } from ".";

5-7: Group AnimatedCounter under "Components/". Change the title in packages/propel/src/animated-counter/animated-counter.stories.tsx to

title: "Components/AnimatedCounter",

to match other stories (Skeleton, Separator, Tabs).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 291101a and e38e764.

📒 Files selected for processing (5)
  • packages/propel/package.json (1 hunks)
  • packages/propel/src/animated-counter/animated-counter.stories.tsx (1 hunks)
  • packages/propel/src/animated-counter/animated-counter.tsx (1 hunks)
  • packages/propel/src/animated-counter/index.ts (1 hunks)
  • packages/propel/tsdown.config.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Build and lint web apps
🔇 Additional comments (4)
packages/propel/src/animated-counter/index.ts (1)

1-2: LGTM — correct re-exports

Public API shape is clear and consistent.

packages/propel/tsdown.config.ts (1)

6-6: Export mapping wired correctly for animated-counter
Source file exists at packages/propel/src/animated-counter/index.ts and package.json exports “./animated-counter” → “./dist/animated-counter/index.js”.

packages/propel/src/animated-counter/animated-counter.stories.tsx (2)

49-55: Default story looks good.

Interactive demo with increment/decrement and initial args is clear.


5-20: Stronger typing with satisfies Meta<> (optional)
TypeScript 5.8.3 supports satisfies, so you can preserve literal types and catch config drift at compile-time:

-const meta: Meta<typeof AnimatedCounter> = {
+const meta = {
   title: "AnimatedCounter",
   component: AnimatedCounter,
   parameters: {
     layout: "centered",
   },
   tags: ["autodocs"],
   argTypes: {
     size: {
       control: { type: "select" },
       options: ["sm", "md", "lg"],
     },
   },
-};
+} satisfies Meta<typeof AnimatedCounter>;
 
 export default meta;

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
packages/tailwind-config/global.css (3)

721-741: Naming symmetry: add slideOutUp alias for clarity.

We have slideOutDown but slideOut (up) is implicit. Adding an explicit slideOutUp alias improves readability without breaking existing names.

 @keyframes slideOut {
   0% {
     transform: translateY(0);
     opacity: 1;
   }
   100% {
     transform: translateY(-100%);
     opacity: 0;
   }
 }
+
+@keyframes slideOutUp {
+  0% {
+    transform: translateY(0);
+    opacity: 1;
+  }
+  100% {
+    transform: translateY(-100%);
+    opacity: 0;
+  }
+}

699-719: Minor perf nit: consider translate3d for smoother compositing on older/low-end GPUs.

Swapping translateY(...) for translate3d(0, ... , 0) can help avoid occasional subpixel jitter.


699-750: Scope keyframe names or register in Tailwind theme for consistency.

To avoid global name collisions and to standardize usage, either:

  • Prefix names (e.g., propel-slide-in-from-bottom), or
  • Define these in tailwind config (theme.extend.keyframes/animation) and consume via Tailwind utilities.

Low-risk, optional tidy-up.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e38e764 and 8d0fa9d.

📒 Files selected for processing (1)
  • packages/tailwind-config/global.css (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build and lint web apps
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
packages/tailwind-config/global.css (1)

743-750: LGTM on fadeOut.

Simple, reusable, and complements the slide variants.

@sriramveeraghanta sriramveeraghanta merged commit 45688bd into preview Sep 9, 2025
6 of 7 checks passed
@sriramveeraghanta sriramveeraghanta deleted the dev-propel-animated-counter branch September 9, 2025 18:21
yarikoptic pushed a commit to yarikoptic/plane that referenced this pull request Oct 1, 2025
* dev: animated counter added to propel

* chore: animated counter story added

* chore: propel config updated

* chore: code refactor

* chore: code refactor

* fix: format error
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants