Skip to content

fix(bar): use Math.round instead of bitwise truncation for bar positioning#7297

Merged
PavelVanecek merged 4 commits into
recharts:mainfrom
EduardF1:fix/bar-position-rounding
May 8, 2026
Merged

fix(bar): use Math.round instead of bitwise truncation for bar positioning#7297
PavelVanecek merged 4 commits into
recharts:mainfrom
EduardF1:fix/bar-position-rounding

Conversation

@EduardF1

@EduardF1 EduardF1 commented May 4, 2026

Copy link
Copy Markdown
Contributor

Problem

In ComposedChart, Bar and Scatter components can have misaligned X coordinates due to inconsistent use of the bitwise OR (>> 0) operator for truncating fractional pixel values.

Root cause

In combineAllBarPositions.ts, two places use >> 0 (bitwise right-shift) to truncate a float to an integer:

` s
// Case 1 – user-set barSize path
const offset = ((bandSize - sum) / 2) >> 0; // truncates toward zero

// Case 2 – auto-size path
if (originalSize > 1) {
originalSize >>= 0; // truncates toward zero
}
`

0 always floors toward zero (i.e. truncates the decimal), while Scatter uses raw floating-point values for its cx. This mismatch causes visible sub-pixel (and sometimes full-pixel) misalignment when bar widths or gaps result in fractional offsets.

Fix

Replace both truncations with Math.round(), which rounds to the nearest integer consistently with how other positional values are computed:

` s
// Case 1
const offset = Math.round((bandSize - sum) / 2);

// Case 2
if (originalSize > 1) {
originalSize = Math.round(originalSize);
}
`

Fixes #6115

Summary by CodeRabbit

  • Bug Fixes

    • Refined bar sizing and centering with rounded layout calculations, improving alignment and producing slightly adjusted bar widths across chart types.
    • Fixed stacking and background layering so bars and backgrounds render in the correct order.
  • Tests

    • Expanded and added regression tests for bar sizing, sub-pixel/zero-value edge cases, radial geometry, animations, tooltip payloads, and selector behavior.

@coderabbitai

coderabbitai Bot commented May 4, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (102)
  • test-vr/__snapshots__/tests/BarChart.reverseStackOrder.spec-vr.tsx-snapshots/StackedBarChart-with-reverseStackOrder-false-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/BarChart.reverseStackOrder.spec-vr.tsx-snapshots/StackedBarChart-with-reverseStackOrder-false-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/BarChart.reverseStackOrder.spec-vr.tsx-snapshots/StackedBarChart-with-reverseStackOrder-false-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/BarChart.reverseStackOrder.spec-vr.tsx-snapshots/StackedBarChart-with-reverseStackOrder-true-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/BarChart.reverseStackOrder.spec-vr.tsx-snapshots/StackedBarChart-with-reverseStackOrder-true-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/BarChart.reverseStackOrder.spec-vr.tsx-snapshots/StackedBarChart-with-reverseStackOrder-true-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Angled-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Angled-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Angled-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Chart-Reversed-By-Angles-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Chart-Reversed-By-Angles-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Chart-Reversed-By-Angles-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Chart-Reversed-By-Both-Angles-And-Reverse-Axis-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Chart-Reversed-By-Both-Angles-And-Reverse-Axis-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Chart-Reversed-By-Both-Angles-And-Reverse-Axis-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Reversed-Angle-Axis-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Reversed-Angle-Axis-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Reversed-Angle-Axis-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Reversed-Both-Axes-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Reversed-Both-Axes-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Reversed-Both-Axes-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Reversed-Radius-Axis-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Reversed-Radius-Axis-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Reversed-Radius-Axis-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Rings-With-Custom-Domain-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Rings-With-Custom-Domain-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Rings-With-Custom-Domain-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Rings-With-Data-Keys-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Rings-With-Data-Keys-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Rings-With-Data-Keys-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Rings-With-Data-Keys-And-Types-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Rings-With-Data-Keys-And-Types-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Rings-With-Data-Keys-And-Types-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Rings-With-Default-Axes-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Rings-With-Default-Axes-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Rings-With-Default-Axes-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Rings-With-Implicit-Axes-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Rings-With-Implicit-Axes-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Rings-With-Implicit-Axes-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Rings-With-Radius-Axis-Vertically-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Rings-With-Radius-Axis-Vertically-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Rings-With-Radius-Axis-Vertically-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Rings-With-Types-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Rings-With-Types-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/RadialBarChart.spec-vr.tsx-snapshots/Rings-With-Types-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarAlign.spec-vr.tsx-snapshots/CustomBandScaleExample---left-aligned-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarAlign.spec-vr.tsx-snapshots/CustomBandScaleExample---left-aligned-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarAlign.spec-vr.tsx-snapshots/CustomBandScaleExample---left-aligned-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/BarChartExample-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/BarChartHasBackground-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/BarChartHasBackground-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/BarChartStackedBySign-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/BarChartStackedBySign-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/BarChartWithCustomizedEvent-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/BarChartWithCustomizedEvent-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/BarChartWithCustomizedEvent-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/BarChartWithMinHeight-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/BarChartWithMultiXAxis-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/BiaxialBarChart-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/BiaxialBarChart-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/BoxPlotExample-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/BoxPlotExample-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/BrushBarChart-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/BrushBarChart-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/BrushBarChart-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/CustomShapeBarChart-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/CustomShapeBarChart-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/MixBarChart-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/MixBarChart-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/PositiveAndNegativeBarChart-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/PositiveAndNegativeBarChart-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/SimpleBarChart-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/SimpleBarChart-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/StackedBarChart-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/ComposedChartApiExamples.spec-vr.tsx-snapshots/ComposedChartExample-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/ComposedChartApiExamples.spec-vr.tsx-snapshots/ComposedChartNavExample-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/ComposedChartApiExamples.spec-vr.tsx-snapshots/ComposedChartNavExample-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/ComposedChartApiExamples.spec-vr.tsx-snapshots/ComposedChartNavExample-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/ComposedChartApiExamples.spec-vr.tsx-snapshots/LineBarAreaComposedChart-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/ComposedChartApiExamples.spec-vr.tsx-snapshots/SameDataComposedChart-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/ComposedChartApiExamples.spec-vr.tsx-snapshots/VerticalComposedChart-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/ComposedChartApiExamples.spec-vr.tsx-snapshots/VerticalComposedChart-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/ComposedChartApiExamples.spec-vr.tsx-snapshots/VerticalComposedChart-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/Customize.spec-vr.tsx-snapshots/CustomizeLegendAndTooltipStyle-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/Customize.spec-vr.tsx-snapshots/CustomizeLegendAndTooltipStyle-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/Customize.spec-vr.tsx-snapshots/CustomizeLegendAndTooltipStyle-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/Customize.spec-vr.tsx-snapshots/CustomizeSizeAndStroke-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/Customize.spec-vr.tsx-snapshots/CustomizeSizeAndStroke-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/Customize.spec-vr.tsx-snapshots/CustomizeSizeAndStroke-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/DomainAndTicks.spec-vr.tsx-snapshots/MassBarChart-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/LabelApiExamples.spec-vr.tsx-snapshots/LabelBarChartExample-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/LabelListApiExamples.spec-vr.tsx-snapshots/LabelListChartExample-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/LabelListApiExamples.spec-vr.tsx-snapshots/LabelListChartExample-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/RadialBarChartApiExamples.spec-vr.tsx-snapshots/RadialBarChartClickToFocusLegendExample-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/RadialBarChartApiExamples.spec-vr.tsx-snapshots/RadialBarChartClickToFocusLegendExample-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/RadialBarChartApiExamples.spec-vr.tsx-snapshots/RadialBarChartClickToFocusLegendExample-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/RadialBarChartApiExamples.spec-vr.tsx-snapshots/RadialBarChartNavExample-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/RadialBarChartApiExamples.spec-vr.tsx-snapshots/RadialBarChartNavExample-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/RadialBarChartApiExamples.spec-vr.tsx-snapshots/RadialBarChartNavExample-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/dark-mode.spec-vr.tsx-snapshots/dark-mode-examples-list-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/dark-mode.spec-vr.tsx-snapshots/dark-mode-examples-list-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/dark-mode.spec-vr.tsx-snapshots/dark-mode-examples-list-1-webkit-linux.png is excluded by !**/*.png

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bd7d5aed-1447-43b7-8650-2e60422d7b7b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Bar position/layout rounding in combineAllBarPositions switches from bitwise truncation to Math.round for centring offsets and computed original sizes; many tests across selectors, cartesian, chart, radial, tooltip, and animation suites were updated to match the new rounded geometry.

Changes

Bar position rounding and dependent tests

Layer / File(s) Summary
Core Calculation
src/state/selectors/combiners/combineAllBarPositions.ts
Replaced truncation with rounding: ((bandSize - sum) / 2) >> 0Math.round((bandSize - sum) / 2) and originalSize >>= 0originalSize = Math.round(originalSize).
Selector unit tests / regression tests
test/state/selectors/combiners/combineAllBarPositions.spec.ts, test/state/selectors/barStackSelectors.spec.tsx
Added regression tests for the new rounding behavior and updated selector expectations (explicit rectangle arrays, widths updated e.g., 10→11) to reflect rounded sizes/offsets.
Cartesian / Axis tests
test/cartesian/XAxis/XAxis.barSize.spec.tsx, test/cartesian/XAxis/XAxis.padding.spec.tsx, test/cartesian/Bar/Bar.spec.tsx
Updated numerous bar geometry expectations (widths, x/y positions, SVG path d) across barSize, padding, gap, minPointSize, maxBarSize, and background zIndex scenarios to align with rounded calculations.
Chart-level tests
test/chart/BarChart.spec.tsx, test/chart/RadialBarChart.spec.tsx
Revised bar and radial-sector geometries and stacked/grouped bar position expectations (width/offset/path updates) to match new layout outputs.
Tooltip / Payload tests
test/component/Tooltip/itemSorter.spec.tsx
Adjusted RadialBar tooltip payload shape expectations to include a nested background object and promoted radius/angle/value fields to top-level inspection points.
Animation tests
test/polar/RadialBar/RadialBar.animation.spec.tsx
Added an animation test for dataset change from 2→3 bars and updated per-step expected path sequences to reflect three bars per step.
Misc / manifests
(diff metadata)
Only test and selector/code rounding change; no public API declarations were added/removed.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • recharts/recharts#6746: Modifies the same combineAllBarPositions.ts combiner and getBarPositions layout logic.
  • recharts/recharts#6723: Touches the bar-position selector pipeline related to combineAllBarPositions / selectAllBarPositions.

Suggested labels

refactor

Suggested reviewers

  • ckifer
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: replacing bitwise truncation with Math.round for bar positioning calculations.
Description check ✅ Passed The PR description covers all key template sections: Problem statement with root cause analysis, the specific Fix with code examples, and links to issue #6115.
Linked Issues check ✅ Passed The PR implementation directly addresses issue #6115 by replacing bitwise truncation (>> 0) with Math.round in combineAllBarPositions.ts to align Bar and Scatter X coordinates in ComposedChart.
Out of Scope Changes check ✅ Passed All code changes in combineAllBarPositions.ts align with the stated objective. Test updates across multiple files reflect expected geometry adjustments from the rounding fix.

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

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

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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: 2

🤖 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 `@src/state/selectors/combiners/combineAllBarPositions.ts`:
- Around line 70-74: The computation of originalSize in
combineAllBarPositions.ts currently uses Math.round which can increase bar
widths so total bars plus gaps may exceed bandSize; replace the Math.round call
on originalSize with Math.floor (or equivalent truncation for positive values)
to ensure len * originalSize + (len−1)*realBarGap + 2*offset never exceeds
bandSize, then keep the subsequent size = isWellBehavedNumber(maxBarSize) ?
Math.min(originalSize, maxBarSize) : originalSize logic unchanged so maxBarSize
still caps the floored value.
- Line 42: Add unit tests for the combineAllBarPositions function to exercise
the new Math.round() behavior: include cases where (bandSize - sum) / 2 has
fractional part > 0.5 (e.g., yields 2.6 -> offset rounds to 3), where offset is
negative when sum > bandSize after fallback, and where originalSize has
fractional part > 0.5 (e.g., 24.8 -> 25); assert the returned positions and
sizes match expected rounded values and the negative-offset handling. Target the
combiners/combineAllBarPositions tests and create focused specs that construct
inputs to hit the offset and originalSize rounding branches and verify outputs.
Ensure tests fail on the old truncation behavior and pass with the current
Math.round() implementation.
🪄 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: 0158589e-b5c2-43e6-99ad-821cc7913701

📥 Commits

Reviewing files that changed from the base of the PR and between 215a050 and 12a4fc1.

📒 Files selected for processing (1)
  • src/state/selectors/combiners/combineAllBarPositions.ts

}

const offset = ((bandSize - sum) / 2) >> 0;
const offset = Math.round((bandSize - sum) / 2);

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 | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for existing test files covering combineAllBarPositions / getBarPositions
rg -rn --type=ts "combineAllBarPositions\|getBarPositions" --glob "**/*.test.*" -A 2

Repository: recharts/recharts

Length of output: 43


🏁 Script executed:

# Find the file and check its content
fd "combineAllBarPositions" --type f

Repository: recharts/recharts

Length of output: 117


🏁 Script executed:

# Look at the file content, particularly lines around 42 and 72
cat -n src/state/selectors/combiners/combineAllBarPositions.ts | head -80

Repository: recharts/recharts

Length of output: 3391


🏁 Script executed:

# Search more broadly for test files related to bar positions or combiners
fd --type f -e test.ts -e spec.ts | grep -i "bar\|position\|combiner" | head -20

Repository: recharts/recharts

Length of output: 359


🏁 Script executed:

# Search for any tests that import combineAllBarPositions or getBarPositions using different patterns
rg "import.*combineAllBarPositions|import.*getBarPositions" --type ts

Repository: recharts/recharts

Length of output: 293


🏁 Script executed:

# Search for test files for barSelectors
fd "barSelectors" --type f | grep -E "\.(test|spec)\.(ts|tsx)"

Repository: recharts/recharts

Length of output: 43


🏁 Script executed:

# Search for test files related to BarChart or bar positioning
fd --type f -e spec.ts -e test.ts | xargs grep -l "BarChart\|combineBarSizeList" 2>/dev/null | head -10

Repository: recharts/recharts

Length of output: 411


🏁 Script executed:

# Check if there's a test directory structure for combiners
find test -type d -name "combiners" 2>/dev/null

Repository: recharts/recharts

Length of output: 92


🏁 Script executed:

# Look at the git history to see what changed recently for this file
git log --oneline -n 20 src/state/selectors/combiners/combineAllBarPositions.ts 2>/dev/null

Repository: recharts/recharts

Length of output: 142


🏁 Script executed:

# List all test files in the combiners directory
ls -la test/state/selectors/combiners/

Repository: recharts/recharts

Length of output: 499


🏁 Script executed:

# Check if there's a test file for combineAllBarPositions that might not show up in the fd search
ls -la test/state/selectors/combiners/ | grep -i bar

Repository: recharts/recharts

Length of output: 43


🏁 Script executed:

# Search for any test content that mentions the getBarPositions function
rg "getBarPositions" test/ --type ts

Repository: recharts/recharts

Length of output: 43


🏁 Script executed:

# Check the recent commit to see what changed
git show 12a4fc1 --stat

Repository: recharts/recharts

Length of output: 50374


🏁 Script executed:

# Check what the actual change was in the commit
git show 12a4fc1 src/state/selectors/combiners/combineAllBarPositions.ts | head -100

Repository: recharts/recharts

Length of output: 3601


🏁 Script executed:

# Look for tests that might cover BarChart or bar positioning
find test -name "*.spec.ts" -type f | xargs grep -l "BarChart\|bar position\|bandSize" 2>/dev/null | head -5

Repository: recharts/recharts

Length of output: 174


🏁 Script executed:

# Search for any existing tests that use combineAllBarPositions or getBarPositions
find . -name "*.spec.ts" -o -name "*.test.ts" | xargs grep -l "barSize\|getBarPositions\|combineAllBarPositions" 2>/dev/null

Repository: recharts/recharts

Length of output: 201


🏁 Script executed:

# Check if there are any test files at all that might import from barSelectors or radialBarSelectors
rg "combineAllBarPositions" test/ --type ts

Repository: recharts/recharts

Length of output: 43


🏁 Script executed:

# Look at the barSelectors file to see how combineAllBarPositions is used
grep -A 5 -B 5 "combineAllBarPositions" src/state/selectors/barSelectors.ts | head -30

Repository: recharts/recharts

Length of output: 1240


🏁 Script executed:

# Count test files in the test directory to understand test coverage structure
find test -name "*.spec.ts" -type f | wc -l

Repository: recharts/recharts

Length of output: 64


🏁 Script executed:

# Check if there's a test file for barSelectors
ls -la test/state/selectors/ | grep -i bar

Repository: recharts/recharts

Length of output: 307


🏁 Script executed:

# Check if the existing bar selector tests cover combineAllBarPositions
cat test/state/selectors/barStackSelectors.spec.tsx | head -50

Repository: recharts/recharts

Length of output: 1873


🏁 Script executed:

# Check what the radialBarSelectors test file covers
cat test/state/selectors/radialBarSelectors.spec.tsx

Repository: recharts/recharts

Length of output: 1391


🏁 Script executed:

# Look at selectBarRectangles test to see if it covers positioning
head -80 test/state/selectors/selectBarRectangles.stackOffset.spec.tsx

Repository: recharts/recharts

Length of output: 2506


🏁 Script executed:

# Check if there are integration tests in any chart test files
find test -name "*BarChart*" -type f | head -5

Repository: recharts/recharts

Length of output: 241


🏁 Script executed:

# Check the actual test content of selectBarRectangles.stackOffset.spec.tsx to see if it covers fractional cases
grep -n "Math.round\|fractional\|bandSize\|originalSize" test/state/selectors/selectBarRectangles.stackOffset.spec.tsx

Repository: recharts/recharts

Length of output: 43


🏁 Script executed:

# Look for any test that specifically tests the combineBarSizeList or bar positioning logic
rg "combineBarSizeList" test/ --type ts -A 3

Repository: recharts/recharts

Length of output: 43


🏁 Script executed:

# Check if the new file has a corresponding test
ls -la test/state/selectors/combiners/ | grep -i "combineAllBarPositions\|combineBarSizeList"

Repository: recharts/recharts

Length of output: 43


🏁 Script executed:

# See the full combineAllBarPositions file to understand what needs testing
cat -n src/state/selectors/combiners/combineAllBarPositions.ts

Repository: recharts/recharts

Length of output: 4771


Add unit tests for combineAllBarPositions.ts covering fractional-rounding edge cases.

This new file lacks unit test coverage, which violates the coding guideline requiring 100% unit test coverage for new code in src/**/*.{ts,tsx}. The behavioral changes at lines 42 and 72—switching from bitwise truncation (>> 0) to Math.round()—need explicit test cases:

  • Line 42: (bandSize - sum) / 2 with fractional part > 0.5 (e.g., 2.6 rounds to 3, was truncated to 2)
  • Line 42: Negative offset (when sum > bandSize after fallback)
  • Line 72: originalSize with fractional part > 0.5 (e.g., 24.8 rounds to 25, was truncated to 24)

Also applies to: 72-72

🤖 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/state/selectors/combiners/combineAllBarPositions.ts` at line 42, Add unit
tests for the combineAllBarPositions function to exercise the new Math.round()
behavior: include cases where (bandSize - sum) / 2 has fractional part > 0.5
(e.g., yields 2.6 -> offset rounds to 3), where offset is negative when sum >
bandSize after fallback, and where originalSize has fractional part > 0.5 (e.g.,
24.8 -> 25); assert the returned positions and sizes match expected rounded
values and the negative-offset handling. Target the
combiners/combineAllBarPositions tests and create focused specs that construct
inputs to hit the offset and originalSize rounding branches and verify outputs.
Ensure tests fail on the old truncation behavior and pass with the current
Math.round() implementation.

Comment on lines 70 to 74
let originalSize = (bandSize - 2 * offset - (len - 1) * realBarGap) / len;
if (originalSize > 1) {
originalSize >>= 0;
originalSize = Math.round(originalSize);
}
const size = isWellBehavedNumber(maxBarSize) ? Math.min(originalSize, maxBarSize) : originalSize;

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 | ⚡ Quick win

Math.round can make total bar area exceed bandSize.

originalSize is derived so that len × originalSize + (len−1) × realBarGap + 2 × offset = bandSize exactly. When the fractional part exceeds 0.5, Math.round rounds up, which means each bar is rendered slightly wider than its slot allows — total overflow up to 0.5 × len pixels. The old >>= 0 (truncation) guaranteed bars never exceeded their allocated space.

For typical charts this is imperceptible (≤ 0.5 px per bar), but at narrow bandSizes or large len values, adjacent bars may visually overlap. If pixel-perfect containment is needed, Math.floor (equivalent to the old truncation for positive values) would be safer:

🔍 Alternative: use `Math.floor` to preserve no-overflow guarantee while still being explicit
-      originalSize = Math.round(originalSize);
+      originalSize = Math.floor(originalSize);
📝 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.

Suggested change
let originalSize = (bandSize - 2 * offset - (len - 1) * realBarGap) / len;
if (originalSize > 1) {
originalSize >>= 0;
originalSize = Math.round(originalSize);
}
const size = isWellBehavedNumber(maxBarSize) ? Math.min(originalSize, maxBarSize) : originalSize;
let originalSize = (bandSize - 2 * offset - (len - 1) * realBarGap) / len;
if (originalSize > 1) {
originalSize = Math.floor(originalSize);
}
const size = isWellBehavedNumber(maxBarSize) ? Math.min(originalSize, maxBarSize) : originalSize;
🤖 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/state/selectors/combiners/combineAllBarPositions.ts` around lines 70 -
74, The computation of originalSize in combineAllBarPositions.ts currently uses
Math.round which can increase bar widths so total bars plus gaps may exceed
bandSize; replace the Math.round call on originalSize with Math.floor (or
equivalent truncation for positive values) to ensure len * originalSize +
(len−1)*realBarGap + 2*offset never exceeds bandSize, then keep the subsequent
size = isWellBehavedNumber(maxBarSize) ? Math.min(originalSize, maxBarSize) :
originalSize logic unchanged so maxBarSize still caps the floored value.

@PavelVanecek

PavelVanecek commented May 5, 2026

Copy link
Copy Markdown
Collaborator

Do we have reproduction, screenshots perhaps, with before & after? Also could we cover this with an eslint rule to prevent regressions? Or unit test?

Edit: judging by the failures I guess the test part is covered.

@codecov

codecov Bot commented May 5, 2026

Copy link
Copy Markdown

Bundle Report

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

Detailed changes
Bundle name Size Change
recharts/bundle-cjs 1.4MB 28 bytes (0.0%) ⬆️
recharts/bundle-es6 1.23MB 28 bytes (0.0%) ⬆️
recharts/bundle-umd 592.62kB 18 bytes (0.0%) ⬆️
recharts/bundle-treeshaking-cartesian 701.81kB 28 bytes (0.0%) ⬆️
recharts/bundle-treeshaking-polar 485.2kB 28 bytes (0.01%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: recharts/bundle-treeshaking-polar

Assets Changed:

Asset Name Size Change Total Size Change (%)
bundle.js 28 bytes 485.2kB 0.01%
view changes for bundle: recharts/bundle-umd

Assets Changed:

Asset Name Size Change Total Size Change (%)
Recharts.js 18 bytes 592.62kB 0.0%
view changes for bundle: recharts/bundle-treeshaking-cartesian

Assets Changed:

Asset Name Size Change Total Size Change (%)
bundle.js 28 bytes 701.81kB 0.0%
view changes for bundle: recharts/bundle-es6

Assets Changed:

Asset Name Size Change Total Size Change (%)
state/selectors/combiners/combineAllBarPositions.js 28 bytes 4.43kB 0.64%
view changes for bundle: recharts/bundle-cjs

Assets Changed:

Asset Name Size Change Total Size Change (%)
state/selectors/combiners/combineAllBarPositions.js 28 bytes 4.69kB 0.6%

…ed spec

The PR's bitwise→Math.round change shifts a number of bar positions and
sizes by sub-pixel amounts. Updates the hard-coded expected values in
the affected suites (BarChart, Bar, XAxis padding/barSize, RadialBar*,
itemSorter, barStackSelectors) and adds a targeted spec that exercises
combineAllBarPositions for the issue recharts#6115 alignment scenario.
@EduardF1

EduardF1 commented May 5, 2026

Copy link
Copy Markdown
Contributor Author

@PavelVanecek thanks for the review. Addressing your questions:

Reproduction + before/after screenshots

Side-by-side reproduction of issue #6115 (bandSize=91, barCategoryGap='10%', single Bar+Scatter where the Scatter dot's cx should sit at the bar's centre). The vertical red dashed line is the Scatter cx; the bar should be centred on it.

side-by-side

Before/after as separate images: before.png · after.png

Numeric proof from the same render (delta = scatter cx − bar centre):

cat before delta after delta bar width before/after
A +0.4 px −0.1 px 72 / 73
B +0.4 px −0.1 px 72 / 73
C +0.4 px −0.1 px 72 / 73
D +0.4 px −0.1 px 72 / 73
E +0.4 px −0.1 px 72 / 73

That matches @VIDHITTS's analysis on #6115 exactly: with >> 0, originalSize truncates 72.8 → 72 and the bar centre lands at 9.1 + 36 = 45.1 while Scatter cx = bandSize/2 = 45.5 (0.4 px error). With Math.round we land on 73 and bar centre 45.6, matching Scatter to within 0.1 px.

Unit test coverage

Added a focused spec at test/state/selectors/combiners/combineAllBarPositions.spec.ts that exercises both branches:

I sanity-checked these on main (pre-fix) and they fail with the expected −1 deltas, so they lock in the rounding direction.

ESLint rule to prevent regressions

>> 0 truncation patterns are easy to miss in review. I'd suggest enabling no-bitwise for the src/ tree, with an opt-out comment on any genuinely intentional bit-twiddling. Not in this PR — happy to do it as a follow-up if you want.

Other failing checks

  • The 49 unit-test failures from the previous run were all hard-coded expected values that encoded the old >> 0 truncation residue (e.g. offset: 5.999999999999997 from a borderline number that now lands cleanly on 6). Those are updated in the latest commit; suite is now green locally for unit:lib (5800 pass, no failures attributable to this PR).
  • VR Tests fail on three browsers because the rendered pixels shifted; those snapshots need regenerating (Docker-only npm run test-vr:update per test-vr/README.md). I can't run that locally on Windows; happy to hand it off, or push a separate snapshot-update commit if you point me at the runner.

CodeRabbit's Math.floor suggestion

CodeRabbit flagged that Math.round on originalSize can push len * originalSize + (len-1) * realBarGap + 2 * offset over bandSize and suggested Math.floor. I considered it and went with Math.round:

  • For Scatter and Bar components have misaligned X coordinates in ComposedChart #6115's exact case Math.floor(72.8) = 72 puts us back at the 0.4 px error this PR is meant to fix.
  • The overflow CodeRabbit is worried about is bounded by 0.5 × len px and is absorbed into the existing barCategoryGap offset zone (the bars don't actually paint outside bandSize because maxBarSize already clamps the practical worst case).

If you'd rather have Math.floor for the overflow guarantee even at the cost of partial alignment, I'll switch — but my read is Math.round is the better trade for the issue at hand.

@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

🧹 Nitpick comments (1)
test/polar/RadialBar/RadialBar.animation.spec.tsx (1)

255-285: ⚡ Quick win

Track selector churn in the new data-change animation case.

This scenario already uses createSelectorTestCase, but it only asserts path snapshots. Since this path combines a data-length change with multiple animation steps, it is also a good place to lock down selector call count so rerender regressions do not slip in with future animation changes.

As per coding guidelines "Verify the number of selector calls using the spy object from createSelectorTestCase to spot unnecessary re-renders and improve performance".

🤖 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/polar/RadialBar/RadialBar.animation.spec.tsx` around lines 255 - 285,
The test should also verify selector churn: use the spy returned by
createSelectorTestCase (available via renderTestCase — e.g., selectorSpy) and
assert its call count after the animation completes; add an assertion like
expect(selectorSpy).toHaveBeenCalledTimes(5) (one call per animation frame shown
in expectAnimatedRadialBarPaths) immediately after the existing path snapshot
assertion to lock down unnecessary selector re-renders for the data-length
change scenario.
🤖 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 `@test/state/selectors/combiners/combineAllBarPositions.spec.ts`:
- Around line 20-48: Update the test to assert exact centre alignment with the
Scatter midpoint rather than only checking rounded sizes/offsets: in the
single-bar case (calling combineAllBarPositions) assert that
positions[0].position.offset + positions[0].position.size / 2 === bandSize / 2
(use the bandSize variable 91 used in the test), and in the user-sized two-bar
case assert that the group midpoint (positions[0].position.offset + (sum of
barSizes for the group) / 2) === bandSize / 2; keep the existing checks for
size/offset rounding but add these chart-level centre alignment assertions so
the test ensures bar/group centre is derived from the same midpoint as Scatter
and prevents the partial-improvement regression.

---

Nitpick comments:
In `@test/polar/RadialBar/RadialBar.animation.spec.tsx`:
- Around line 255-285: The test should also verify selector churn: use the spy
returned by createSelectorTestCase (available via renderTestCase — e.g.,
selectorSpy) and assert its call count after the animation completes; add an
assertion like expect(selectorSpy).toHaveBeenCalledTimes(5) (one call per
animation frame shown in expectAnimatedRadialBarPaths) immediately after the
existing path snapshot assertion to lock down unnecessary selector re-renders
for the data-length change scenario.
🪄 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: adab2edd-7504-4ffd-ba9c-989177d899a6

📥 Commits

Reviewing files that changed from the base of the PR and between 12a4fc1 and 9b3e06e.

📒 Files selected for processing (9)
  • test/cartesian/Bar/Bar.spec.tsx
  • test/cartesian/XAxis/XAxis.barSize.spec.tsx
  • test/cartesian/XAxis/XAxis.padding.spec.tsx
  • test/chart/BarChart.spec.tsx
  • test/chart/RadialBarChart.spec.tsx
  • test/component/Tooltip/itemSorter.spec.tsx
  • test/polar/RadialBar/RadialBar.animation.spec.tsx
  • test/state/selectors/barStackSelectors.spec.tsx
  • test/state/selectors/combiners/combineAllBarPositions.spec.ts

Comment on lines +20 to +48
* For the issue's repro: bandSize=91, barCategoryGap='10%', single bar.
* - barCategoryGap => offset = 9.1
* - originalSize = (91 - 18.2) / 1 = 72.8 -> Math.round -> 73
* - bar centre = offset + originalSize/2 = 9.1 + 36.5 = 45.6
* - Scatter centre = bandSize / 2 = 45.5
* - alignment error: 0.1px (was 0.4px with truncation)
*/
it('rounds fractional originalSize instead of truncating (issue #6115)', () => {
const sizeList: SizeList = [{ stackId: undefined, dataKeys: ['uv'], barSize: undefined }];
const positions = combineAllBarPositions(sizeList, undefined, 0, '10%', 91, 91, undefined);
expect(positions).toBeDefined();
expect(positions).toHaveLength(1);
// Math.round(72.8) === 73, NOT 72 (which is what `>> 0` produced).
expect(positions![0].position.size).toBe(73);
});

it('rounds the centring offset of user-specified barSizes instead of truncating', () => {
// if-branch: barSize set by user.
// bandSize=91, two bars with barSize=20 each, no gap.
// sum = 40, (bandSize - sum) / 2 = 25.5 -> Math.round -> 26 (was 25 via `>> 0`)
const sizeList: SizeList = [
{ stackId: undefined, dataKeys: ['a'], barSize: 20 },
{ stackId: undefined, dataKeys: ['b'], barSize: 20 },
];
const positions = combineAllBarPositions(sizeList, undefined, 0, 0, 91, 91, undefined);
expect(positions).toBeDefined();
expect(positions).toHaveLength(2);
expect(positions![0].position.offset).toBe(26);
});

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 | 🟠 Major | 🏗️ Heavy lift

This regression test still locks in a non-zero Bar/Scatter mismatch.

The first case’s own math leaves the bar centre at 45.6 while Scatter stays at 45.5, and the user-sized case has the same problem (26 + 20 = 46 for a 91px band). So the new assertions codify a partial improvement, not the exact alignment promised by #6115. Please make the bar/group centre derive from the same midpoint as Scatter, and add a chart-level assertion for the repro so the real contract is protected.

🤖 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/state/selectors/combiners/combineAllBarPositions.spec.ts` around lines
20 - 48, Update the test to assert exact centre alignment with the Scatter
midpoint rather than only checking rounded sizes/offsets: in the single-bar case
(calling combineAllBarPositions) assert that positions[0].position.offset +
positions[0].position.size / 2 === bandSize / 2 (use the bandSize variable 91
used in the test), and in the user-sized two-bar case assert that the group
midpoint (positions[0].position.offset + (sum of barSizes for the group) / 2)
=== bandSize / 2; keep the existing checks for size/offset rounding but add
these chart-level centre alignment assertions so the test ensures bar/group
centre is derived from the same midpoint as Scatter and prevents the
partial-improvement regression.

- Add as-cast on selectBarRectangles assertions where the runtime
  rectangle data spreads original chart data fields (uv/pv/xKey/etc.)
  that aren't part of the BarRectangleItem type.
- Add explicit stackId: undefined to selectAllBarPositions assertions
  so the literal satisfies BarWithPosition.
- Re-run prettier on all touched test files to clear the eslint
  prettier/prettier errors (whitespace + formatting only).
@EduardF1

EduardF1 commented May 6, 2026

Copy link
Copy Markdown
Contributor Author

Pushed 3ef336b which clears the Unit Tests + Eslint/Typecheck failures from the previous run.

  • Eslint and Typecheck: ran prettier on the seven test files I touched (whitespace only, net -49 lines). Eslint is now 0 errors, 12 pre-existing no-console warnings.
  • Unit Tests: tsc --project test/tsconfig.json was failing on two patterns. (a) selectBarRectangles returns BarRectangleItem[] but the runtime rectangles spread the original chart-data fields (uv, pv, xKey, name, amt) into each item, which excess-property-check rejects. Added as unknown as BarRectangleItem[] on the four affected assertions to match what the runtime actually emits without re-modelling the type. (b) selectAllBarPositions returns BarWithPosition[] whose stackId: StackId | undefined is required, so I added explicit stackId: undefined to the two left/right-axis literals.
  • All 368 tests across the eight modified files pass locally (npx vitest run).

VR Tests are still red, and I haven't attempted to update the baselines. The diffs are exactly what you'd expect from >> 0Math.round: most snapshots show ~160 px (0.01 ratio) sub-pixel shifts where the centring offset moves by 0.5 px, plus a handful of 0.3–0.4-ratio diffs on the charts where the rounding direction flipped a bar by a full pixel. Those are intentional consequences of the fix.

If the maintainers are happy with the change, /update-snapshots should regenerate the baselines cleanly. Happy to also walk through any individual diff if you want a sanity check on a specific chart before that's run.

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

🧹 Nitpick comments (1)
test/state/selectors/barStackSelectors.spec.tsx (1)

48-520: ⚡ Quick win

Add selector call-count assertions in these updated selector tests.

The new assertions validate geometry well, but they still miss recomputation guards. Please also assert spy call counts for the updated selectBarRectangles and selectStackRects cases.

As per coding guidelines, "Verify the number of selector calls using the spy object from createSelectorTestCase to spot unnecessary re-renders and improve performance."

🤖 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/state/selectors/barStackSelectors.spec.tsx` around lines 48 - 520, Add
assertions that verify the selector spy call count after the geometry
assertions: in the tests that call selectBarRectangles (the two cases using
renderTestCase and returning { spy }) and the selectStackRects test, add
expect(spy).toHaveBeenCalledTimes(1) (or the appropriate expected count)
immediately after expectLastCalledWith to ensure the selector was invoked only
once; reference the spy returned from renderTestCase and the selectors
selectBarRectangles and selectStackRects when adding these assertions.
🤖 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.

Nitpick comments:
In `@test/state/selectors/barStackSelectors.spec.tsx`:
- Around line 48-520: Add assertions that verify the selector spy call count
after the geometry assertions: in the tests that call selectBarRectangles (the
two cases using renderTestCase and returning { spy }) and the selectStackRects
test, add expect(spy).toHaveBeenCalledTimes(1) (or the appropriate expected
count) immediately after expectLastCalledWith to ensure the selector was invoked
only once; reference the spy returned from renderTestCase and the selectors
selectBarRectangles and selectStackRects when adding these assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a6bd964b-2063-4737-bab1-c6563d52a40b

📥 Commits

Reviewing files that changed from the base of the PR and between 9b3e06e and 3ef336b.

📒 Files selected for processing (7)
  • test/cartesian/Bar/Bar.spec.tsx
  • test/cartesian/XAxis/XAxis.barSize.spec.tsx
  • test/cartesian/XAxis/XAxis.padding.spec.tsx
  • test/chart/BarChart.spec.tsx
  • test/chart/RadialBarChart.spec.tsx
  • test/component/Tooltip/itemSorter.spec.tsx
  • test/state/selectors/barStackSelectors.spec.tsx

@EduardF1

EduardF1 commented May 7, 2026

Copy link
Copy Markdown
Contributor Author

Hi @PavelVanecek, the workflow run on commit 3ef336b is sitting at action_required (first-time-contributor approval gate), so the CI hasn't actually run since the fix push. Could you approve the workflow run when convenient? That commit covers the prettier autofix on the new spec files plus the typecheck fixture corrections, so we should know quickly whether anything else is left.

@ckifer

ckifer commented May 7, 2026

Copy link
Copy Markdown
Member

running

@codecov

codecov Bot commented May 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.05%. Comparing base (215a050) to head (5f018b0).
⚠️ Report is 8 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7297      +/-   ##
==========================================
- Coverage   89.05%   89.05%   -0.01%     
==========================================
  Files         541      541              
  Lines       41101    41100       -1     
  Branches     5565     5564       -1     
==========================================
- Hits        36601    36600       -1     
  Misses       4492     4492              
  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.

@EduardF1

EduardF1 commented May 7, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for approving the workflows, @ckifer. CI is now running on commit 3ef336b, and the long-tail integration tests (React 16/17/18/19 across npm/pnpm/yarn) are already green; Eslint+Typecheck and Omnidoc passed too. Only Unit Tests, VR Tests, and Website Build are still in flight.

@PavelVanecek, the screenshots posted yesterday cover the Math.round case you asked about. I'll keep an eye on the remaining checks and push fixes promptly if any of them surface real issues. Would appreciate a review once CI settles.

The Math.round change in combineAllBarPositions.ts shifts bar
positioning by sub-pixel amounts (1 - 2 px in some places). Refresh
the affected VR snapshots so the chromium / firefox / webkit runs
pass on CI.
@EduardF1

EduardF1 commented May 7, 2026

Copy link
Copy Markdown
Contributor Author

Pushed 5f018b0 refreshing the VR snapshots. The Math.round change shifted bar pixels by 1 - 2 px, which affected 102 PNGs across BarChart, RadialBarChart, ComposedChart and a few API-example tests; the remaining 876 snapshots regenerated to byte-identical content (deterministic docker rendering), so the diff is scoped only to bar-related views.

@ckifer / @PavelVanecek, this push is sitting at action_required again (first-time-contributor gate fires per push). Could you re-approve the workflow run when convenient? After this run, the only remaining variable should be Unit Tests, which already passed on the previous run.

@PavelVanecek
PavelVanecek merged commit 7aa22c2 into recharts:main May 8, 2026
53 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.

Scatter and Bar components have misaligned X coordinates in ComposedChart

3 participants