Skip to content

Fix visual diff#6639

Merged
PavelVanecek merged 2 commits intomainfrom
fix-visual-diff
Nov 16, 2025
Merged

Fix visual diff#6639
PavelVanecek merged 2 commits intomainfrom
fix-visual-diff

Conversation

@PavelVanecek
Copy link
Collaborator

@PavelVanecek PavelVanecek commented Nov 16, 2025

Description

We had two visual diff tests fail: https://www.chromatic.com/build?appId=63da8268a0da9970db6992aa&number=3175

The first one was trivial: the default was declared after the explicit prop and was overwriting it.

Second was more complicated. I made a mistake in #6616 in the comparator logic: it was looking up a tooltip settings by reference, which it could never find because it had created a new reference in the same reducer, so the original dispatch lost access to it. This means that the tooltip state stopped updating. Which means yes it fixed the infinite loop but it also broke initializing the tooltip position. I fixed it so that it's still memoized but updating properly.

Related Issue

#6613

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

  • Refactor
    • Restructured tooltip configuration logic across chart components (Area, Bar, Funnel, Line, Scatter, Sankey, Sunburst, Treemap, Pie, Radar, and RadialBar).
    • Updated internal tooltip state handling for improved consistency.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 16, 2025

Walkthrough

The PR refactors tooltip configuration across multiple chart components from a function-based approach to a memoized component-based pattern. Each chart component replaces its getTooltipEntrySettings function with a dedicated memoized wrapper component. The core SetTooltipEntrySettings component is updated to accept pre-computed tooltipEntrySettings instead of generic function and arguments. Tooltip payload replacement logic is also modified to replace entire entries instead of selective field updates.

Changes

Cohort / File(s) Summary
Cartesian chart components
src/cartesian/Area.tsx, src/cartesian/Bar.tsx, src/cartesian/Line.tsx
Replace inline getTooltipEntrySettings functions with memoized SetAreaTooltipEntrySettings, SetBarTooltipEntrySettings, SetLineTooltipEntrySettings components; update render calls to pass individual props instead of fn/args pattern
Specialized cartesian components
src/cartesian/Funnel.tsx, src/cartesian/Scatter.tsx
Convert getTooltipEntrySettings helper to memoized SetFunnelTooltipEntrySettings and SetScatterTooltipEntrySettings components; remove standalone functions and wire props directly in render paths
Hierarchical chart components
src/chart/Sankey.tsx, src/chart/SunburstChart.tsx, src/chart/Treemap.tsx
Replace getTooltipEntrySettings functions with memoized tooltip components (SetSankeyTooltipEntrySettings, SetSunburstTooltipEntrySettings, SetTreemapTooltipEntrySettings); adjust prop forwarding patterns
Polar chart components
src/polar/Pie.tsx, src/polar/Radar.tsx, src/polar/RadialBar.tsx
Replace getTooltipEntrySettings functions with memoized components (SetPieTooltipEntrySettings, SetRadarTooltipEntrySettings, SetRadialBarTooltipEntrySettings); update component invocation patterns
Core tooltip state
src/state/SetTooltipEntrySettings.tsx
Change public signature from generic fn/args pattern to concrete tooltipEntrySettings: TooltipPayloadConfiguration prop; update useLayoutEffect dependencies
Tooltip reducer
src/state/tooltipSlice.ts
Modify replaceTooltipEntrySettings to replace entire payload entries instead of updating individual fields
Story updates
storybook/stories/API/component/LabelList.stories.tsx
Reorder args to place spread of getStoryArgsFromArgsTypesObject(LabelListProps) before explicit property overrides

Sequence Diagram

sequenceDiagram
    participant ComponentA as Chart Component
    participant OldPattern as SetTooltipEntrySettings<br/>(Old Pattern)
    participant NewPattern as SetAreaTooltipEntrySettings<br/>(New Pattern)
    participant Core as SetTooltipEntrySettings<br/>(Core)
    participant Redux as Redux Store

    rect rgb(240, 248, 255)
    Note over ComponentA,Redux: OLD: Function-based Composition
    ComponentA->>OldPattern: render with fn={getTooltipEntrySettings} args={props}
    OldPattern->>OldPattern: fn(args) computes tooltipEntrySettings
    OldPattern->>Core: <SetTooltipEntrySettings fn={fn} args={args} />
    Core->>Core: useLayoutEffect: fn(args) to compute
    Core->>Redux: dispatch setTooltipEntrySettings(computed)
    end

    rect rgb(255, 250, 240)
    Note over ComponentA,Redux: NEW: Component-based Composition
    ComponentA->>NewPattern: render with dataKey={...} stroke={...} etc
    NewPattern->>NewPattern: Compute tooltipEntrySettings
    NewPattern->>Core: <SetTooltipEntrySettings tooltipEntrySettings={config} />
    Core->>Redux: dispatch setTooltipEntrySettings(config)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Pattern consistency verification: Ensure all 11 chart components follow the same memoization and prop-forwarding pattern correctly
  • Core state change validation: Verify SetTooltipEntrySettings.tsx behavior with new prop signature and dependency array updates
  • Redux mutation logic: Confirm replaceTooltipEntrySettings wholesale replacement behaves identically to previous field updates
  • Type safety: Review Pick types and prop destructuring across all new component signatures for correctness

Possibly related PRs

Suggested reviewers

  • ckifer

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Fix visual diff' is vague and does not clearly convey the specific changes made or the core bug being fixed. Consider a more specific title like 'Fix tooltip state updates and prop override order' that better describes the key issues being addressed.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The PR description adequately covers the main issues, provides context from related issues, and documents testing. However, the 'Motivation and Context' and detailed 'How Has This Been Tested' sections could be more comprehensive.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-visual-diff

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (8)
src/cartesian/Funnel.tsx (1)

127-163: Funnel tooltip wiring looks good; consider aligning with resolved defaults

The new SetFunnelTooltipEntrySettings correctly captures dataDefinedOnItem, per-trapezoid positions, and the expected settings fields, and using trapezoids for positions is a nice improvement.

One minor thing to consider: at the call site you pass props.stroke, props.fill, and props.hide, while the rendered FunnelWithState uses the values from resolveDefaultProps. If callers omit these props and rely on defaultFunnelProps, the tooltip configuration will see stroke/fill/hide as undefined, so tooltip color/hidden state might not fully mirror the actual funnel appearance. If you want them to stay in sync even when only defaults are used, you could pass the resolved stroke/fill/hide values into SetFunnelTooltipEntrySettings instead.

Also applies to: 385-400, 439-450

src/polar/Pie.tsx (1)

261-294: LGTM! Consider adding displayName for easier debugging.

The memoized component pattern correctly fixes the tooltip state bug by ensuring stable references. The component will only re-render when its props change, creating a new tooltipEntrySettings object that triggers the reference-based comparison in SetTooltipEntrySettings.

Consider adding a displayName to improve the debugging experience:

 const SetPieTooltipEntrySettings = React.memo(
   ({
     dataKey,
     nameKey,
     sectors,
     stroke,
     strokeWidth,
     fill,
     name,
     hide,
     tooltipType,
   }: Pick<
     InternalProps,
     'dataKey' | 'nameKey' | 'sectors' | 'stroke' | 'strokeWidth' | 'fill' | 'name' | 'hide' | 'tooltipType'
   >) => {
     const tooltipEntrySettings: TooltipPayloadConfiguration = {
       dataDefinedOnItem: sectors.map((p: PieSectorDataItem) => p.tooltipPayload),
       positions: sectors.map((p: PieSectorDataItem) => p.tooltipPosition),
       settings: {
         stroke,
         strokeWidth,
         fill,
         dataKey,
         nameKey,
         name: getTooltipNameProp(name, dataKey),
         hide,
         type: tooltipType,
         color: fill,
         unit: '', // why doesn't Pie support unit?
       },
     };
     return <SetTooltipEntrySettings tooltipEntrySettings={tooltipEntrySettings} />;
   },
 );
+SetPieTooltipEntrySettings.displayName = 'SetPieTooltipEntrySettings';
src/chart/Sankey.tsx (1)

443-471: LGTM! Consistent with the refactoring pattern.

The implementation correctly follows the same memoized component pattern as other chart components. Consider adding displayName for debugging consistency across the codebase.

 const SetSankeyTooltipEntrySettings = React.memo(
   ({
     dataKey,
     nameKey,
     stroke,
     strokeWidth,
     fill,
     name,
     data,
   }: Pick<Props, 'dataKey' | 'nameKey' | 'stroke' | 'strokeWidth' | 'fill' | 'name' | 'data'>) => {
     const tooltipEntrySettings: TooltipPayloadConfiguration = {
       dataDefinedOnItem: data,
       positions: undefined,
       settings: {
         stroke,
         strokeWidth,
         fill,
         dataKey,
         name,
         nameKey,
         hide: false,
         type: undefined,
         color: fill,
         unit: '',
       },
     };
     return <SetTooltipEntrySettings tooltipEntrySettings={tooltipEntrySettings} />;
   },
 );
+SetSankeyTooltipEntrySettings.displayName = 'SetSankeyTooltipEntrySettings';
src/cartesian/Line.tsx (1)

176-209: LGTM! Consistent refactoring pattern.

The Line component correctly implements the memoized tooltip configuration pattern. Consider adding displayName for consistency with other components.

+SetLineTooltipEntrySettings.displayName = 'SetLineTooltipEntrySettings';
src/cartesian/Scatter.tsx (1)

218-258: LGTM! Well-structured with clear type definitions.

The Scatter component introduces a dedicated type InputRequiredToComputeTooltipEntrySettings which improves type clarity. The comment on Line 253 about unit support is helpful. Consider adding displayName for debugging.

+SetScatterTooltipEntrySettings.displayName = 'SetScatterTooltipEntrySettings';
src/chart/Treemap.tsx (1)

544-570: LGTM! Correctly handles Treemap's stateful nature.

The component appropriately accepts currentRoot from Treemap's component state, ensuring tooltip configuration updates when navigation changes. The TODO comment on Line 554 provides helpful context. Consider adding displayName.

+SetTreemapTooltipEntrySettings.displayName = 'SetTreemapTooltipEntrySettings';
src/chart/SunburstChart.tsx (1)

126-158: LGTM! Correctly handles Map serialization.

The conversion from Map to Record on Line 140 is necessary for Redux store compatibility, and the comment clearly explains this. The implementation follows the established pattern. Consider adding displayName.

+SetSunburstTooltipEntrySettings.displayName = 'SetSunburstTooltipEntrySettings';
src/cartesian/Area.tsx (1)

195-228: LGTM! Completes the consistent refactoring pattern.

The Area component correctly implements the memoized tooltip configuration, completing the PR-wide refactoring. Consider adding displayName for debugging consistency.

+SetAreaTooltipEntrySettings.displayName = 'SetAreaTooltipEntrySettings';
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e95a710 and ba62904.

📒 Files selected for processing (14)
  • src/cartesian/Area.tsx (2 hunks)
  • src/cartesian/Bar.tsx (2 hunks)
  • src/cartesian/Funnel.tsx (2 hunks)
  • src/cartesian/Line.tsx (2 hunks)
  • src/cartesian/Scatter.tsx (3 hunks)
  • src/chart/Sankey.tsx (3 hunks)
  • src/chart/SunburstChart.tsx (2 hunks)
  • src/chart/Treemap.tsx (2 hunks)
  • src/polar/Pie.tsx (2 hunks)
  • src/polar/Radar.tsx (2 hunks)
  • src/polar/RadialBar.tsx (2 hunks)
  • src/state/SetTooltipEntrySettings.tsx (2 hunks)
  • src/state/tooltipSlice.ts (1 hunks)
  • storybook/stories/API/component/LabelList.stories.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (13)
src/cartesian/Area.tsx (3)
src/state/tooltipSlice.ts (1)
  • TooltipPayloadConfiguration (52-69)
src/util/ChartUtils.ts (1)
  • getTooltipNameProp (642-653)
src/state/SetTooltipEntrySettings.tsx (1)
  • SetTooltipEntrySettings (11-43)
src/state/SetTooltipEntrySettings.tsx (1)
src/state/tooltipSlice.ts (1)
  • TooltipPayloadConfiguration (52-69)
src/chart/Sankey.tsx (2)
src/state/tooltipSlice.ts (1)
  • TooltipPayloadConfiguration (52-69)
src/state/SetTooltipEntrySettings.tsx (1)
  • SetTooltipEntrySettings (11-43)
src/polar/Radar.tsx (3)
src/state/tooltipSlice.ts (1)
  • TooltipPayloadConfiguration (52-69)
src/util/ChartUtils.ts (1)
  • getTooltipNameProp (642-653)
src/state/SetTooltipEntrySettings.tsx (1)
  • SetTooltipEntrySettings (11-43)
src/cartesian/Funnel.tsx (2)
src/state/tooltipSlice.ts (1)
  • TooltipPayloadConfiguration (52-69)
src/state/SetTooltipEntrySettings.tsx (1)
  • SetTooltipEntrySettings (11-43)
src/chart/Treemap.tsx (2)
src/state/tooltipSlice.ts (1)
  • TooltipPayloadConfiguration (52-69)
src/state/SetTooltipEntrySettings.tsx (1)
  • SetTooltipEntrySettings (11-43)
src/cartesian/Line.tsx (3)
src/state/tooltipSlice.ts (1)
  • TooltipPayloadConfiguration (52-69)
src/util/ChartUtils.ts (1)
  • getTooltipNameProp (642-653)
src/state/SetTooltipEntrySettings.tsx (1)
  • SetTooltipEntrySettings (11-43)
storybook/stories/API/component/LabelList.stories.tsx (2)
storybook/stories/API/props/utils.ts (1)
  • getStoryArgsFromArgsTypesObject (4-15)
storybook/stories/API/props/LabelListProps.ts (1)
  • LabelListProps (44-110)
src/polar/RadialBar.tsx (3)
src/state/tooltipSlice.ts (1)
  • TooltipPayloadConfiguration (52-69)
src/util/ChartUtils.ts (1)
  • getTooltipNameProp (642-653)
src/state/SetTooltipEntrySettings.tsx (1)
  • SetTooltipEntrySettings (11-43)
src/polar/Pie.tsx (3)
src/state/tooltipSlice.ts (1)
  • TooltipPayloadConfiguration (52-69)
src/util/ChartUtils.ts (1)
  • getTooltipNameProp (642-653)
src/state/SetTooltipEntrySettings.tsx (1)
  • SetTooltipEntrySettings (11-43)
src/cartesian/Scatter.tsx (4)
src/util/types.ts (2)
  • DataKey (59-59)
  • TooltipType (80-80)
src/state/tooltipSlice.ts (1)
  • TooltipPayloadConfiguration (52-69)
src/util/ChartUtils.ts (1)
  • getTooltipNameProp (642-653)
src/state/SetTooltipEntrySettings.tsx (1)
  • SetTooltipEntrySettings (11-43)
src/chart/SunburstChart.tsx (2)
src/state/tooltipSlice.ts (1)
  • TooltipPayloadConfiguration (52-69)
src/state/SetTooltipEntrySettings.tsx (1)
  • SetTooltipEntrySettings (11-43)
src/cartesian/Bar.tsx (3)
src/state/tooltipSlice.ts (1)
  • TooltipPayloadConfiguration (52-69)
src/util/ChartUtils.ts (1)
  • getTooltipNameProp (642-653)
src/state/SetTooltipEntrySettings.tsx (1)
  • SetTooltipEntrySettings (11-43)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build, Test, Pack
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (6)
storybook/stories/API/component/LabelList.stories.tsx (1)

39-44: LGTM! Correct fix for property override order.

The spread operator is now correctly positioned before the explicit property assignments, ensuring that dataKey: 'uv' and position: 'top' take precedence over any defaults from getStoryArgsFromArgsTypesObject(LabelListProps). This fixes the bug where defaults were overwriting explicit values.

src/polar/Radar.tsx (1)

122-156: Radar tooltip settings component is consistent and correctly wired

The memoized SetRadarTooltipEntrySettings builds a TooltipPayloadConfiguration that aligns with legend naming/color logic and avoids item-level positions for now, and it’s fed with fully resolved props in Radar, so explicit props take precedence over defaults as expected. No issues spotted.

Also applies to: 545-553

src/polar/RadialBar.tsx (1)

319-351: RadialBar tooltip configuration matches rendered data and styling

Using SetRadialBarTooltipEntrySettings with selector-derived data and resolved stroke/fill/name gives a coherent tooltip configuration that mirrors the actual sectors, without changing external API. Looks good.

Also applies to: 437-446

src/state/tooltipSlice.ts (1)

275-280: Replacing the whole tooltip payload entry is the right fix

Switching to state.tooltipItemPayloads[index] = castDraft(next); keeps the index stable but updates the object reference, which addresses reference-based comparator issues without changing overall state shape. The guard on index > -1 still protects against missing entries. This looks correct.

src/cartesian/Bar.tsx (1)

227-256: Bar tooltip entry settings are correctly encapsulated and passed

The new SetBarTooltipEntrySettings encapsulates the previous tooltip configuration into a memoized component, using the same stroke/fill/name/unit fields and feeding it with resolved props from BarFn. This should preserve existing tooltip behavior while integrating cleanly with the new SetTooltipEntrySettings flow.

Also applies to: 877-886

src/state/SetTooltipEntrySettings.tsx (1)

11-31: LGTM! This is the core fix for the tooltip state bug.

The refactored signature accepting tooltipEntrySettings directly, combined with reference-based comparison on Line 27, correctly fixes the comparator issue mentioned in the PR objectives. The memoized wrapper components in chart files ensure that tooltipEntrySettings references remain stable unless input props change, preventing unnecessary updates while still detecting legitimate changes.

@codecov
Copy link

codecov bot commented Nov 16, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.17%. Comparing base (e95a710) to head (ba62904).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6639      +/-   ##
==========================================
+ Coverage   94.15%   94.17%   +0.02%     
==========================================
  Files         494      494              
  Lines       41458    41666     +208     
  Branches     4826     4824       -2     
==========================================
+ Hits        39033    39241     +208     
  Misses       2420     2420              
  Partials        5        5              

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

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

@codecov
Copy link

codecov bot commented Nov 16, 2025

Bundle Report

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

Detailed changes
Bundle name Size Change
recharts/bundle-cjs 1.13MB 3.05kB (0.27%) ⬆️
recharts/bundle-es6 978.76kB 3.08kB (0.32%) ⬆️
recharts/bundle-umd 513.57kB 1.51kB (0.3%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: recharts/bundle-es6

Assets Changed:

Asset Name Size Change Total Size Change (%)
chart/Treemap.js 196 bytes 26.82kB 0.74%
cartesian/Area.js 372 bytes 25.75kB 1.47%
chart/Sankey.js 320 bytes 25.72kB 1.26%
cartesian/Bar.js 342 bytes 25.32kB 1.37%
polar/Pie.js 304 bytes 23.48kB 1.31%
cartesian/Line.js 361 bytes 23.31kB 1.57%
cartesian/Scatter.js 283 bytes 22.34kB 1.28%
polar/RadialBar.js 285 bytes 19.2kB 1.51%
cartesian/Funnel.js 345 bytes 16.53kB 2.13%
polar/Radar.js 319 bytes 15.9kB 2.05%
chart/SunburstChart.js 169 bytes 11.0kB 1.56%
state/tooltipSlice.js -198 bytes 7.2kB -2.68%
state/SetTooltipEntrySettings.js -21 bytes 1.27kB -1.63%
view changes for bundle: recharts/bundle-cjs

Assets Changed:

Asset Name Size Change Total Size Change (%)
chart/Treemap.js 196 bytes 28.43kB 0.69%
cartesian/Area.js 372 bytes 27.61kB 1.37%
chart/Sankey.js 320 bytes 27.35kB 1.18%
cartesian/Bar.js 342 bytes 26.96kB 1.28%
polar/Pie.js 304 bytes 25.19kB 1.22%
cartesian/Line.js 361 bytes 24.82kB 1.48%
cartesian/Scatter.js 283 bytes 23.91kB 1.2%
polar/RadialBar.js 285 bytes 20.59kB 1.4%
cartesian/Funnel.js 345 bytes 18.02kB 1.95%
polar/Radar.js 319 bytes 17.32kB 1.88%
chart/SunburstChart.js 169 bytes 12.27kB 1.4%
state/tooltipSlice.js -222 bytes 8.57kB -2.53%
state/SetTooltipEntrySettings.js -21 bytes 1.44kB -1.44%
view changes for bundle: recharts/bundle-umd

Assets Changed:

Asset Name Size Change Total Size Change (%)
Recharts.js 1.51kB 513.57kB 0.3%

@PavelVanecek PavelVanecek merged commit 7d92152 into main Nov 16, 2025
28 of 29 checks passed
@PavelVanecek PavelVanecek deleted the fix-visual-diff branch November 16, 2025 08:40
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.

2 participants