Refactoring animation code#7457
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (4)
WalkthroughThe PR replaces the queue-based ChangesAnimation System Refactor
Sequence Diagram(s)sequenceDiagram
participant Component as JavascriptAnimate / CSSTransitionAnimate
participant useAnimationManager
participant animationControllerImpl
participant RechartsAnimation as JavascriptAnimation / CSSTransition
participant TimeoutController as RequestAnimationFrameTimeoutController
participant setState
Component->>useAnimationManager: animationManagerFromProps?
useAnimationManager-->>Component: AnimationController (from props or context)
Component->>RechartsAnimation: new JavascriptAnimation(from, to, easing, duration, begin, callbacks)
Note right of RechartsAnimation: onAnimationStart called immediately
Component->>animationControllerImpl: (timeoutController, animation, setStyle)
animationControllerImpl->>TimeoutController: setTimeout(onUpdate, 0)
loop active ticks
TimeoutController-->>animationControllerImpl: onUpdate(now)
animationControllerImpl->>RechartsAnimation: tick(now) → timeRemaining
animationControllerImpl->>RechartsAnimation: getState() / getProgress()
animationControllerImpl->>RechartsAnimation: getInterpolated()
animationControllerImpl->>setState: listener(interpolatedValue)
animationControllerImpl->>TimeoutController: setTimeout(onUpdate, timeRemaining)
end
animationControllerImpl->>RechartsAnimation: complete()
Note right of RechartsAnimation: onAnimationEnd called
Component-->>animationControllerImpl: cleanup → cancel()
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Bundle ReportChanges will decrease total bundle size by 47.0kB (-0.84%) ⬇️. This is within the configured threshold ✅ Detailed changes
Affected Assets, Files, and Routes:view changes for bundle: recharts/bundle-es6Assets Changed:
view changes for bundle: recharts/bundle-umdAssets Changed:
view changes for bundle: recharts/bundle-cjsAssets Changed:
view changes for bundle: recharts/bundle-treeshaking-polarAssets Changed:
view changes for bundle: recharts/bundle-treeshaking-cartesianAssets Changed:
view changes for bundle: recharts/bundle-treeshaking-sankeyAssets Changed:
view changes for bundle: recharts/bundle-treeshaking-treemapAssets Changed:
|
|
Tip All tests passed and all changes approved!🟢 UI Tests: 198 tests unchanged |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #7457 +/- ##
==========================================
+ Coverage 88.25% 88.41% +0.15%
==========================================
Files 607 602 -5
Lines 14102 13906 -196
Branches 3545 3514 -31
==========================================
- Hits 12446 12295 -151
+ Misses 1469 1428 -41
+ Partials 187 183 -4 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Staging Deployment Details
These deployments will remain available for 30 days. To update snapshots: Comment |
62758d1 to
99e594f
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (3)
src/animation/AnimationController.ts (1)
4-15: ⚡ Quick winUpdate the controller JSDoc to match the function contract.
This exported type is a function, not a class, and it returns a cancel callback rather than a listener handle. Please remove the stale
AnimationManagerwording too. As per coding guidelines, “Update TypeScript interfaces and JSDoc comments in source code to automatically update API documentation”.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/animation/AnimationController.ts` around lines 4 - 15, Update the JSDoc comments for AnimationController to accurately reflect the function contract. Remove the description referring to AnimationController as a class and update the return value documentation to correctly state that the function returns a cancel callback rather than an animation handle for listening to updates. Remove any stale references to AnimationManager from the comments as it is no longer relevant to this function's responsibility.Source: Coding guidelines
test/animation/AnimationControllerImpl.spec.ts (1)
173-173: ⚡ Quick winUse
expectLastCalledWithhelper for last-call assertions.These assertions use
toHaveBeenLastCalledWith; switch to the repository helper for typed call checks.As per coding guidelines, "Use the
expectLastCalledWithhelper function instead ofexpect(spy).toHaveBeenLastCalledWith(...)for better typing and autocompletion."Also applies to: 180-180, 204-204, 211-211, 250-250, 254-254, 258-258, 262-262
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/animation/AnimationControllerImpl.spec.ts` at line 173, Replace all instances of `expect(spy).toHaveBeenLastCalledWith(...)` with the `expectLastCalledWith` helper function throughout the test file. This includes the assertions at lines 173, 180, 204, 211, 250, 254, 258, and 262. Import the `expectLastCalledWith` helper function at the top of the file if it is not already imported, then refactor each assertion to use the helper function pattern instead of the direct expect syntax to improve typing and autocompletion support.Source: Coding guidelines
test/animation/RechartsAnimation.spec.ts (1)
470-470: ⚡ Quick winUse
expectLastCalledWithhelper instead oftoHaveBeenLastCalledWith.Replace this matcher with the typed helper to align with test conventions and keep assertion typing consistent.
As per coding guidelines, "Use the
expectLastCalledWithhelper function instead ofexpect(spy).toHaveBeenLastCalledWith(...)for better typing and autocompletion."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/animation/RechartsAnimation.spec.ts` at line 470, Replace the matcher call expect(spy).toHaveBeenLastCalledWith(0.2) with the expectLastCalledWith helper function instead. This helper provides better typing and aligns with the project's test conventions for consistency in assertion patterns and improved autocompletion support.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@package.json`:
- Line 143: The `@types/node` version in package.json is set to 25.x.x but the
.nvmrc file specifies Node 24 as the runtime, creating a mismatch where
TypeScript can check for Node 25 APIs that don't exist in the Node 24 runtime.
Either update the `@types/node` dependency in package.json to 24.x.x to match the
Node 24 runtime specified in .nvmrc, or alternatively update .nvmrc to specify
Node 25 to match the types version. Choose whichever aligns with your project's
runtime requirements.
In `@src/animation/AnimationControllerImpl.ts`:
- Around line 18-31: The nextUpdate function reschedules indefinitely after an
animation completes because the non-active branch (the fallback setTimeout on
line 33) always reschedules regardless of whether the handle is already
completed. When animationHandle.getState() is no longer 'active' after
completion, tick() returns 0 and the fallback reschedule creates an infinite
loop. Add a guard condition before the final timeoutController.setTimeout call
(line 33) to check if the animation handle is completed, and only reschedule if
it is not completed. This prevents already-completed or custom-completing
handles from spinning with perpetual zero-timeout reschedules.
In `@src/animation/JavascriptAnimate.tsx`:
- Around line 66-86: The useEffect hook does not reset the animation progress
when starting a new animation cycle, causing stale style values from previous
animations to persist. Before creating the new JavascriptAnimation instance,
reset the style state by calling setStyle with the initial progress value (0) to
ensure a clean slate for each new animation that begins, preventing the jump
from stale progress to the correct value.
In `@src/animation/RechartsAnimation.ts`:
- Around line 15-23: The JSDoc comment for the RechartsAnimation interface's
`init` state inaccurately describes when the `onAnimationStart` callback is
called. The documentation currently states that in the `init` state the
animation "did not announce its start yet", but the constructor actually calls
`onAnimationStart` synchronously. Update the JSDoc documentation for the `init`
state (in the RechartsAnimation interface) to accurately reflect that
`onAnimationStart` is invoked synchronously during construction, ensuring the
state description aligns with the actual implementation behavior.
- Around line 280-282: The getInterpolated method in RechartsAnimation currently
applies the easing function to the interpolated result, but it should apply
easing to the normalized progress value before interpolation. Change the order
so that the easing function is applied to this.getProgress() first to get the
eased progress value, then pass that eased progress to the interpolate function
along with this.getFrom() and this.getTo(). This ensures the easing function
operates on the correct normalized progress range regardless of the actual from
and to numeric values.
- Around line 214-216: In the code block containing the setProgress call, check
if animationDuration is less than or equal to 0 before performing the division
operation. If animationDuration is non-positive, immediately set the progress to
1 (complete state) by calling setProgress(1). Otherwise, proceed with the normal
calculation of timeElapsed divided by animationDuration. This prevents NaN or
negative progress values from being passed to setProgress, which would cause the
animation controller to keep scheduling zero-delay updates instead of
completing.
In `@test/animation/AnimationControllerImpl.spec.ts`:
- Around line 135-136: The second assertion on line 136 duplicates the spy2
check instead of verifying the pre-cancel state. Replace the duplicate
expect(spy2).toHaveBeenCalledTimes(2) on line 136 with
expect(spy1).toHaveBeenCalledTimes(1) to properly assert that spy1 (the callback
for animation1) was called once before the cancellation occurred, ensuring the
test validates both animations advanced before cancellation.
In `@test/animation/JavascriptAnimate.timing.spec.tsx`:
- Around line 66-68: In the test block around the duplicate assertions, replace
the second expect statement that checks
handleAnimationStart.not.toHaveBeenCalled() with an assertion that verifies
handleAnimationEnd is also not called when canBegin is false. This ensures the
test properly validates that both animation callbacks remain unused when the
animation cannot begin.
In `@test/animation/MockProgressAnimationManager.ts`:
- Around line 108-117: The start() method calls mockAnimationController() but
does not capture or store the cancel handle it returns, so the stop() method has
no way to actually cancel the active controller loop. Store the return value of
mockAnimationController() as an instance variable (likely something like
this.cancelAnimation or similar), and then in the stop() method, call that
cancel function before nulling out the animation and isPrimed fields to ensure
scheduled callbacks are properly cleaned up.
In `@test/animation/mockTimeoutController.ts`:
- Around line 39-45: The timeoutLimit check in mockTimeoutController.ts occurs
after the callback is already pushed to the this.timeouts queue, allowing the
queue to exceed the limit before the error is thrown. Reorder the logic to
validate the timeoutLimit condition before pushing the callback to the queue,
ensuring that if the limit would be exceeded, the callback is never added and
the queue remains in a valid state when the error is thrown.
---
Nitpick comments:
In `@src/animation/AnimationController.ts`:
- Around line 4-15: Update the JSDoc comments for AnimationController to
accurately reflect the function contract. Remove the description referring to
AnimationController as a class and update the return value documentation to
correctly state that the function returns a cancel callback rather than an
animation handle for listening to updates. Remove any stale references to
AnimationManager from the comments as it is no longer relevant to this
function's responsibility.
In `@test/animation/AnimationControllerImpl.spec.ts`:
- Line 173: Replace all instances of `expect(spy).toHaveBeenLastCalledWith(...)`
with the `expectLastCalledWith` helper function throughout the test file. This
includes the assertions at lines 173, 180, 204, 211, 250, 254, 258, and 262.
Import the `expectLastCalledWith` helper function at the top of the file if it
is not already imported, then refactor each assertion to use the helper function
pattern instead of the direct expect syntax to improve typing and autocompletion
support.
In `@test/animation/RechartsAnimation.spec.ts`:
- Line 470: Replace the matcher call expect(spy).toHaveBeenLastCalledWith(0.2)
with the expectLastCalledWith helper function instead. This helper provides
better typing and aligns with the project's test conventions for consistency in
assertion patterns and improved autocompletion support.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: d820dda1-f913-4ed1-a05c-a93b5cab2803
📒 Files selected for processing (46)
omnidoc/verifyExamples.spec.tspackage.jsonscripts/snapshots/es6Files.txtscripts/snapshots/libFiles.txtscripts/snapshots/typesFiles.txtsrc/animation/AnimationController.tssrc/animation/AnimationControllerImpl.tssrc/animation/AnimationManager.tssrc/animation/AnimationProgressProvider.tsxsrc/animation/CSSTransitionAnimate.tsxsrc/animation/JavascriptAnimate.tsxsrc/animation/ManualTimeoutController.tssrc/animation/ProgressAnimationManager.tssrc/animation/RechartsAnimation.tssrc/animation/configUpdate.tssrc/animation/createDefaultAnimationManager.tsxsrc/animation/easing.tssrc/animation/timeoutController.tssrc/animation/useAnimationManager.tsxsrc/animation/util.tssrc/cartesian/ErrorBar.tsxsrc/chart/Treemap.tsxsrc/index.tsstryker.config.mjstest/animation/AnimationControllerImpl.spec.tstest/animation/AnimationManager.spec.tstest/animation/CSSTransitionAnimate.timing.spec.tsxtest/animation/CompositeAnimationManager.tstest/animation/JavascriptAnimate.progress.spec.tsxtest/animation/JavascriptAnimate.timing.spec.tsxtest/animation/ManualTimeoutController.spec.tstest/animation/MockAbstractAnimationManager.tstest/animation/MockProgressAnimationManager.tstest/animation/MockTickingAnimationManager.tstest/animation/ProgressAnimationManager.spec.tstest/animation/RechartsAnimation.spec.tstest/animation/configUpdate.spec.tstest/animation/mockAnimationController.tstest/animation/mockTimeoutController.tstest/animation/util.spec.tstest/cartesian/Area.animation.spec.tsxtest/cartesian/Bar.animation.spec.tsxtest/cartesian/Line.animation.spec.tsxtest/component/Label.spec.tsxtest/polar/Pie/Pie.animation.spec.tsxwww/src/docs/apiCates.ts
💤 Files with no reviewable changes (15)
- omnidoc/verifyExamples.spec.ts
- test/animation/MockTickingAnimationManager.ts
- test/animation/configUpdate.spec.ts
- test/animation/ManualTimeoutController.spec.ts
- test/animation/MockAbstractAnimationManager.ts
- src/animation/AnimationProgressProvider.tsx
- src/animation/AnimationManager.ts
- src/animation/createDefaultAnimationManager.tsx
- test/animation/ProgressAnimationManager.spec.ts
- test/animation/AnimationManager.spec.ts
- src/animation/ManualTimeoutController.ts
- src/animation/configUpdate.ts
- src/index.ts
- src/animation/ProgressAnimationManager.ts
- src/animation/util.ts
✅ Files skipped from review due to trivial changes (2)
- src/animation/timeoutController.ts
- scripts/snapshots/typesFiles.txt
|
Staging Deployment Details
These deployments will remain available for 30 days. |
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 10
🧹 Nitpick comments (3)
src/animation/AnimationController.ts (1)
4-15: ⚡ Quick winUpdate the controller JSDoc to match the function contract.
This exported type is a function, not a class, and it returns a cancel callback rather than a listener handle. Please remove the stale
AnimationManagerwording too. As per coding guidelines, “Update TypeScript interfaces and JSDoc comments in source code to automatically update API documentation”.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/animation/AnimationController.ts` around lines 4 - 15, Update the JSDoc comments for AnimationController to accurately reflect the function contract. Remove the description referring to AnimationController as a class and update the return value documentation to correctly state that the function returns a cancel callback rather than an animation handle for listening to updates. Remove any stale references to AnimationManager from the comments as it is no longer relevant to this function's responsibility.Source: Coding guidelines
test/animation/AnimationControllerImpl.spec.ts (1)
173-173: ⚡ Quick winUse
expectLastCalledWithhelper for last-call assertions.These assertions use
toHaveBeenLastCalledWith; switch to the repository helper for typed call checks.As per coding guidelines, "Use the
expectLastCalledWithhelper function instead ofexpect(spy).toHaveBeenLastCalledWith(...)for better typing and autocompletion."Also applies to: 180-180, 204-204, 211-211, 250-250, 254-254, 258-258, 262-262
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/animation/AnimationControllerImpl.spec.ts` at line 173, Replace all instances of `expect(spy).toHaveBeenLastCalledWith(...)` with the `expectLastCalledWith` helper function throughout the test file. This includes the assertions at lines 173, 180, 204, 211, 250, 254, 258, and 262. Import the `expectLastCalledWith` helper function at the top of the file if it is not already imported, then refactor each assertion to use the helper function pattern instead of the direct expect syntax to improve typing and autocompletion support.Source: Coding guidelines
test/animation/RechartsAnimation.spec.ts (1)
470-470: ⚡ Quick winUse
expectLastCalledWithhelper instead oftoHaveBeenLastCalledWith.Replace this matcher with the typed helper to align with test conventions and keep assertion typing consistent.
As per coding guidelines, "Use the
expectLastCalledWithhelper function instead ofexpect(spy).toHaveBeenLastCalledWith(...)for better typing and autocompletion."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/animation/RechartsAnimation.spec.ts` at line 470, Replace the matcher call expect(spy).toHaveBeenLastCalledWith(0.2) with the expectLastCalledWith helper function instead. This helper provides better typing and aligns with the project's test conventions for consistency in assertion patterns and improved autocompletion support.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@package.json`:
- Line 143: The `@types/node` version in package.json is set to 25.x.x but the
.nvmrc file specifies Node 24 as the runtime, creating a mismatch where
TypeScript can check for Node 25 APIs that don't exist in the Node 24 runtime.
Either update the `@types/node` dependency in package.json to 24.x.x to match the
Node 24 runtime specified in .nvmrc, or alternatively update .nvmrc to specify
Node 25 to match the types version. Choose whichever aligns with your project's
runtime requirements.
In `@src/animation/AnimationControllerImpl.ts`:
- Around line 18-31: The nextUpdate function reschedules indefinitely after an
animation completes because the non-active branch (the fallback setTimeout on
line 33) always reschedules regardless of whether the handle is already
completed. When animationHandle.getState() is no longer 'active' after
completion, tick() returns 0 and the fallback reschedule creates an infinite
loop. Add a guard condition before the final timeoutController.setTimeout call
(line 33) to check if the animation handle is completed, and only reschedule if
it is not completed. This prevents already-completed or custom-completing
handles from spinning with perpetual zero-timeout reschedules.
In `@src/animation/JavascriptAnimate.tsx`:
- Around line 66-86: The useEffect hook does not reset the animation progress
when starting a new animation cycle, causing stale style values from previous
animations to persist. Before creating the new JavascriptAnimation instance,
reset the style state by calling setStyle with the initial progress value (0) to
ensure a clean slate for each new animation that begins, preventing the jump
from stale progress to the correct value.
In `@src/animation/RechartsAnimation.ts`:
- Around line 15-23: The JSDoc comment for the RechartsAnimation interface's
`init` state inaccurately describes when the `onAnimationStart` callback is
called. The documentation currently states that in the `init` state the
animation "did not announce its start yet", but the constructor actually calls
`onAnimationStart` synchronously. Update the JSDoc documentation for the `init`
state (in the RechartsAnimation interface) to accurately reflect that
`onAnimationStart` is invoked synchronously during construction, ensuring the
state description aligns with the actual implementation behavior.
- Around line 280-282: The getInterpolated method in RechartsAnimation currently
applies the easing function to the interpolated result, but it should apply
easing to the normalized progress value before interpolation. Change the order
so that the easing function is applied to this.getProgress() first to get the
eased progress value, then pass that eased progress to the interpolate function
along with this.getFrom() and this.getTo(). This ensures the easing function
operates on the correct normalized progress range regardless of the actual from
and to numeric values.
- Around line 214-216: In the code block containing the setProgress call, check
if animationDuration is less than or equal to 0 before performing the division
operation. If animationDuration is non-positive, immediately set the progress to
1 (complete state) by calling setProgress(1). Otherwise, proceed with the normal
calculation of timeElapsed divided by animationDuration. This prevents NaN or
negative progress values from being passed to setProgress, which would cause the
animation controller to keep scheduling zero-delay updates instead of
completing.
In `@test/animation/AnimationControllerImpl.spec.ts`:
- Around line 135-136: The second assertion on line 136 duplicates the spy2
check instead of verifying the pre-cancel state. Replace the duplicate
expect(spy2).toHaveBeenCalledTimes(2) on line 136 with
expect(spy1).toHaveBeenCalledTimes(1) to properly assert that spy1 (the callback
for animation1) was called once before the cancellation occurred, ensuring the
test validates both animations advanced before cancellation.
In `@test/animation/JavascriptAnimate.timing.spec.tsx`:
- Around line 66-68: In the test block around the duplicate assertions, replace
the second expect statement that checks
handleAnimationStart.not.toHaveBeenCalled() with an assertion that verifies
handleAnimationEnd is also not called when canBegin is false. This ensures the
test properly validates that both animation callbacks remain unused when the
animation cannot begin.
In `@test/animation/MockProgressAnimationManager.ts`:
- Around line 108-117: The start() method calls mockAnimationController() but
does not capture or store the cancel handle it returns, so the stop() method has
no way to actually cancel the active controller loop. Store the return value of
mockAnimationController() as an instance variable (likely something like
this.cancelAnimation or similar), and then in the stop() method, call that
cancel function before nulling out the animation and isPrimed fields to ensure
scheduled callbacks are properly cleaned up.
In `@test/animation/mockTimeoutController.ts`:
- Around line 39-45: The timeoutLimit check in mockTimeoutController.ts occurs
after the callback is already pushed to the this.timeouts queue, allowing the
queue to exceed the limit before the error is thrown. Reorder the logic to
validate the timeoutLimit condition before pushing the callback to the queue,
ensuring that if the limit would be exceeded, the callback is never added and
the queue remains in a valid state when the error is thrown.
---
Nitpick comments:
In `@src/animation/AnimationController.ts`:
- Around line 4-15: Update the JSDoc comments for AnimationController to
accurately reflect the function contract. Remove the description referring to
AnimationController as a class and update the return value documentation to
correctly state that the function returns a cancel callback rather than an
animation handle for listening to updates. Remove any stale references to
AnimationManager from the comments as it is no longer relevant to this
function's responsibility.
In `@test/animation/AnimationControllerImpl.spec.ts`:
- Line 173: Replace all instances of `expect(spy).toHaveBeenLastCalledWith(...)`
with the `expectLastCalledWith` helper function throughout the test file. This
includes the assertions at lines 173, 180, 204, 211, 250, 254, 258, and 262.
Import the `expectLastCalledWith` helper function at the top of the file if it
is not already imported, then refactor each assertion to use the helper function
pattern instead of the direct expect syntax to improve typing and autocompletion
support.
In `@test/animation/RechartsAnimation.spec.ts`:
- Line 470: Replace the matcher call expect(spy).toHaveBeenLastCalledWith(0.2)
with the expectLastCalledWith helper function instead. This helper provides
better typing and aligns with the project's test conventions for consistency in
assertion patterns and improved autocompletion support.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: d820dda1-f913-4ed1-a05c-a93b5cab2803
📒 Files selected for processing (46)
omnidoc/verifyExamples.spec.tspackage.jsonscripts/snapshots/es6Files.txtscripts/snapshots/libFiles.txtscripts/snapshots/typesFiles.txtsrc/animation/AnimationController.tssrc/animation/AnimationControllerImpl.tssrc/animation/AnimationManager.tssrc/animation/AnimationProgressProvider.tsxsrc/animation/CSSTransitionAnimate.tsxsrc/animation/JavascriptAnimate.tsxsrc/animation/ManualTimeoutController.tssrc/animation/ProgressAnimationManager.tssrc/animation/RechartsAnimation.tssrc/animation/configUpdate.tssrc/animation/createDefaultAnimationManager.tsxsrc/animation/easing.tssrc/animation/timeoutController.tssrc/animation/useAnimationManager.tsxsrc/animation/util.tssrc/cartesian/ErrorBar.tsxsrc/chart/Treemap.tsxsrc/index.tsstryker.config.mjstest/animation/AnimationControllerImpl.spec.tstest/animation/AnimationManager.spec.tstest/animation/CSSTransitionAnimate.timing.spec.tsxtest/animation/CompositeAnimationManager.tstest/animation/JavascriptAnimate.progress.spec.tsxtest/animation/JavascriptAnimate.timing.spec.tsxtest/animation/ManualTimeoutController.spec.tstest/animation/MockAbstractAnimationManager.tstest/animation/MockProgressAnimationManager.tstest/animation/MockTickingAnimationManager.tstest/animation/ProgressAnimationManager.spec.tstest/animation/RechartsAnimation.spec.tstest/animation/configUpdate.spec.tstest/animation/mockAnimationController.tstest/animation/mockTimeoutController.tstest/animation/util.spec.tstest/cartesian/Area.animation.spec.tsxtest/cartesian/Bar.animation.spec.tsxtest/cartesian/Line.animation.spec.tsxtest/component/Label.spec.tsxtest/polar/Pie/Pie.animation.spec.tsxwww/src/docs/apiCates.ts
💤 Files with no reviewable changes (15)
- omnidoc/verifyExamples.spec.ts
- test/animation/MockTickingAnimationManager.ts
- test/animation/configUpdate.spec.ts
- test/animation/ManualTimeoutController.spec.ts
- test/animation/MockAbstractAnimationManager.ts
- src/animation/AnimationProgressProvider.tsx
- src/animation/AnimationManager.ts
- src/animation/createDefaultAnimationManager.tsx
- test/animation/ProgressAnimationManager.spec.ts
- test/animation/AnimationManager.spec.ts
- src/animation/ManualTimeoutController.ts
- src/animation/configUpdate.ts
- src/index.ts
- src/animation/ProgressAnimationManager.ts
- src/animation/util.ts
✅ Files skipped from review due to trivial changes (2)
- src/animation/timeoutController.ts
- scripts/snapshots/typesFiles.txt
🛑 Comments failed to post (10)
package.json (1)
143-143:
⚠️ Potential issue | 🟡 Minor🧩 Analysis chain
🏁 Script executed:
#!/bin/bash set -euo pipefail printf '.nvmrc: ' cat .nvmrc printf '\n@types/node: ' jq -r '.devDependencies["`@types/node`"] // .dependencies["`@types/node`"] // "not found"' package.jsonRepository: recharts/recharts
Length of output: 93
Align
@types/nodeversion with Node runtime in.nvmrc.Currently
.nvmrcspecifies Node 24, but@types/[email protected]targets Node 25 APIs. This mismatch allows type-checking for Node 25 features that are unavailable in the Node 24 runtime used by dev scripts and tests. Either update.nvmrcto Node 25 or downgrade@types/nodeto24.x.x.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@package.json` at line 143, The `@types/node` version in package.json is set to 25.x.x but the .nvmrc file specifies Node 24 as the runtime, creating a mismatch where TypeScript can check for Node 25 APIs that don't exist in the Node 24 runtime. Either update the `@types/node` dependency in package.json to 24.x.x to match the Node 24 runtime specified in .nvmrc, or alternatively update .nvmrc to specify Node 25 to match the types version. Choose whichever aligns with your project's runtime requirements.Source: Learnings
src/animation/AnimationControllerImpl.ts (1)
18-31:
⚠️ Potential issue | 🟠 Major | ⚡ Quick winStop scheduling once the handle is completed.
A completed handle’s
tick()returns0; the non-active branch then reschedules forever. Guardcompletedbefore the Line 30 fallback so already-completed or custom-completing handles don’t spin.Suggested fix
let cancellable: CancelableTimeout | undefined; const nextUpdate = (now: number) => { const timeRemaining = animationHandle.tick(now); + if (animationHandle.getState() === 'completed') { + cancellable = undefined; + return; + } if (animationHandle.getState() === 'active') { listener(animationHandle.getInterpolated()); if (animationHandle.getProgress() === 1) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/animation/AnimationControllerImpl.ts` around lines 18 - 31, The nextUpdate function reschedules indefinitely after an animation completes because the non-active branch (the fallback setTimeout on line 33) always reschedules regardless of whether the handle is already completed. When animationHandle.getState() is no longer 'active' after completion, tick() returns 0 and the fallback reschedule creates an infinite loop. Add a guard condition before the final timeoutController.setTimeout call (line 33) to check if the animation handle is completed, and only reschedule if it is not completed. This prevents already-completed or custom-completing handles from spinning with perpetual zero-timeout reschedules.src/animation/JavascriptAnimate.tsx (1)
66-86:
⚠️ Potential issue | 🟠 Major | ⚡ Quick winReset progress when starting a new animation.
When this effect restarts after a completed animation,
stylecan still be1. Because the controller does not emit during thebegindelay, children render stale progress until activation, then jump back to0.Suggested fix
if (!isActive || !canBegin || easingFunction == null) { return noop; } + setStyle(from); + const timeoutController = new RequestAnimationFrameTimeoutController(); const animation = new JavascriptAnimation({📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.useEffect(() => { const easingFunction = createEasingFunction(easing); if (!isActive || !canBegin || easingFunction == null) { return noop; } setStyle(from); const timeoutController = new RequestAnimationFrameTimeoutController(); const animation = new JavascriptAnimation({ animationId, easing: easingFunction, animationDuration: duration, animationBegin: begin, onAnimationStart, onAnimationEnd, from, to, }); return animationManager(timeoutController, animation, setStyle);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/animation/JavascriptAnimate.tsx` around lines 66 - 86, The useEffect hook does not reset the animation progress when starting a new animation cycle, causing stale style values from previous animations to persist. Before creating the new JavascriptAnimation instance, reset the style state by calling setStyle with the initial progress value (0) to ensure a clean slate for each new animation that begins, preventing the jump from stale progress to the correct value.src/animation/RechartsAnimation.ts (3)
15-23:
⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAlign the
initstate docs with the synchronous start callback.Line 18 says
inithas not announcedonAnimationStart, but the constructor calls it synchronously on Lines 171-172. Please update the contract before this becomes public API documentation. As per coding guidelines, “Update TypeScript interfaces and JSDoc comments in source code to automatically update API documentation”.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/animation/RechartsAnimation.ts` around lines 15 - 23, The JSDoc comment for the RechartsAnimation interface's `init` state inaccurately describes when the `onAnimationStart` callback is called. The documentation currently states that in the `init` state the animation "did not announce its start yet", but the constructor actually calls `onAnimationStart` synchronously. Update the JSDoc documentation for the `init` state (in the RechartsAnimation interface) to accurately reflect that `onAnimationStart` is invoked synchronously during construction, ensuring the state description aligns with the actual implementation behavior.Source: Coding guidelines
214-216:
⚠️ Potential issue | 🟠 Major | ⚡ Quick winHandle non-positive durations before dividing.
With
animationDuration <= 0, Line 215 can produceNaNor negative progress; the controller then keeps scheduling zero-delay updates instead of completing. Treat zero/negative durations as immediately complete progress.Suggested fix
} const timeElapsed = now - this.animationStartedTime; + if (this.animationDuration <= 0) { + this.setProgress(1); + return 0; + } this.setProgress(timeElapsed / this.animationDuration); return this.nextAnimationUpdate(timeElapsed);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const timeElapsed = now - this.animationStartedTime; if (this.animationDuration <= 0) { this.setProgress(1); return 0; } this.setProgress(timeElapsed / this.animationDuration); return this.nextAnimationUpdate(timeElapsed);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/animation/RechartsAnimation.ts` around lines 214 - 216, In the code block containing the setProgress call, check if animationDuration is less than or equal to 0 before performing the division operation. If animationDuration is non-positive, immediately set the progress to 1 (complete state) by calling setProgress(1). Otherwise, proceed with the normal calculation of timeElapsed divided by animationDuration. This prevents NaN or negative progress values from being passed to setProgress, which would cause the animation controller to keep scheduling zero-delay updates instead of completing.
280-282:
⚠️ Potential issue | 🟠 Major | ⚡ Quick winApply easing to progress before interpolation.
EasingFunctionoperates on normalized progress. The current order only works forfrom=0/to=1; for other numeric ranges it eases the interpolated value itself and returns incorrect values.Suggested fix
public getInterpolated(): number { - return this.easing(interpolate(this.getFrom(), this.getTo(), this.getProgress())); + return interpolate(this.getFrom(), this.getTo(), this.easing(this.getProgress())); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/animation/RechartsAnimation.ts` around lines 280 - 282, The getInterpolated method in RechartsAnimation currently applies the easing function to the interpolated result, but it should apply easing to the normalized progress value before interpolation. Change the order so that the easing function is applied to this.getProgress() first to get the eased progress value, then pass that eased progress to the interpolate function along with this.getFrom() and this.getTo(). This ensures the easing function operates on the correct normalized progress range regardless of the actual from and to numeric values.test/animation/AnimationControllerImpl.spec.ts (1)
135-136:
⚠️ Potential issue | 🟡 Minor | ⚡ Quick winSecond assertion repeats
spy2and missesspy1pre-cancel check.Line 136 duplicates the previous assertion, so this step never verifies that animation1 also advanced before cancellation.
Suggested fix
expect(spy2).toHaveBeenCalledTimes(2); - expect(spy2).toHaveBeenCalledTimes(2); + expect(spy1).toHaveBeenCalledTimes(2);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/animation/AnimationControllerImpl.spec.ts` around lines 135 - 136, The second assertion on line 136 duplicates the spy2 check instead of verifying the pre-cancel state. Replace the duplicate expect(spy2).toHaveBeenCalledTimes(2) on line 136 with expect(spy1).toHaveBeenCalledTimes(1) to properly assert that spy1 (the callback for animation1) was called once before the cancellation occurred, ensuring the test validates both animations advanced before cancellation.test/animation/JavascriptAnimate.timing.spec.tsx (1)
66-68:
⚠️ Potential issue | 🟡 Minor | ⚡ Quick winDuplicate assertion hides missing end-callback check.
handleAnimationStartis asserted twice, so this case never verifies thatonAnimationEndstays uncalled whencanBegin=false.Suggested fix
expect(handleAnimationStart).not.toHaveBeenCalled(); - expect(handleAnimationStart).not.toHaveBeenCalled(); + expect(handleAnimationEnd).not.toHaveBeenCalled();🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/animation/JavascriptAnimate.timing.spec.tsx` around lines 66 - 68, In the test block around the duplicate assertions, replace the second expect statement that checks handleAnimationStart.not.toHaveBeenCalled() with an assertion that verifies handleAnimationEnd is also not called when canBegin is false. This ensures the test properly validates that both animation callbacks remain unused when the animation cannot begin.test/animation/MockProgressAnimationManager.ts (1)
108-117:
⚠️ Potential issue | 🟠 Major | ⚡ Quick win
stop()does not cancel the active controller loop.Line 111 starts a controller but drops the cancel handle, so Line 114 only nulls fields while scheduled callbacks can still run.
Suggested fix
export class MockProgressAnimationManager<T, E> implements MockAnimationManager { private readonly onStop?: () => void; + private cancelController?: () => void; @@ start(animation: RechartsAnimation<T, E>, listener: (newState: T) => void) { + this.cancelController?.(); this.animation = animation; this.isPrimed = false; // Reset the primed state when starting a new animation - mockAnimationController(this.timeoutController, this.animation, listener); + this.cancelController = mockAnimationController(this.timeoutController, this.animation, listener); } stop() { + this.cancelController?.(); + this.cancelController = undefined; this.animation = null; this.isPrimed = false; // Reset the primed state when stopping the queue this.onStop?.(); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/animation/MockProgressAnimationManager.ts` around lines 108 - 117, The start() method calls mockAnimationController() but does not capture or store the cancel handle it returns, so the stop() method has no way to actually cancel the active controller loop. Store the return value of mockAnimationController() as an instance variable (likely something like this.cancelAnimation or similar), and then in the stop() method, call that cancel function before nulling out the animation and isPrimed fields to ensure scheduled callbacks are properly cleaned up.test/animation/mockTimeoutController.ts (1)
39-45:
⚠️ Potential issue | 🟠 Major | ⚡ Quick winCheck
timeoutLimitbefore mutating the queue.Line 39 pushes the timeout before Line 41 enforces the limit, so a rejected
setTimeoutstill leaves a queued callback behind.Suggested fix
- this.timeouts.push({ callback: copy, delay }); - - if (this.config.timeoutLimit != null && this.timeouts.length > this.config.timeoutLimit) { + if (this.config.timeoutLimit != null && this.timeouts.length >= this.config.timeoutLimit) { throw new Error( - `Timeout queue has size ${this.timeouts.length} which is more than the allowed ${this.config.timeoutLimit}`, + `Timeout queue has size ${this.timeouts.length + 1} which is more than the allowed ${this.config.timeoutLimit}`, ); } + this.timeouts.push({ callback: copy, delay });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/animation/mockTimeoutController.ts` around lines 39 - 45, The timeoutLimit check in mockTimeoutController.ts occurs after the callback is already pushed to the this.timeouts queue, allowing the queue to exceed the limit before the error is thrown. Reorder the logic to validate the timeoutLimit condition before pushing the callback to the queue, ensuring that if the limit would be exceeded, the callback is never added and the queue remains in a valid state when the error is thrown.
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
Staging Deployment Details
These deployments will remain available for 30 days. |
|
Staging Deployment Details
These deployments will remain available for 30 days. |
## Description So I tried to reproduce the Pie + Legend animation problem and turns out that I accidentally fixed it in #7457. So here is at least a test to prevent regressions. I confirmed I can reproduce in browser and in the unit test before that commit, and cannot reproduce after the commit. ## Related Issue Closes #7254 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Enhanced test infrastructure for pie chart animations with new helper functions for validating sector paths and angles during animation. * Added comprehensive test coverage for pie chart animations when a legend is present, including animation lifecycle callbacks. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
So I wasn't happy with how the animation code is structured so I rewrote it from scratch. This didn't matter before but in 3.9 this will be a public API so let's get it nice and separated.
This is a refactor so the aim is to have 100% identical behavior to what it was before, just with new code.
Before:
After:
Not quite yet 100% settled, please share feedback:
Notable changes:
onAnimationStartasynchronously which is different from the production code which is calling it synchronously. I fixed the inconsistency which in turn made a few tests to fail so that's why you see changes in test code.Summary by CodeRabbit
AnimationProgressProvideranduseAnimationProgressfrom the public API.complete()finalization and improved progress interpolation behavior.AnimationController, updating animation lifecycle and easing wiring across relevant components.lgtmscript to run lint, build, tests, and type checks together.