Fix missing BoxPlot markers: preserve zero-height bars with custom shape prop#7067
Conversation
… custom shapes Co-authored-by: PavelVanecek <1100170+PavelVanecek@users.noreply.github.com>
… custom shapes Co-authored-by: PavelVanecek <1100170+PavelVanecek@users.noreply.github.com>
…ecks Co-authored-by: PavelVanecek <1100170+PavelVanecek@users.noreply.github.com>
shape prop
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
WalkthroughThis PR introduces a Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 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 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 |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
test/cartesian/Bar.truncateByDomain.spec.tsx (1)
155-205: UseexpectLastCalledWithin this modified selector assertion block.This block was touched and still uses
toHaveBeenLastCalledWith, which diverges from the test helper convention used elsewhere.As per coding guidelines: Use the `expectLastCalledWith` helper function instead of `expect(spy).toHaveBeenLastCalledWith(...)` for better typing and autocompletion.Suggested update
import { describe, expect, it, test } from 'vitest'; import { createSelectorTestCase } from '../helper/createSelectorTestCase'; +import { expectLastCalledWith } from '../helper/expectLastCalledWith'; @@ - expect(spy).toHaveBeenLastCalledWith({ + expectLastCalledWith(spy, { date: { graphicalItems: [ @@ - }); + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/cartesian/Bar.truncateByDomain.spec.tsx` around lines 155 - 205, The test 'selectStackGroups' currently calls expect(spy).toHaveBeenLastCalledWith(...) — update this assertion to use the project test-helper expectLastCalledWith helper instead; locate the test block that invokes selectStackGroups and replace the expect(spy).toHaveBeenLastCalledWith call with a call to expectLastCalledWith(spy, <same expected object>) so the assertion uses the helper while keeping the same expected payload (references: test name 'selectStackGroups', selector function 'selectStackGroups', and the local 'spy' variable).src/cartesian/Bar.tsx (1)
1116-1122: Narrow custom-shape detection to actual custom renderers.Line 1185 marks any non-null
shapeas custom, which can disable zero-dimension filtering more broadly than necessary. Consider flagging only function/element shapes so optimization stays active for default-rectangle variants.Proposed refinement
function BarFn(outsideProps: Props) { const props = resolveDefaultProps(outsideProps, defaultBarProps); + const hasCustomShape = React.isValidElement(props.shape) || typeof props.shape === 'function'; // stackId may arrive from props or from BarStack context const stackId = useStackId(props.stackId); const isPanorama = useIsPanorama(); @@ - hasCustomShape={props.shape != null} + hasCustomShape={hasCustomShape} />Also applies to: 1185-1185
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/cartesian/Bar.tsx` around lines 1116 - 1122, The guard that skips zero-dimension filtering uses hasCustomShape derived from a non-null check on shape; change the detection in the Bar component so hasCustomShape is set only when shape is an actual custom renderer (e.g., typeof shape === 'function' or React.isValidElement(shape) / React.isValidElement-like check) instead of any non-null value, so the early-return condition in the null/zero check (the if block referencing x, y, width, height and hasCustomShape) continues to filter out width===0/height===0 for default rectangle shapes but allows true custom renderers to bypass it.test/cartesian/Bar.zeroDimensionFiltering.spec.tsx (1)
272-307: Add a DOM assertion for rendered marker lines, not only selector output.Great selector-level check. I’d also assert the custom shape is actually rendered to guard the visual regression end-to-end.
Suggested extension
it('should not filter out zero-height bars that have a custom shape (BoxPlot use case)', () => { const HorizonBar = (props: { x?: number; y?: number; width?: number; height?: number }) => { const { x, y, width } = props; if (x == null || y == null || width == null) { return null; } - return <line x1={x} y1={y} x2={x + width} y2={y} stroke="#000" strokeWidth={3} />; + return <line className="horizon-marker" x1={x} y1={y} x2={x + width} y2={y} stroke="#000" strokeWidth={3} />; }; @@ - const { spy } = renderTestCase(state => selectBarRectangles(state, 'bar-marker', false, undefined)); + const { container, spy } = renderTestCase(state => selectBarRectangles(state, 'bar-marker', false, undefined)); const rectangles = spy.mock.lastCall?.[0] ?? []; @@ rectangles.forEach((rect: { height: number }) => { expect(rect.height).toBe(0); }); + expect(container.querySelectorAll('.horizon-marker')).toHaveLength(data.length); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/cartesian/Bar.zeroDimensionFiltering.spec.tsx` around lines 272 - 307, The test currently only asserts selector output (selectBarRectangles) for the custom HorizonBar but doesn't verify the DOM rendering; update the spec to also query the rendered output from renderTestCase (the BarChart with Bar id "bar-marker" and shape HorizonBar) and assert that the expected <line> elements for each data row are present and have the expected attributes (e.g., y/x/width or stroke/strokeWidth) so the custom shape is actually rendered end-to-end; use the same renderTestCase helper to get the rendered container and querySelectorAll('line') (or a scoped selector) and assert its length equals data.length and attribute values match the marker positions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/cartesian/Bar.tsx`:
- Around line 1116-1122: The guard that skips zero-dimension filtering uses
hasCustomShape derived from a non-null check on shape; change the detection in
the Bar component so hasCustomShape is set only when shape is an actual custom
renderer (e.g., typeof shape === 'function' or React.isValidElement(shape) /
React.isValidElement-like check) instead of any non-null value, so the
early-return condition in the null/zero check (the if block referencing x, y,
width, height and hasCustomShape) continues to filter out width===0/height===0
for default rectangle shapes but allows true custom renderers to bypass it.
In `@test/cartesian/Bar.truncateByDomain.spec.tsx`:
- Around line 155-205: The test 'selectStackGroups' currently calls
expect(spy).toHaveBeenLastCalledWith(...) — update this assertion to use the
project test-helper expectLastCalledWith helper instead; locate the test block
that invokes selectStackGroups and replace the
expect(spy).toHaveBeenLastCalledWith call with a call to
expectLastCalledWith(spy, <same expected object>) so the assertion uses the
helper while keeping the same expected payload (references: test name
'selectStackGroups', selector function 'selectStackGroups', and the local 'spy'
variable).
In `@test/cartesian/Bar.zeroDimensionFiltering.spec.tsx`:
- Around line 272-307: The test currently only asserts selector output
(selectBarRectangles) for the custom HorizonBar but doesn't verify the DOM
rendering; update the spec to also query the rendered output from renderTestCase
(the BarChart with Bar id "bar-marker" and shape HorizonBar) and assert that the
expected <line> elements for each data row are present and have the expected
attributes (e.g., y/x/width or stroke/strokeWidth) so the custom shape is
actually rendered end-to-end; use the same renderTestCase helper to get the
rendered container and querySelectorAll('line') (or a scoped selector) and
assert its length equals data.length and attribute values match the marker
positions.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
src/cartesian/Bar.tsxsrc/state/types/BarSettings.tstest/cartesian/Bar.truncateByDomain.spec.tsxtest/cartesian/Bar.zeroDimensionFiltering.spec.tsxtest/cartesian/Bar/Bar.spec.tsxtest/cartesian/Brush.stacked.spec.tsxtest/chart/BarChart.spec.tsxtest/state/graphicalItemsSlice.spec.tstest/state/selectors/axisSelectors.spec.tsxtest/state/selectors/selectStackGroups.spec.tsx
Bundle ReportChanges will increase total bundle size by 1.38kB (0.03%) ⬆️. This is within the configured threshold ✅ Detailed changes
Affected Assets, Files, and Routes:view changes for bundle: recharts/bundle-umdAssets Changed:
view changes for bundle: recharts/bundle-cjsAssets Changed:
view changes for bundle: recharts/bundle-treeshaking-cartesianAssets Changed:
view changes for bundle: recharts/bundle-es6Assets Changed:
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #7067 +/- ##
=======================================
Coverage 90.20% 90.21%
=======================================
Files 526 526
Lines 39648 39657 +9
Branches 5440 5447 +7
=======================================
+ Hits 35766 35775 +9
Misses 3873 3873
Partials 9 9 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Staging Deployment Details
These deployments will remain available for 30 days. To update snapshots: Comment |
Description
PR #6800 introduced zero-dimension bar filtering for perf (skip rendering
width === 0 || height === 0bars). This broke the BoxPlot pattern, which uses zero-height stacked bars with a customshapeprop to render horizontal line markers at stack positions.Changes
src/state/types/BarSettings.ts: AddedhasCustomShape: booleantoBarSettingssrc/cartesian/Bar.tsx(registration): PasseshasCustomShape={props.shape != null}into Redux state when Bar mountssrc/cartesian/Bar.tsx(computeBarRectangles): Zero-dimension filter now skips bars with a custom shape — they may still produce visible output even at zero height/widthBarSettingssnapshots across 8+ test files to includehasCustomShape: falseBar.zeroDimensionFiltering.spec.tsxasserting custom-shape zero-height bars are not filteredRelated Issue
https://github.com/recharts/recharts/issues/
Motivation and Context
The BoxPlot storybook example went blank after #6800. Zero-height bars are a legitimate pattern when a custom
shapehandles rendering — the bar coordinates are still needed for positioning.How Has This Been Tested?
should not filter out zero-height bars that have a custom shape (BoxPlot use case)— verifies all data points produce aBarRectangleItemwithheight === 0instead of being droppedtsc --noEmitclean acrosssrc/,test/, andstorybook/Screenshots (if appropriate):
N/A (storybook visual regression covers this via Chromatic)
Types of changes
Checklist:
Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.
Summary by CodeRabbit
New Features
Tests