Skip to content

Fix stacked Bar animation#6593

Merged
ckifer merged 1 commit intomainfrom
stackedbaranimation
Nov 10, 2025
Merged

Fix stacked Bar animation#6593
ckifer merged 1 commit intomainfrom
stackedbaranimation

Conversation

@PavelVanecek
Copy link
Collaborator

@PavelVanecek PavelVanecek commented Nov 10, 2025

Description

Fix initial animation of stacked Bars.

At first I have considered rendering them at full height which would also solve this problem but that turned out to be much larger change than just animating the x/y position too.

Related Issue

Fixes #523

Screenshots (if appropriate):

Before

Screen.Recording.2025-11-10.at.11.16.10.mov
Screen.Recording.2025-11-10.at.11.17.00.mov

After

Screen.Recording.2025-11-10.at.11.12.20.mov
Screen.Recording.2025-11-10.at.11.17.23.mov

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have added tests to cover my changes.
  • I have added a storybook story or VR test, or extended an existing story or VR test to show my changes

Summary by CodeRabbit

  • New Features

    • Added stackedBarStart property to bar chart data structures, exposing the base coordinate for stacked bars.
    • Enhanced animation interpolation for stacked bars in both horizontal and vertical layouts, now properly positioning bar elements during transitions.
  • Tests

    • Updated test expectations to verify the new stackedBarStart property is correctly included in bar chart payloads and event handlers across various stacking scenarios.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 10, 2025

Walkthrough

A new stackedBarStart property is added to the BarRectangleItem interface to track the chart coordinate of the base value for stacked bars. Bar animation interpolation logic is updated to use this property during rendering for both horizontal and vertical layouts. Test expectations across multiple test files are updated to include this new property in bar payloads.

Changes

Cohort / File(s) Summary
Production implementation
src/cartesian/Bar.tsx
Added stackedBarStart: number field to BarRectangleItem interface. Updated bar animation interpolation to compute height via entry.height and adjust y/x coordinates using entry.stackedBarStart for horizontal and vertical layouts respectively. In computeBarRectangles, precomputed stackedBarStart as numericAxis.scale(baseValue) and propagated to each BarRectangleItem.
Test expectations
test/cartesian/Bar.spec.tsx, test/cartesian/Bar.truncateByDomain.spec.tsx, test/chart/BarChart.spec.tsx
Updated test fixtures and assertions to expect stackedBarStart property in bar payloads and event handler data. Values added include 195, 275, 45, and 165 depending on test scenario context.

Sequence Diagram

sequenceDiagram
    participant Render as Bar Render
    participant ComputeBar as computeBarRectangles
    participant Scale as numericAxis.scale
    participant Interp as Bar Animation<br/>(interpolation)
    participant Output as Animated Bar

    Render->>ComputeBar: Calculate bar rectangles<br/>with baseValue
    ComputeBar->>Scale: scale(baseValue)
    Scale-->>ComputeBar: stackedBarStart coordinate
    ComputeBar->>ComputeBar: Assign stackedBarStart<br/>to each BarRectangleItem
    ComputeBar-->>Render: BarRectangleItem with<br/>stackedBarStart
    Render->>Interp: Interpolate entry.height,<br/>entry.stackedBarStart
    rect rgba(76, 175, 80, 0.1)
    note over Interp: New: y/x adjusted via<br/>stackedBarStart interpolation
    end
    Interp-->>Output: Coordinated stack<br/>animation
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Animation interpolation logic: The core changes in Bar.tsx involve bar animation coordinate computation for both horizontal and vertical layouts; requires careful verification of interpolation correctness
  • Propagation of stackedBarStart: Ensure stackedBarStart is correctly precomputed via numericAxis.scale(baseValue) and consistently referenced across rendering and animation phases
  • Test coverage span: While test updates are repetitive (adding the same property across multiple test files), the breadth of test files affected (3 test suites) warrants verification that all scenarios properly capture the new field

Suggested labels

enhancement

Suggested reviewers

  • ckifer

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix stacked Bar animation' is concise and directly describes the main change—fixing the animation behavior of stacked bars in the chart library.
Description check ✅ Passed The PR description covers most required template sections: clear description, related issue link, motivation, screenshots, type of change marked, and tests added. Minor checklist items unchecked but not critical.
Linked Issues check ✅ Passed The PR implements the core fix for issue #523 by ensuring stacked bar segments animate cohesively from their stacked position rather than independently, addressing the broken animation appearance.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing stacked bar animation: introducing stackedBarStart field for animation interpolation, updating bar rendering and animation logic, and extending tests accordingly.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch stackedbaranimation

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.

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 96da90b and 63ae3ba.

📒 Files selected for processing (4)
  • src/cartesian/Bar.tsx (4 hunks)
  • test/cartesian/Bar.spec.tsx (5 hunks)
  • test/cartesian/Bar.truncateByDomain.spec.tsx (2 hunks)
  • test/chart/BarChart.spec.tsx (7 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not hardcode any user-facing strings or formatting choices in the library code; leave internationalization to library users

Files:

  • src/cartesian/Bar.tsx
🧠 Learnings (1)
📚 Learning: 2025-10-25T07:35:46.188Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: test/README.md:0-0
Timestamp: 2025-10-25T07:35:46.188Z
Learning: Applies to test/**/*.test.{ts,tsx} : Mock getBoundingClientRect using test/helper/MockGetBoundingClientRect.ts when rendering Recharts components (e.g., Tooltip, Legend, charts)

Applied to files:

  • test/cartesian/Bar.spec.tsx
  • test/cartesian/Bar.truncateByDomain.spec.tsx
  • test/chart/BarChart.spec.tsx
🧬 Code graph analysis (1)
src/cartesian/Bar.tsx (1)
src/util/DataUtils.ts (1)
  • interpolate (105-110)
⏰ 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, Test, Pack
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (10)
src/cartesian/Bar.tsx (4)

569-575: Animation logic correctly implements the fix for stacked bars.

For horizontal bars, the animation now:

  • Interpolates y from stackedBarStart (base position) to entry.y (final position)
  • Interpolates height from 0 to entry.height

This makes stacked bar segments appear to grow from a common baseline rather than all growing simultaneously from their individual positions, addressing the issue described in #523.


580-582: Vertical layout animation mirrors horizontal logic correctly.

The vertical animation interpolates x from stackedBarStart to entry.x while width grows from 0 to entry.width, maintaining consistency with the horizontal layout approach.


839-839: stackedBarStart properly propagated to all bar items.

The computed stackedBarStart is included in every BarRectangleItem, ensuring it's available for animation, tooltips, and event handlers.


106-109: No public API breaking change — BarRectangleItem is constructed only internally.

Search results confirm that BarRectangleItem is never manually constructed by external code. The type is only used in type annotations for callback parameters and return types. The only place where BarRectangleItem objects are created is inside computeBarRectangles() (lines 837–850), where stackedBarStart is already properly assigned. This change is safe and poses no burden on library consumers.

test/cartesian/Bar.truncateByDomain.spec.tsx (1)

108-108: Test expectations correctly updated for new stackedBarStart field.

The expected stackedBarStart: 275 value is added to both bar data objects in the stacked bar test. This value represents the chart coordinate of the baseValue for this specific test configuration.

Also applies to: 144-144

test/chart/BarChart.spec.tsx (3)

796-796: Test expectations correctly include stackedBarStart for stacked bars.

The tests now expect stackedBarStart: 45 for stacked bar scenarios. All bars in the same stack share the same stackedBarStart value, which is correct - they all animate from the common base of the stack.

Also applies to: 829-829, 862-862, 895-895


856-856: Generic number expectation appropriate for background props test.

Using expect.any(Number) is appropriate here since the background component test focuses on prop passing behavior rather than specific coordinate values.


2471-2471: Boxplot/whisker test expectations updated consistently.

The stackedBarStart: 165 value is correctly added to all three expected bar items in the boxplot simulation test, reflecting the base coordinate for this multi-bar stacked scenario.

Also applies to: 2511-2511, 2551-2551

test/cartesian/Bar.spec.tsx (2)

856-856: Background component receives stackedBarStart in props.

The test correctly expects stackedBarStart: expect.any(Number) to be passed to custom background components, ensuring consistency across all bar-related components.


1788-1788: Event handlers receive stackedBarStart in bar data payload.

All bar event handlers (onClick, onMouseOver, onMouseOut, onTouchMove) now include stackedBarStart: 195 in their payload. This confirms the field is part of the public API exposed to users, who may rely on it for custom animations or visual effects.

Also applies to: 1842-1842, 1883-1883, 1936-1936

// @ts-expect-error this assumes that the domain is always numeric, but doesn't check for it
const stackedDomain: ReadonlyArray<number> = stackedData ? numericAxis.scale.domain() : null;
const baseValue = getBaseValueOfBar({ numericAxis });
const stackedBarStart: number = numericAxis.scale(baseValue);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify scale function never returns null or undefined for baseValue.

The numericAxis.scale(baseValue) result is assigned to a number-typed variable without null checking. While the interpolate function handles non-number start values gracefully (returning the end value), this could silently skip the animation improvement if the scale returns null/undefined.


🏁 Script executed:

#!/bin/bash
# Check if scale functions are expected to return nullable values
rg -n -B5 -A5 'numericAxis\.scale' --type=ts --type=tsx | head -50

Length of output: 27



---

🏁 Script executed:

```shell
#!/bin/bash
# Search for numericAxis.scale usage with correct ripgrep syntax
rg -n -B5 -A5 'numericAxis\.scale' --type=typescript

Length of output: 34


🏁 Script executed:

#!/bin/bash
# View the Bar.tsx file around line 771 for context
fd 'Bar\.tsx' --type f -x cat {} | sed -n '765,780p'

Length of output: 820


🏁 Script executed:

#!/bin/bash
# Search for scale method/function definitions to understand return type
rg -n 'scale\s*\(' --type=typescript -A3 | grep -E '(scale|return|:)' | head -40

Length of output: 34


🏁 Script executed:

#!/bin/bash
# Look for Scale type or interface definitions
rg -n -B2 -A5 'interface.*Scale|type.*Scale|\.scale\s*:' --type=typescript | head -60

Length of output: 34



---

🏁 Script executed:

```shell
#!/bin/bash
# Search for numericAxis.scale without type filtering
rg -n 'numericAxis\.scale' | head -30

Length of output: 631


🏁 Script executed:

#!/bin/bash
# Find the function containing line 771 - search backwards for function start
fd 'Bar\.tsx' --type f -x cat {} | sed -n '730,775p'

Length of output: 2106


🏁 Script executed:

#!/bin/bash
# Search for scale method in axis-related files
rg -n '\.scale\s*[:=].*function|scale.*:.*function' -A5 | head -60

Length of output: 571


🏁 Script executed:

#!/bin/bash
# Look for d3-scale or scale-related type imports
rg -n 'import.*scale|from.*scale|Scale' src/cartesian/Bar.tsx

Length of output: 569



---

🏁 Script executed:

```shell
#!/bin/bash
# Find BaseAxisWithScale type definition
rg -n -B3 -A10 'type BaseAxisWithScale|interface BaseAxisWithScale' 

Length of output: 971


🏁 Script executed:

#!/bin/bash
# Look at axisSelectors to understand the scale property type
rg -n -B5 -A15 'BaseAxisWithScale' src/state/selectors/axisSelectors.ts | head -80

Length of output: 803


🏁 Script executed:

#!/bin/bash
# Check the actual usage patterns around line 791 and 800 to see how scale results are handled
fd 'Bar\.tsx' --type f -x cat {} | sed -n '785,825p'

Length of output: 1587



---

🏁 Script executed:

```shell
#!/bin/bash
# Find RechartsScale type definition
rg -n -B2 -A8 'type RechartsScale|interface RechartsScale' src/state/selectors/axisSelectors.ts

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Search for the interpolate function mentioned in the review comment
rg -n -B3 -A10 'function interpolate|const interpolate' src/cartesian/Bar.tsx

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Look at context around line 769-771 to see the full scope of baseValue handling
fd 'Bar\.tsx' --type f -x cat {} | sed -n '760,805p'

Length of output: 1125



---

🏁 Script executed:

```shell
#!/bin/bash
# Search for RechartsScale type across all files
rg -n 'type RechartsScale|interface RechartsScale' 

Length of output: 59


🏁 Script executed:

#!/bin/bash
# Get the exact line 771 from Bar.tsx with surrounding context
grep -n 'const stackedBarStart: number = numericAxis.scale' src/cartesian/Bar.tsx -B10 -A10

Length of output: 1103


🏁 Script executed:

#!/bin/bash
# Search for RechartsScale to understand the scale function's return type
rg -n 'RechartsScale' src/state/selectors/axisSelectors.ts | head -20

Length of output: 543


🏁 Script executed:

#!/bin/bash
# Search for the interpolate function mentioned in the review comment
rg -n 'interpolate' src/cartesian/Bar.tsx -B2 -A5

Length of output: 1334



---

🏁 Script executed:

```shell
#!/bin/bash
# Get the full RechartsScale interface definition
sed -n '152,200p' src/util/ChartUtils.ts

Length of output: 1433


🏁 Script executed:

#!/bin/bash
# Check the interpolate function signature to see how it handles non-number values
rg -n -B2 -A8 'function interpolate|export.*interpolate' src/util/DataUtils.ts

Length of output: 555


Add null checking for stackedBarStart assignment at line 771, or verify that baseValue always produces a defined scale result.

The code assigns numericAxis.scale(baseValue) to a number-typed variable without null checking. However, evidence from the codebase shows scale() can return undefined:

  • Line 800 uses nullish coalescing: currentValueScale ?? baseValueScale ?? undefined
  • The RechartsScale interface's return type signature appears incomplete

If scale returns undefined, stackedBarStart becomes undefined. When passed to interpolate() as the start parameter (lines 570, 580), this causes it to silently return the end value, potentially skipping the animation improvement.

Either: (1) add null checking similar to line 800, or (2) verify and document why baseValue guarantees a defined scale result.

🤖 Prompt for AI Agents
In src/cartesian/Bar.tsx around line 771, the assignment const stackedBarStart:
number = numericAxis.scale(baseValue); assumes scale() returns a number but
scale can be undefined; change this to guard against undefined like other places
(e.g., use nullish coalescing or a fallback) or assert/ensure baseValue yields a
defined scale. Specifically, replace the direct assignment with a nullable typed
value or use numericAxis.scale(baseValue) ?? fallbackValue (or propagate
undefined and handle it before calling interpolate), and update the variable
type accordingly so subsequent calls to interpolate() receive a defined start or
are skipped/handled when undefined.

@codecov
Copy link

codecov bot commented Nov 10, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.57%. Comparing base (96da90b) to head (63ae3ba).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #6593   +/-   ##
=======================================
  Coverage   94.57%   94.57%           
=======================================
  Files         491      491           
  Lines       40687    40691    +4     
  Branches     4768     4768           
=======================================
+ Hits        38481    38485    +4     
  Misses       2201     2201           
  Partials        5        5           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@codecov
Copy link

codecov bot commented Nov 10, 2025

Bundle Report

Changes will increase total bundle size by 486 bytes (0.02%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
recharts/bundle-cjs 1.12MB 220 bytes (0.02%) ⬆️
recharts/bundle-es6 966.37kB 188 bytes (0.02%) ⬆️
recharts/bundle-umd 509.26kB 78 bytes (0.02%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: recharts/bundle-cjs

Assets Changed:

Asset Name Size Change Total Size Change (%)
cartesian/Bar.js 220 bytes 26.62kB 0.83%
view changes for bundle: recharts/bundle-es6

Assets Changed:

Asset Name Size Change Total Size Change (%)
cartesian/Bar.js 188 bytes 24.98kB 0.76%
view changes for bundle: recharts/bundle-umd

Assets Changed:

Asset Name Size Change Total Size Change (%)
Recharts.js 78 bytes 509.26kB 0.02%

Copy link
Member

@ckifer ckifer left a comment

Choose a reason for hiding this comment

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

✨ 🚀

@ckifer ckifer merged commit 59d5d38 into main Nov 10, 2025
29 checks passed
@ckifer ckifer deleted the stackedbaranimation branch November 10, 2025 06:16
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.

The loading animation of stacked bar chart feels broken

2 participants