Skip to content

Fix/per graphical item formatter prop#7287

Merged
PavelVanecek merged 2 commits into
recharts:mainfrom
shreedharbhat98:fix/per-graphical-item-formatter-prop
Jun 29, 2026
Merged

Fix/per graphical item formatter prop#7287
PavelVanecek merged 2 commits into
recharts:mainfrom
shreedharbhat98:fix/per-graphical-item-formatter-prop

Conversation

@shreedharbhat98

@shreedharbhat98 shreedharbhat98 commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

Description

Restores the ability to pass a formatter prop directly to graphical chart components (Line, Bar, Area,
Scatter, Funnel, Pie, RadialBar) to format tooltip values on a per-series basis.

Each component now accepts a formatter prop of type Formatter. When set, it takes precedence over the formatter
prop defined on the <Tooltip> component, following the existing entry.formatter || formatter || defaultFormatter
chain already present in DefaultTooltipContent.

Related Issue

Fixes #6210

Motivation and Context

In v2, users could co-locate tooltip formatting logic with each graphical item, which was ergonomic when charts had
multiple series each requiring different formatting (e.g. currency, percentages, counts). This was silently dropped in
v3 when the Redux-based tooltip state was introduced. The DefaultTooltipContent still checked entry.formatter at
line 137, but it was never being populated from the graphical item's props.

This change is purely additive — no existing API is changed or removed.

How Has This Been Tested?

  • Added test/component/Tooltip/graphicalItem.formatter.spec.tsx with 7 unit tests covering:
    • formatter is stored in the Redux tooltip payload entry for Bar and Line
    • The formatted value is rendered in the tooltip DOM
    • Per-item formatter takes precedence over the Tooltip-level formatter
    • Mixed scenario: two Bars where only one has formatter — only that entry is transformed
  • Updated omnidoc/readProject.spec.ts inline snapshots to include the new formatter prop
  • Added a commentSimilarityExceptions entry for Label/LabelList whose formatter prop serves a different purpose
    (SVG text serialisation)
  • All existing tooltip tests continue to pass (721 tests)
  • TypeScript type-checking passes across lib, test, storybook, and website

Screenshots (if appropriate)

N/A — tooltip rendering is covered by unit tests.

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. (JSDoc added to each prop; run npm run omnidoc to regenerate API
    docs)
  • 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

New Features

  • Added optional formatter prop to chart components (Area, Bar, Funnel, Line, Scatter, Pie, RadialBar) for per-component tooltip value formatting.
  • Component-level formatters take precedence over the Tooltip component's formatter prop.

Tests

  • Added comprehensive test suite verifying per-component formatter functionality and precedence behavior in tooltip rendering.

…charts#6210)

Re-adds the ability to pass a `formatter` prop directly to graphical
chart components (Line, Bar, Area, Scatter, Funnel, Pie, RadialBar) to
format tooltip values per series. This was available in v2 but was
silently dropped when Redux-based tooltip state was introduced in v3.

The per-item formatter takes precedence over the Tooltip-level formatter,
preserving the existing `entry.formatter || formatter || defaultFormatter`
chain in DefaultTooltipContent.
- Update readProject inline snapshots to include new `formatter` prop on Bar
- Add commentSimilarityExceptions entry for Label/LabelList `formatter`,
  which serializes SVG text (distinct from graphical item tooltip formatters)
@coderabbitai

coderabbitai Bot commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

The PR adds an optional formatter prop to seven chart components (Area, Bar, Funnel, Line, Scatter, Pie, RadialBar), enabling per-graphical-item tooltip value formatting. Each component's formatter is wired into its tooltip entry settings with precedence over the Tooltip component's formatter. A comprehensive test suite validates the functionality and prop precedence behavior.

Changes

Cohort / File(s) Summary
Comment Exception Configuration
omnidoc/commentSimilarityExceptions.ts
Added comment similarity exception rule for Label and LabelList's formatter prop to mark JSDoc differences intentional (SVG text serialization behavior).
Cartesian Components
src/cartesian/Area.tsx, src/cartesian/Bar.tsx, src/cartesian/Funnel.tsx, src/cartesian/Line.tsx, src/cartesian/Scatter.tsx
Added optional formatter?: Formatter prop to public component interfaces (AreaProps, BarProps, FunnelProps, LineProps, ScatterProps); wired formatter into respective tooltip entry settings (e.g., SetAreaTooltipEntrySettings, SetBarTooltipEntrySettings) to enable per-component tooltip value formatting.
Polar Components
src/polar/Pie.tsx, src/polar/RadialBar.tsx
Added optional formatter?: Formatter prop to PieProps and RadialBarProps; integrated formatter into SetPieTooltipEntrySettings and SetRadialBarTooltipEntrySettings respectively for per-component tooltip formatting.
Tests & Snapshots
test/component/Tooltip/graphicalItem.formatter.spec.tsx, omnidoc/readProject.spec.ts
Introduced comprehensive test suite validating per-graphical-item formatter functionality, formatter precedence (component-level over Tooltip-level), and mixed item scenarios; updated snapshot to include "formatter" prop in Bar-related prop lists.

Sequence Diagram

sequenceDiagram
    participant Component as Chart Component<br/>(Area, Bar, etc.)
    participant Settings as Tooltip Settings<br/>Builder
    participant Payload as Tooltip Payload<br/>Configuration
    participant Tooltip as DefaultTooltipContent

    Component->>Settings: Pass formatter prop to<br/>SetComponentTooltipEntrySettings()
    Settings->>Payload: Store formatter in<br/>TooltipPayloadConfiguration.settings
    Tooltip->>Payload: Read formatter from<br/>entry.settings.formatter
    Tooltip->>Tooltip: Apply formatter if present<br/>(precedence over Tooltip.formatter)
    Tooltip-->>Component: Render formatted value
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • PavelVanecek
🚥 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 'Fix/per graphical item formatter prop' is clear and directly describes the main change: adding per-graphical-item formatter prop support to chart components.
Description check ✅ Passed The PR description is comprehensive, covering all required sections from the template including description, related issue, motivation, testing approach, types of changes, and checklist items.
Linked Issues check ✅ Passed The PR successfully implements all objectives from issue #6210: restores formatter prop to graphical components (Line, Bar, Area, Scatter, Funnel, Pie, RadialBar), ensures per-item precedence over Tooltip-level formatter, and includes comprehensive tests verifying the functionality.
Out of Scope Changes check ✅ Passed All changes are in-scope and directly related to #6210: component prop additions, tooltip integration, tests, and documentation updates align with restoring per-graphical-item formatter functionality.

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

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

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

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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.

🧹 Nitpick comments (3)
src/polar/Pie.tsx (1)

356-360: 🏗️ Heavy lift

Add direct tests for Pie/Funnel/Scatter/RadialBar formatter paths

The formatter wiring is added across multiple components, but the current test coverage described in this PR validates only Bar/Line behavior. Please add component-level assertions (or a shared parametrized suite) for Pie/Funnel/Scatter/RadialBar tooltip payload + rendered value paths to prevent regressions in component-specific payload shapes.

As per coding guidelines src/**/*.{ts,tsx}: Aim for 100% unit test code coverage when writing new code.

Also applies to: 424-487, 1012-1025

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

In `@src/polar/Pie.tsx` around lines 356 - 360, Add unit tests that cover the
tooltip formatter wiring for Pie, Funnel, Scatter and RadialBar components: for
each component (Pie component's formatter prop, Funnel, Scatter, RadialBar)
write tests that render the component with a mock Tooltip.formatter function and
assert both the payload shape passed into the formatter and the final rendered
tooltip value; you can implement a shared parametrized test suite that iterates
over component constructors/names and checks (1) that the formatter is called
with the expected payload shape and fields, and (2) that the formatted string is
rendered in the Tooltip output, targeting the Tooltip component’s formatter prop
used by each component to prevent regressions in component-specific payload
shapes.
test/component/Tooltip/graphicalItem.formatter.spec.tsx (2)

34-42: ⚡ Quick win

Assert selector call counts for createSelectorTestCase spy.

Please add spy call-count checks in selector-based tests to catch accidental extra rerenders in tooltip state selection.

Based on learnings: "Applies to test/**/*.{test,spec}.{ts,tsx} : Verify the number of selector calls using the spy object from createSelectorTestCase to spot unnecessary re-renders and improve performance."

Also applies to: 95-101, 117-125

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

In `@test/component/Tooltip/graphicalItem.formatter.spec.tsx` around lines 34 -
42, Add assertions that verify the spy returned from createSelectorTestCase (the
spy variable used in the selector-based tests) was called the expected number of
times to catch extra selector invocations; for example, after rendering in the
test that calls renderTestCase(state => selectTooltipPayload(...)) add an
assertion like expect(spy).toHaveBeenCalledTimes(1) (or the correct expected
count) immediately before or after the existing expectLastCalledWith check, and
apply the same pattern to the other selector-based tests that use the spy so
each test asserts the spy call count.

16-134: ⚡ Quick win

Add at least one Area formatter test in this suite.

This suite validates Bar/Line well, but the PR also wires formatter into Area; adding one payload+render assertion for Area would close the regression gap on changed source behavior.

As per coding guidelines "src/**/*.{ts,tsx}: Aim for 100% unit test code coverage when writing new code."

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

In `@test/component/Tooltip/graphicalItem.formatter.spec.tsx` around lines 16 -
134, Add a new test block mirroring the existing "Line with formatter" or "Bar
with formatter" cases but using Area/AreaChart: create an areaFormatter vi.fn(()
=> 'AREA_FORMATTED'), render via createSelectorTestCase with <AreaChart
...><Tooltip /><Area dataKey="pv" id="area-pv" formatter={areaFormatter}
/></AreaChart>, then (1) assert selectTooltipPayload(state, 'axis', 'hover',
'1') contains an entry with dataKey: 'pv' and formatter: areaFormatter and (2)
render and use showTooltip with areaChartMouseHoverTooltipSelector and
expectTooltipPayload to show 'pv : AREA_FORMATTED'; reference Area, AreaChart,
areaFormatter, selectTooltipPayload, createSelectorTestCase, showTooltip,
expectTooltipPayload.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/polar/Pie.tsx`:
- Around line 356-360: Add unit tests that cover the tooltip formatter wiring
for Pie, Funnel, Scatter and RadialBar components: for each component (Pie
component's formatter prop, Funnel, Scatter, RadialBar) write tests that render
the component with a mock Tooltip.formatter function and assert both the payload
shape passed into the formatter and the final rendered tooltip value; you can
implement a shared parametrized test suite that iterates over component
constructors/names and checks (1) that the formatter is called with the expected
payload shape and fields, and (2) that the formatted string is rendered in the
Tooltip output, targeting the Tooltip component’s formatter prop used by each
component to prevent regressions in component-specific payload shapes.

In `@test/component/Tooltip/graphicalItem.formatter.spec.tsx`:
- Around line 34-42: Add assertions that verify the spy returned from
createSelectorTestCase (the spy variable used in the selector-based tests) was
called the expected number of times to catch extra selector invocations; for
example, after rendering in the test that calls renderTestCase(state =>
selectTooltipPayload(...)) add an assertion like
expect(spy).toHaveBeenCalledTimes(1) (or the correct expected count) immediately
before or after the existing expectLastCalledWith check, and apply the same
pattern to the other selector-based tests that use the spy so each test asserts
the spy call count.
- Around line 16-134: Add a new test block mirroring the existing "Line with
formatter" or "Bar with formatter" cases but using Area/AreaChart: create an
areaFormatter vi.fn(() => 'AREA_FORMATTED'), render via createSelectorTestCase
with <AreaChart ...><Tooltip /><Area dataKey="pv" id="area-pv"
formatter={areaFormatter} /></AreaChart>, then (1) assert
selectTooltipPayload(state, 'axis', 'hover', '1') contains an entry with
dataKey: 'pv' and formatter: areaFormatter and (2) render and use showTooltip
with areaChartMouseHoverTooltipSelector and expectTooltipPayload to show 'pv :
AREA_FORMATTED'; reference Area, AreaChart, areaFormatter, selectTooltipPayload,
createSelectorTestCase, showTooltip, expectTooltipPayload.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c4aaa59b-3191-48eb-9688-bc8f838477f5

📥 Commits

Reviewing files that changed from the base of the PR and between 3b91255 and 2424117.

📒 Files selected for processing (10)
  • omnidoc/commentSimilarityExceptions.ts
  • omnidoc/readProject.spec.ts
  • src/cartesian/Area.tsx
  • src/cartesian/Bar.tsx
  • src/cartesian/Funnel.tsx
  • src/cartesian/Line.tsx
  • src/cartesian/Scatter.tsx
  • src/polar/Pie.tsx
  • src/polar/RadialBar.tsx
  • test/component/Tooltip/graphicalItem.formatter.spec.tsx

@codecov

codecov Bot commented Apr 30, 2026

Copy link
Copy Markdown

Bundle Report

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

Detailed changes
Bundle name Size Change
recharts/bundle-cjs 1.31MB 454 bytes (0.03%) ⬆️
recharts/bundle-es6 1.13MB 454 bytes (0.04%) ⬆️
recharts/bundle-umd 552.13kB 322 bytes (0.06%) ⬆️
recharts/bundle-treeshaking-cartesian 645.79kB 320 bytes (0.05%) ⬆️
recharts/bundle-treeshaking-polar 449.27kB 134 bytes (0.03%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: recharts/bundle-umd

Assets Changed:

Asset Name Size Change Total Size Change (%)
Recharts.js 322 bytes 552.13kB 0.06%
view changes for bundle: recharts/bundle-treeshaking-polar

Assets Changed:

Asset Name Size Change Total Size Change (%)
bundle.js 134 bytes 449.27kB 0.03%
view changes for bundle: recharts/bundle-cjs

Assets Changed:

Asset Name Size Change Total Size Change (%)
cartesian/Area.js 64 bytes 29.24kB 0.22%
cartesian/Bar.js 64 bytes 29.03kB 0.22%
cartesian/Line.js 64 bytes 28.5kB 0.23%
polar/Pie.js 64 bytes 28.31kB 0.23%
cartesian/Scatter.js 64 bytes 24.41kB 0.26%
polar/RadialBar.js 70 bytes 21.38kB 0.33%
cartesian/Funnel.js 64 bytes 19.17kB 0.34%
view changes for bundle: recharts/bundle-es6

Assets Changed:

Asset Name Size Change Total Size Change (%)
cartesian/Area.js 64 bytes 27.4kB 0.23%
cartesian/Bar.js 64 bytes 27.28kB 0.24%
cartesian/Line.js 64 bytes 26.89kB 0.24%
polar/Pie.js 64 bytes 26.5kB 0.24%
cartesian/Scatter.js 64 bytes 22.77kB 0.28%
polar/RadialBar.js 70 bytes 19.93kB 0.35%
cartesian/Funnel.js 64 bytes 17.68kB 0.36%
view changes for bundle: recharts/bundle-treeshaking-cartesian

Assets Changed:

Asset Name Size Change Total Size Change (%)
bundle.js 320 bytes 645.79kB 0.05%

@codecov

codecov Bot commented Apr 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.05%. Comparing base (3b91255) to head (2424117).
⚠️ Report is 139 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #7287   +/-   ##
=======================================
  Coverage   89.05%   89.05%           
=======================================
  Files         541      541           
  Lines       41088    41109   +21     
  Branches     5563     5564    +1     
=======================================
+ Hits        36589    36610   +21     
  Misses       4491     4491           
  Partials        8        8           

☔ View full report in Codecov by Harness.
📢 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.

@shreedharbhat98

Copy link
Copy Markdown
Contributor Author

@ckifer Would you mind taking a loot at this PR please?

Thanks in advance!

@ckifer ckifer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It looks ok to me at a glance, can you add tests for more than just bar please? You should be able to do the same tests in an it.each

cc @PavelVanecek to review before merging as well. I'm just looking from my phone which isn't a great place to review PRs 😅

@shreedharbhat98

Copy link
Copy Markdown
Contributor Author

It looks ok to me at a glance, can you add tests for more than just bar please? You should be able to do the same tests in an it.each

cc @PavelVanecek to review before merging as well. I'm just looking from my phone which isn't a great place to review PRs 😅

Happy to!

components: ['Label', 'LabelList'],
props: ['formatter'],
reason:
'Label and LabelList formatter serializes content for SVG text rendering, unlike graphical item formatters which format tooltip values',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Does it really? Or is this hallucination?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

lol I didn't read the docs 🤦

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The reason string is accurate in intent but "serializes" is slightly imprecise — it really just transforms the value.
The Label/LabelList formatter has the signature:

  type LabelFormatter = (label: RenderableText) => RenderableText;

It takes a raw value and returns something renderable inside an SVG element — typically a formatted string like 4000 → "4.0k". It never touches the tooltip.

The Bar/Line formatter on the other hand has a much richer signature:

  (value, name, entry, index, payload) => ReactNode

and formats values shown in the HTML tooltip overlay.
Same prop name, completely different context and signature — hence the exception. Happy to update the wording to may be "transforms the label value for SVG text display" if that reads clearer.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@PavelVanecek Just bumping this up incase if you have missed it!

@PavelVanecek
PavelVanecek merged commit 2db6ca5 into recharts:main Jun 29, 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.

v3 removed the ability to pass custom tooltip formatter through chart components

4 participants