Skip to content

fix(BarChart): render stacked bars when all values are 0 (#6235)#7199

Merged
ckifer merged 1 commit into
recharts:mainfrom
andypoorman:6235
Apr 3, 2026
Merged

fix(BarChart): render stacked bars when all values are 0 (#6235)#7199
ckifer merged 1 commit into
recharts:mainfrom
andypoorman:6235

Conversation

@andypoorman

@andypoorman andypoorman commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

Description

A [0, 0] domain was being discarded as "no data" but it meant two different things: no stacks at all, or stacks that are genuinely all zeros. We now detect the "no stacks" case earlier so all-zero stacked data isn't thrown away with it.

Related Issue

#6235

Motivation and Context

How Has This Been Tested?

  • 5 new tests across 2 files:
    • test/chart/BarChart.spec.tsx: Bar component renders with all-zero stacked data, YAxis ticks still appear, custom shape callbacks fire, vertical layout works
    • test/util/ChartUtils.spec.tsx: getDomainOfStackGroups returns undefined for empty groups, returns [0, 0] for real all-zero stacked data
    • Tested story board with with this change (which I didn't commit but just used to to make sure I could repro and fix):
    storybook/stories/Examples/BarChart/BarChart.stories.tsx
    
    

92 ...getStoryArgsFromArgsTypesObject(BarChartArgs),
93 width: 500,
94 height: 300,
95 - data: pageData,
95 + data: pageData.map(d => ({ ...d, uv: 0, pv: 0 })),
96 margin: {
97 top: 20,
98 right: 30,

```

Screenshots (if appropriate):

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:

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

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved stacked bar chart rendering when all numeric values are zero, ensuring bars and axis ticks display correctly.
  • Tests

    • Added test coverage for stacked bar chart edge cases with zero values, including custom shape callbacks and vertical layouts.

@coderabbitai

coderabbitai Bot commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

This PR modifies domain calculation logic for stacked bar charts to properly handle cases where all numeric values are zero. It updates selector and utility functions to change how they treat empty and zero-only stack groups, and adds corresponding test coverage.

Changes

Cohort / File(s) Summary
Domain Calculation Logic
src/state/selectors/axisSelectors.ts, src/util/ChartUtils.ts
Modified combineDomainOfStackGroups to always return getDomainOfStackGroups results (removing prior conditional for [0, 0] domains) and updated getDomainOfStackGroups to return undefined when stackGroups is empty, not just null/undefined.
Test Coverage
test/chart/BarChart.spec.tsx, test/util/ChartUtils.spec.tsx
Added six new test cases: three for stacked bar rendering with all-zero values (verifying component mounting, custom shape callbacks, and vertical layout behavior), and two for getDomainOfStackGroups with empty and zero-only stack data.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Suggested reviewers

  • PavelVanecek
  • ckifer
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main fix: addressing stacked bar rendering when all values are 0, with direct reference to the related issue (#6235).
Description check ✅ Passed The PR description comprehensively covers all required sections: clear description of the problem and fix, related issue link, motivation/context, detailed testing approach with 5 new tests across 2 files, and proper checklist completion (bug fix marked, tests added, documentation marked n/a).
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/chart/BarChart.spec.tsx`:
- Around line 1383-1390: The tests render BarChart components directly and can
leave timer-driven updates pending; after each render call (e.g., the
render(...) invocation that renders <BarChart ...> in BarChart.spec.tsx and the
similar render blocks referenced around the other ranges), add a call to
vi.runOnlyPendingTimers() immediately after the render to advance pending timers
(do not use vi.runAllTimers()). Ensure this is applied to the render locations
corresponding to the first snippet and the other two test blocks mentioned so
assertions won't race with pending timer callbacks.
🪄 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: 60d8f360-4416-4395-bb2a-3acbf082a982

📥 Commits

Reviewing files that changed from the base of the PR and between f3ae755 and bda2cdb.

📒 Files selected for processing (4)
  • src/state/selectors/axisSelectors.ts
  • src/util/ChartUtils.ts
  • test/chart/BarChart.spec.tsx
  • test/util/ChartUtils.spec.tsx

Comment on lines +1383 to +1390
const { container } = render(
<BarChart width={400} height={300} data={allZeroData}>
<YAxis />
<Bar dataKey="uv" stackId="test" fill="#ff7300" isAnimationActive={false} />
<Bar dataKey="pv" stackId="test" fill="#387908" isAnimationActive={false} />
</BarChart>,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Flush pending timers after render in these added tests.

These renders are outside createSelectorTestCase, so timer-driven updates can race assertions unless pending timers are advanced.

Proposed patch
@@
       const { container } = render(
         <BarChart width={400} height={300} data={allZeroData}>
           <YAxis />
           <Bar dataKey="uv" stackId="test" fill="#ff7300" isAnimationActive={false} />
           <Bar dataKey="pv" stackId="test" fill="#387908" isAnimationActive={false} />
         </BarChart>,
       );
+      act(() => {
+        vi.runOnlyPendingTimers();
+      });
@@
       const { container } = render(
         <BarChart width={400} height={300} data={allZeroData}>
           <YAxis />
           <Bar dataKey="uv" stackId="test" shape={customShape} isAnimationActive={false} />
         </BarChart>,
       );
+      act(() => {
+        vi.runOnlyPendingTimers();
+      });
@@
       const { container } = render(
         <BarChart width={400} height={300} data={allZeroData} layout="vertical">
           <XAxis type="number" />
           <YAxis dataKey="name" type="category" />
           <Bar dataKey="uv" stackId="test" shape={customShape} isAnimationActive={false} />
         </BarChart>,
       );
+      act(() => {
+        vi.runOnlyPendingTimers();
+      });

As per coding guidelines "Call vi.runOnlyPendingTimers() to advance timers after renders when not using createSelectorTestCase helper, and avoid vi.runAllTimers() to prevent infinite loops".

Also applies to: 1406-1412, 1424-1431

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/chart/BarChart.spec.tsx` around lines 1383 - 1390, The tests render
BarChart components directly and can leave timer-driven updates pending; after
each render call (e.g., the render(...) invocation that renders <BarChart ...>
in BarChart.spec.tsx and the similar render blocks referenced around the other
ranges), add a call to vi.runOnlyPendingTimers() immediately after the render to
advance pending timers (do not use vi.runAllTimers()). Ensure this is applied to
the render locations corresponding to the first snippet and the other two test
blocks mentioned so assertions won't race with pending timer callbacks.

@codecov

codecov Bot commented Apr 2, 2026

Copy link
Copy Markdown

Bundle Report

Changes will decrease total bundle size by 554 bytes (-0.01%) ⬇️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
recharts/bundle-cjs 1.3MB -135 bytes (-0.01%) ⬇️
recharts/bundle-es6 1.13MB -135 bytes (-0.01%) ⬇️
recharts/bundle-umd 547.89kB -14 bytes (-0.0%) ⬇️
recharts/bundle-treeshaking-cartesian 643.98kB -135 bytes (-0.02%) ⬇️
recharts/bundle-treeshaking-polar 448.36kB -135 bytes (-0.03%) ⬇️

Affected Assets, Files, and Routes:

view changes for bundle: recharts/bundle-cjs

Assets Changed:

Asset Name Size Change Total Size Change (%)
state/selectors/axisSelectors.js -176 bytes 69.09kB -0.25%
util/ChartUtils.js 41 bytes 20.03kB 0.21%
view changes for bundle: recharts/bundle-treeshaking-cartesian

Assets Changed:

Asset Name Size Change Total Size Change (%)
bundle.js -135 bytes 643.98kB -0.02%
view changes for bundle: recharts/bundle-treeshaking-polar

Assets Changed:

Asset Name Size Change Total Size Change (%)
bundle.js -135 bytes 448.36kB -0.03%
view changes for bundle: recharts/bundle-es6

Assets Changed:

Asset Name Size Change Total Size Change (%)
state/selectors/axisSelectors.js -176 bytes 59.07kB -0.3%
util/ChartUtils.js 41 bytes 18.02kB 0.23%
view changes for bundle: recharts/bundle-umd

Assets Changed:

Asset Name Size Change Total Size Change (%)
Recharts.js -14 bytes 547.89kB -0.0%

@codecov

codecov Bot commented Apr 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.07%. Comparing base (f3ae755) to head (bda2cdb).
⚠️ Report is 7 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #7199   +/-   ##
=======================================
  Coverage   89.06%   89.07%           
=======================================
  Files         539      539           
  Lines       41011    41007    -4     
  Branches     5553     5553           
=======================================
- Hits        36527    36525    -2     
+ Misses       4476     4474    -2     
  Partials        8        8           

☔ 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.

@PavelVanecek

Copy link
Copy Markdown
Collaborator

Just recently we merged an optimization where we don't render Bars with zero height - isn't this a regression?

@PavelVanecek

Copy link
Copy Markdown
Collaborator

#6800

@andypoorman

Copy link
Copy Markdown
Contributor Author

Yeah here's the before/after of my fix:

before:
before

after:
after

This fix technically does not regress #6800. That PR added the zero-dimension filter at computeBarRectangles line 1116-1124, which is still intact. This change is upstream it ensures the axis gets a domain so computeBarRectangles even runs. Zero-height bars without custom shapes are still filtered. The first test explicitly asserts .recharts-bar-rectangle count is 0.

IMO the two cases don't overlap:

  • Perf: filter zero-dimension rectangles early #6800's scenario: Mixed data with some zeros and some non-zeros (sparse). The domain is already valid (e.g., [0, 5000]). The optimization filters individual zero-height bars within that.
  • Our scenario: ALL values are 0. The domain was [0, 0] nothing render entirely

So no, our fix doesn't bring back the perf problem from #6800. Those sparse charts still have a non-zero domain and the zero-dimension filter still fires for their empty bars.

@ckifer called it an "accidental breaking change" though in #6235, and it worked in v2, so I was assuming that was desired behavior so arguably this is fixing that regression

But about whether the bar should show up or not, I think one person's feature is another person's bug lol. @PavelVanecek I'll defer to you one whether we should:

@ckifer

ckifer commented Apr 2, 2026

Copy link
Copy Markdown
Member

Yeah I think this is fine based on the above

@ckifer
ckifer merged commit 12e725e into recharts:main Apr 3, 2026
52 checks passed
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.

3 participants