Skip to content

[fix] Update ticks calculator and domain extension#7146

Merged
ckifer merged 9 commits intomainfrom
boxplot
Mar 17, 2026
Merged

[fix] Update ticks calculator and domain extension#7146
ckifer merged 9 commits intomainfrom
boxplot

Conversation

@PavelVanecek
Copy link
Copy Markdown
Collaborator

@PavelVanecek PavelVanecek commented Mar 17, 2026

Description

Fixes two bugs that were affecting the new boxplot example - now we don't need to be embarassed to release it.

Related Issue

Fixes #7134
Fixes #7138

Screenshots (if appropriate):

image

Notice the XAxis domain (before: ABDE, after: ABCDE) and notice the YAxis domain (now it correctly extends based on the error bar values).

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

  • Bug Fixes
    • Improved axis domain calculations to accurately include error bar ranges when present.
    • Enhanced handling of mixed data sources in axis domain computations for complex chart scenarios.
    • Better support for Panorama mode in data-driven axis calculations.

PavelVanecek and others added 9 commits March 15, 2026 10:17
When a chart has items with own data (e.g. Scatter with outliers) alongside
items that use chart-level data (e.g. Bar), the numerical axis domain was
only covering the items-with-own-data. The root cause was that
combineDomainOfAllAppliedNumericalValuesIncludingErrorValues received
selectDisplayedData which returns only graphicalItemsData when any item
has own data, ignoring chart-level data entirely.

Fix: restructure the iteration to be item-first. For each item, use
item.data when present, falling back to the chart-level data slice. This
ensures Bar (chart-level) and Scatter (own data) both contribute to the
domain.

The chartDataSlice is passed as a new input to the domain selector via
selectChartDataSliceIfNotInPanorama, a content-memoized selector that
returns a stable reference when the slice content hasn't changed. This
prevents spurious recomputes but introduces one additional render in
ScatterChart and ComposedChart (consistent with existing behaviour in
horizontal LineChart, which already expected 4 renders).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 17, 2026

Walkthrough

The PR introduces mixed data-source domain calculation by adding selectors to detect when Cartesian chart items rely on chart-level data rather than their own data prop. It extends the domain computation pipeline to handle cases with no items by falling back to chart data when an axis has a dataKey, and updates error domain calculations to account for ranged bar values and whisker ranges.

Changes

Cohort / File(s) Summary
Selector Module Updates
src/state/selectors/axisSelectors.ts, src/state/selectors/dataSelectors.ts, src/state/selectors/polarSelectors.ts, src/state/selectors/tooltipSelectors.ts
Introduces new data-sourcing selectors (selectChartDataSliceIfNotInPanorama, selectChartDataSliceIgnoringIndexes, selectChartDataSliceWithIndexes, selectAnyCartesianItemsUsesChartData) and extends domain calculation pipeline. Renames combineAppliedValues to combineAllAppliedValues and updates selector dependencies to support mixed chart and item-level data. Modifies combineDomainOfAllAppliedNumericalValuesIncludingErrorValues signature to accept chartDataSlice parameter. Updates error domain computation to extract numeric values from arrays when needed.
Test Updates
test/cartesian/ErrorBar.spec.tsx, test/cartesian/XAxis/XAxis.categorydomain.spec.tsx, test/chart/LineChart.spec.tsx, test/state/selectors/axisSelectors.spec.tsx
Adds test coverage for error bar domain calculations with ranged bar values, validating that axis domains extend to cover whisker ranges. Tests domain combination from both chart-level and graphical item data. Updates test expectations for tick generation and selector render counts based on new data-sourcing behavior.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant AxisDomain as Domain Calculation
    participant DataDetection as Data Detection
    participant DataSelection as Data Selection
    participant ErrorBars as Error Bar Handler

    User->>AxisDomain: Render chart (Bar + ErrorBar ± Scatter)
    AxisDomain->>DataDetection: Check if any item uses chart data
    
    alt Item has own data
        DataDetection-->>AxisDomain: anyItemUsesChartData = false
        AxisDomain->>DataSelection: Use item-level data only
    else Item lacks data prop
        DataDetection-->>AxisDomain: anyItemUsesChartData = true
        AxisDomain->>DataSelection: Merge chart data + item data
    end
    
    DataSelection-->>AxisDomain: Return combined data
    AxisDomain->>ErrorBars: Compute domain with error ranges
    ErrorBars->>ErrorBars: Extract numeric values from ranged data
    ErrorBars-->>AxisDomain: Extended domain (base ± errors)
    AxisDomain-->>User: Render axis with full domain
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

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

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.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 refers to 'ticks calculator and domain extension' which aligns with the PR's domain and axis-related changes, though it doesn't highlight the specific boxplot bug fixes mentioned in the description.
Description check ✅ Passed The description provides required sections: description mentions fixing boxplot bugs, linked issues are specified (#7134 and #7138), changes are classified as bug fixes, and testing coverage is claimed. However, some sections like 'Motivation and Context' and 'How Has This Been Tested' lack detail.
Linked Issues check ✅ Passed The PR changes address both linked issues: #7134 is fixed by extending domain computation to include ErrorBar ranged values via updated combineDomainOfAllAppliedNumericalValuesIncludingErrorValues logic; #7138 is fixed by restructuring domain combination to iterate items first and merge chart-level data, ensuring no data points are skipped.
Out of Scope Changes check ✅ Passed All changes are scoped to addressing the two linked issues: domain/tick calculations for boxplot support, axis data selection, and related selector updates. Test additions and export changes directly support these fixes. No unrelated modifications detected.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch boxplot
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

CodeRabbit can suggest fixes for GitHub Check annotations.

Configure the reviews.tools.github-checks setting to adjust the time to wait for GitHub Checks to complete.

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 17, 2026

Codecov Report

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

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7146      +/-   ##
==========================================
+ Coverage   89.59%   89.61%   +0.02%     
==========================================
  Files         536      536              
  Lines       40401    40479      +78     
  Branches     5498     5520      +22     
==========================================
+ Hits        36197    36275      +78     
  Misses       4196     4196              
  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.

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 17, 2026

Bundle Report

Changes will increase total bundle size by 35.68kB (0.71%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
recharts/bundle-cjs 1.3MB 5.48kB (0.42%) ⬆️
recharts/bundle-es6 1.13MB 5.06kB (0.45%) ⬆️
recharts/bundle-umd 546.67kB 875 bytes (0.16%) ⬆️
recharts/bundle-treeshaking-sankey 345.54kB 4.84kB (1.42%) ⬆️
recharts/bundle-treeshaking-polar 447.99kB 4.92kB (1.11%) ⬆️
recharts/bundle-treeshaking-treemap 354.02kB 4.84kB (1.39%) ⬆️
recharts/bundle-treeshaking-sunburst 323.22kB 4.84kB (1.52%) ⬆️
recharts/bundle-treeshaking-cartesian 643.6kB 4.84kB (0.76%) ⬆️

Affected Assets, Files, and Routes:

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

Assets Changed:

Asset Name Size Change Total Size Change (%)
bundle.js 4.84kB 323.22kB 1.52%
view changes for bundle: recharts/bundle-treeshaking-polar

Assets Changed:

Asset Name Size Change Total Size Change (%)
bundle.js 4.92kB 447.99kB 1.11%
view changes for bundle: recharts/bundle-cjs

Assets Changed:

Asset Name Size Change Total Size Change (%)
state/selectors/axisSelectors.js 3.3kB 69.25kB 5.0%
state/selectors/tooltipSelectors.js 309 bytes 18.22kB 1.73%
state/selectors/polarSelectors.js 52 bytes 4.94kB 1.06%
state/selectors/dataSelectors.js 1.82kB 4.23kB 75.35% ⚠️
view changes for bundle: recharts/bundle-treeshaking-sankey

Assets Changed:

Asset Name Size Change Total Size Change (%)
bundle.js 4.84kB 345.54kB 1.42%
view changes for bundle: recharts/bundle-treeshaking-cartesian

Assets Changed:

Asset Name Size Change Total Size Change (%)
bundle.js 4.84kB 643.6kB 0.76%
view changes for bundle: recharts/bundle-umd

Assets Changed:

Asset Name Size Change Total Size Change (%)
Recharts.js 875 bytes 546.67kB 0.16%
view changes for bundle: recharts/bundle-treeshaking-treemap

Assets Changed:

Asset Name Size Change Total Size Change (%)
bundle.js 4.84kB 354.02kB 1.39%
view changes for bundle: recharts/bundle-es6

Assets Changed:

Asset Name Size Change Total Size Change (%)
state/selectors/axisSelectors.js 3.16kB 59.23kB 5.64% ⚠️
state/selectors/tooltipSelectors.js 300 bytes 14.72kB 2.08%
state/selectors/polarSelectors.js 74 bytes 3.94kB 1.92%
state/selectors/dataSelectors.js 1.52kB 3.32kB 84.9% ⚠️

Copy link
Copy Markdown
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

🧹 Nitpick comments (1)
test/cartesian/XAxis/XAxis.categorydomain.spec.tsx (1)

595-611: Please pin the recompute budget in these new selector tests.

They currently only assert the final domain, so an unnecessary extra selector pass will still go green. createSelectorTestCase already gives you the spy—add toHaveBeenCalledTimes(...) here as well. 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 the current code and only fix it if needed.

In `@test/cartesian/XAxis/XAxis.categorydomain.spec.tsx` around lines 595 - 611,
The tests created with createSelectorTestCase (renderTestCase) are missing
assertions on selector call counts; after obtaining the spy from
renderTestCase(useXAxisDomain) and renderTestCase(selectTooltipAxisDomain) add
an assertion like expect(spy).toHaveBeenCalledTimes(1) to pin the recompute
budget and ensure only one selector pass occurs; update the two it blocks that
call useXAxisDomain and selectTooltipAxisDomain to include this call-count
assertion immediately after the existing expectLastCalledWith(spy, [...])
checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/state/selectors/dataSelectors.ts`:
- Around line 60-97: The selectors selectChartDataSliceIfNotInPanorama,
selectChartDataSliceIgnoringIndexes, and selectChartDataSliceWithIndexes
currently always return chartData.slice(...) which produces a new array
reference even when contents are identical; change each selector so it returns a
stable array instance when the slice content hasn’t changed by caching the last
returned slice and the last inputs (e.g., last chartData reference, last
dataStartIndex, last dataEndIndex) inside the selector projector (or create a
createSelectorCreator wrapper using defaultMemoize + deep equality) and, before
creating a new slice, compare start/end and either the chartData reference or
deep-equal the candidate slice to the cached slice and return the cached slice
when equal; update the projector bodies of selectChartDataSliceIfNotInPanorama,
selectChartDataSliceIgnoringIndexes, and selectChartDataSliceWithIndexes to use
that cache logic.

---

Nitpick comments:
In `@test/cartesian/XAxis/XAxis.categorydomain.spec.tsx`:
- Around line 595-611: The tests created with createSelectorTestCase
(renderTestCase) are missing assertions on selector call counts; after obtaining
the spy from renderTestCase(useXAxisDomain) and
renderTestCase(selectTooltipAxisDomain) add an assertion like
expect(spy).toHaveBeenCalledTimes(1) to pin the recompute budget and ensure only
one selector pass occurs; update the two it blocks that call useXAxisDomain and
selectTooltipAxisDomain to include this call-count assertion immediately after
the existing expectLastCalledWith(spy, [...]) checks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ecc5c928-ba85-4aa8-b7ac-9b0e1140caf2

📥 Commits

Reviewing files that changed from the base of the PR and between fa2d2f1 and 064b2d4.

⛔ Files ignored due to path filters (6)
  • 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/BoxPlotExample-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/CandlestickExample-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/CandlestickExample-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/BarChartApiExamples.spec-vr.tsx-snapshots/CandlestickExample-1-webkit-linux.png is excluded by !**/*.png
📒 Files selected for processing (8)
  • src/state/selectors/axisSelectors.ts
  • src/state/selectors/dataSelectors.ts
  • src/state/selectors/polarSelectors.ts
  • src/state/selectors/tooltipSelectors.ts
  • test/cartesian/ErrorBar.spec.tsx
  • test/cartesian/XAxis/XAxis.categorydomain.spec.tsx
  • test/chart/LineChart.spec.tsx
  • test/state/selectors/axisSelectors.spec.tsx

Comment on lines +60 to +97
/**
* Returns the chart-level data slice (respecting Brush indexes), memoized by content so that
* spurious Immer reference changes (e.g. dispatching `setChartData(undefined)` when data is
* already `undefined`) do not propagate to downstream selectors.
*
* Used when a selector needs chart-level data but must avoid extra recomputes when the
* data content has not actually changed.
*/
export const selectChartDataSliceIfNotInPanorama: (
state: RechartsRootState,
_unused1: unknown,
_unused2: unknown,
isPanorama: boolean,
) => ChartData = createSelector(
[selectChartDataWithIndexesIfNotInPanoramaPosition4],
({ chartData, dataStartIndex, dataEndIndex }: ChartDataState): ChartData =>
chartData != null ? chartData.slice(dataStartIndex, dataEndIndex + 1) : [],
);

/**
* Returns the chart-level data slice (ignoring Brush indexes), memoized by content.
* Used in tooltip and polar selectors that always need the full data range.
*/
export const selectChartDataSliceIgnoringIndexes: (state: RechartsRootState) => ChartData = createSelector(
[selectChartDataAndAlwaysIgnoreIndexes],
({ chartData, dataStartIndex, dataEndIndex }: ChartDataState): ChartData =>
chartData != null ? chartData.slice(dataStartIndex, dataEndIndex + 1) : [],
);

/**
* Returns the chart-level data slice (with Brush indexes applied), memoized by content.
* Used in tooltip selectors.
*/
export const selectChartDataSliceWithIndexes: (state: RechartsRootState) => ChartData = createSelector(
[selectChartDataWithIndexes],
({ chartData, dataStartIndex, dataEndIndex }: ChartDataState): ChartData =>
chartData != null ? chartData.slice(dataStartIndex, dataEndIndex + 1) : [],
);
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

The “memoized by content” guarantee is not implemented yet.

These selectors still emit a fresh slice(...) array whenever the surrounding ChartDataState object gets a new reference, so unchanged slices keep invalidating downstream selectors. The 3→4 call-count bump in test/state/selectors/axisSelectors.spec.tsx is already one symptom of that. Please stabilize equal slices here instead of teaching callers to expect the extra recompute. Based on learnings "Value consistency, usability, and performance in Recharts implementation".

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

In `@src/state/selectors/dataSelectors.ts` around lines 60 - 97, The selectors
selectChartDataSliceIfNotInPanorama, selectChartDataSliceIgnoringIndexes, and
selectChartDataSliceWithIndexes currently always return chartData.slice(...)
which produces a new array reference even when contents are identical; change
each selector so it returns a stable array instance when the slice content
hasn’t changed by caching the last returned slice and the last inputs (e.g.,
last chartData reference, last dataStartIndex, last dataEndIndex) inside the
selector projector (or create a createSelectorCreator wrapper using
defaultMemoize + deep equality) and, before creating a new slice, compare
start/end and either the chartData reference or deep-equal the candidate slice
to the cached slice and return the cached slice when equal; update the projector
bodies of selectChartDataSliceIfNotInPanorama,
selectChartDataSliceIgnoringIndexes, and selectChartDataSliceWithIndexes to use
that cache logic.

@github-actions
Copy link
Copy Markdown
Contributor

Staging Deployment Details

These deployments will remain available for 30 days.

To update snapshots: Comment /update-snapshots on this PR to automatically update the baseline screenshots.

@ckifer
Copy link
Copy Markdown
Member

ckifer commented Mar 17, 2026

now we don't need to be embarassed to release it

lol

@ckifer ckifer merged commit b39edbf into main Mar 17, 2026
57 of 59 checks passed
@ckifer ckifer deleted the boxplot branch March 17, 2026 19:31
@PavelVanecek PavelVanecek mentioned this pull request Mar 22, 2026
PavelVanecek added a commit that referenced this pull request Mar 22, 2026
## Description

#7146 fixed a bug where the
XAxis was ignoring data defined in both chart and graphical item. One of
our stories did just that:
https://www.chromatic.com/test?appId=63da8268a0da9970db6992aa&id=69bf5ed7116eb6d0d637ed0d
so here is a fix.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
* Refreshed sample datasets in the Tooltip story to showcase updated
data structures.
  * Enhanced axis configuration examples in tooltip documentation.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants