Skip to content

fix: sync pie tooltip/legend color with per-sector fill#6977

Merged
PavelVanecek merged 14 commits intorecharts:mainfrom
2YH02:fix/6966
Feb 15, 2026
Merged

fix: sync pie tooltip/legend color with per-sector fill#6977
PavelVanecek merged 14 commits intorecharts:mainfrom
2YH02:fix/6966

Conversation

@2YH02
Copy link
Contributor

@2YH02 2YH02 commented Feb 9, 2026

Description

Fixes Pie tooltip/legend color sync when sectors are customized via shape, by preserving per-sector color information in Pie tooltip payload flow.

  • Adds per-sector color/fill to Pie tooltip payload entries.
  • Marks Pie tooltip settings with chartType: 'pie'.
  • Preserves color/fill for Pie payload items in tooltip payload combiner (keeps existing behavior for non-Pie chart types).
  • Adds/updates tests and a Storybook example for verification.

Related Issue

Fixes #6966

Fixes #6957

Motivation and Context

When Pie sectors are customized with shape, visual sector colors can diverge from tooltip/legend color metadata.
This change ensures Pie payload carries per-sector color data and that tooltip payload combining does not discard it for Pie, improving color consistency.

How Has This Been Tested?

Ran targeted tests locally:

  • npm run test -- test/polar/Pie/Pie.spec.tsx
  • npm run test -- test/state/selectors/pieSelectors.spec.tsx
  • npm run test -- test/component/Tooltip/defaultIndex.spec.tsx
  • npm run test -- test/component/Tooltip/itemSorter.spec.tsx
  • npm run test -- test/state/selectors/selectors.spec.tsx
  • npm run test -- test/component/Tooltip/tooltipEventType.spec.tsx
  • npm run test -- test/component/Tooltip/Tooltip.payload.spec.tsx
  • npm run check-types-storybook
  • npm run lint

Also added a Storybook example:

  • storybook/stories/Examples/Pie/PieColorSync.stories.tsx

Screenshots (if appropriate):

pieChart.mov

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

  • New Features

    • Pie chart tooltips now show per-sector color/fill and honor the active sector’s fill for tooltip items.
    • New Storybook example demonstrating custom sector coloring synced with tooltips.
  • Improvements

    • Tooltip entries preserve and propagate sector color/fill (with a gray fallback when unspecified) for consistent visuals.
    • Tooltip payload shape standardized to always include color and fill.
  • Tests

    • Updated tests to assert color and fill are present in tooltip payloads.


if (Array.isArray(tooltipPayload)) {
tooltipPayload.forEach(item => {
const preserveItemColor = settings?.chartType === 'pie';
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why only Pie? What happens if we allow this for all charts?

Copy link
Member

Choose a reason for hiding this comment

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

+1

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I originally scoped this to Pie to minimize behavior changes, since the issue report was Pie-specific.
That said, preserving color/fill for all chart types may be better for consistency.

I tried the global version (preserving item.color/item.fill for all charts) and reran related tooltip/selector tests, and they all passed.

If you prefer, I can update this PR either way:

  1. Keep Pie-only scope (minimal change)
  2. Apply globally across chart types (consistency-first)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's try all charts. Unit tests will pass either way because we don't test colour in unit tests. VR tests will show the difference.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated based on your feedback:
I removed the Pie-only check.

Now combineTooltipPayload keeps each item's color/fill for array payloads in all chart types.

I also updated the related tooltip test expectations.

Copy link
Contributor Author

@2YH02 2YH02 Feb 12, 2026

Choose a reason for hiding this comment

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

@PavelVanecek
Follow-up update:
I kept item-level color/fill for array payloads, and added fallback behavior when item values are missing:

  • color: item.color ?? settings?.color
  • fill: item.fill ?? settings?.fill

This keeps the all-charts behavior while preserving stable existing defaults. (commit: de86534)

I reran VR tests, and diffs appear across multiple chart examples (not only Pie), which is consistent with applying this tooltip color/fill behavior across chart types.

@codecov
Copy link

codecov bot commented Feb 11, 2026

Codecov Report

❌ Patch coverage is 87.77778% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.09%. Comparing base (fb0cd6b) to head (ae10b47).
⚠️ Report is 21 commits behind head on main.

Files with missing lines Patch % Lines
...state/selectors/combiners/combineTooltipPayload.ts 81.03% 11 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6977      +/-   ##
==========================================
- Coverage   90.45%   90.09%   -0.37%     
==========================================
  Files         517      523       +6     
  Lines       38618    39002     +384     
  Branches     5347     5388      +41     
==========================================
+ Hits        34933    35137     +204     
- Misses       3676     3856     +180     
  Partials        9        9              

☔ 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 Feb 11, 2026

Bundle Report

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

Detailed changes
Bundle name Size Change
recharts/bundle-cjs 1.26MB 2.87kB (0.23%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: recharts/bundle-cjs

Assets Changed:

Asset Name Size Change Total Size Change (%)
polar/Pie.js 1.44kB 27.46kB 5.54% ⚠️
hooks.js 140 bytes 23.59kB 0.6%
polar/PolarAngleAxis.js 160 bytes 12.81kB 1.27%
state/selectors/combiners/combineTooltipPayload.js 1.12kB 8.46kB 15.33% ⚠️

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 12, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Pie now threads per-sector and activeShape fill/colors into tooltip payloads by deriving sectorColor during sector computation and passing activeShape into tooltip entry settings so tooltip items include color and fill fields.

Changes

Cohort / File(s) Summary
Pie component & tooltip payload shaping
src/polar/Pie.tsx
Add getActiveShapeFill; derive sectorColor from entry .fill or pieSettings.fill; forward activeShape to tooltip settings and include color/fill on sector tooltipPayload items.
Tooltip payload combiner
src/state/selectors/combiners/combineTooltipPayload.ts
Introduce normalized TooltipPayloadItemLike parsing and preserve per-item color/fill (fallback to settings) when constructing tooltip entries.
Storybook example
storybook/stories/Examples/Pie/PieColorSync.stories.tsx
New story showing per-sector/custom-shape coloring synced between Sector rendering and tooltip payload colors.
Pie unit & integration tests
test/polar/Pie/Pie.spec.tsx, test/component/Tooltip/defaultIndex.spec.tsx, test/component/Tooltip/itemSorter.spec.tsx
Update assertions to expect explicit color and fill (e.g., #808080) in tooltip payloads; add tests for per-sector and activeShape-driven tooltip colors.
Selector & tooltip payload tests/types
test/state/selectors/pieSelectors.spec.tsx, test/state/selectors/selectors.spec.tsx
Adjust tests and exported tooltip payload expectations to include color and fill properties on tooltip entries (values updated accordingly).
Manifest
package.json
Minor manifest edits.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant PieImpl as PieImpl
participant Sectors as computePieSectors
participant Combiner as combineTooltipPayload
participant Tooltip as TooltipComponent

PieImpl->>Sectors: compute sectors (derive sectorColor from entry.fill or pieSettings.fill)
Sectors-->>PieImpl: sectors w/ tooltipPayload items (include color & fill)
PieImpl->>Combiner: request tooltip entry settings (pass activeShape)
Combiner-->>PieImpl: tooltip entries (preserve item color/fill, fallback to settings)
PieImpl->>Tooltip: render tooltip with payload (entries include color & fill)
Tooltip-->>User: display tooltip items with provided color/fill

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • PavelVanecek
  • ckifer
🚥 Pre-merge checks | ✅ 2 | ❌ 2
❌ Failed checks (2 warnings)
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.
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (47 files):

⚔️ .github/workflows/ci.yml (content)
⚔️ .github/workflows/deploy-website.yml (content)
⚔️ README.md (content)
⚔️ omnidoc/readExamples.spec.ts (content)
⚔️ omnidoc/readExamples.ts (content)
⚔️ omnidoc/verifyExamples.spec.ts (content)
⚔️ package-lock.json (content)
⚔️ package.json (content)
⚔️ src/cartesian/Funnel.tsx (content)
⚔️ src/cartesian/ReferenceArea.tsx (content)
⚔️ src/cartesian/ReferenceDot.tsx (content)
⚔️ src/cartesian/Scatter.tsx (content)
⚔️ src/chart/Sankey.tsx (content)
⚔️ src/chart/Treemap.tsx (content)
⚔️ src/component/DefaultLegendContent.tsx (content)
⚔️ src/hooks.ts (content)
⚔️ src/index.ts (content)
⚔️ src/polar/Pie.tsx (content)
⚔️ src/polar/PolarAngleAxis.tsx (content)
⚔️ src/polar/Radar.tsx (content)
⚔️ src/polar/RadialBar.tsx (content)
⚔️ src/shape/Dot.tsx (content)
⚔️ src/state/selectors/combiners/combineTooltipPayload.ts (content)
⚔️ storybook/stories/API/chart/Treemap.stories.tsx (content)
⚔️ test-vr/__snapshots__/tests/www/AreaChartApiExamples.spec-vr.tsx-snapshots/CrosshairExample-1-chromium-linux.png (content)
⚔️ test-vr/__snapshots__/tests/www/AreaChartApiExamples.spec-vr.tsx-snapshots/CrosshairExample-1-firefox-linux.png (content)
⚔️ test-vr/__snapshots__/tests/www/AreaChartApiExamples.spec-vr.tsx-snapshots/CrosshairExample-1-webkit-linux.png (content)
⚔️ test-vr/playwright-ct.Dockerfile (content)
⚔️ test-vr/tests/www/AreaChartApiExamples.spec-vr.tsx (content)
⚔️ test/cartesian/ReferenceArea.spec.tsx (content)
⚔️ test/cartesian/ReferenceDot.spec.tsx (content)
⚔️ test/cartesian/ReferenceLine/ReferenceLine.spec.tsx (content)
⚔️ test/cartesian/XAxis/XAxis.brush.spec.tsx (content)
⚔️ test/chart/Sankey.spec.tsx (content)
⚔️ test/chart/Treemap.spec.tsx (content)
⚔️ test/component/Tooltip/defaultIndex.spec.tsx (content)
⚔️ test/component/Tooltip/itemSorter.spec.tsx (content)
⚔️ test/polar/Pie/Pie.spec.tsx (content)
⚔️ test/polar/Radar.spec.tsx (content)
⚔️ test/state/selectors/pieSelectors.spec.tsx (content)
⚔️ test/state/selectors/radarSelectors.spec.tsx (content)
⚔️ test/state/selectors/selectors.spec.tsx (content)
⚔️ www/src/components/GuideView/CoordinateSystems/index.tsx (content)
⚔️ www/src/docs/apiExamples/getRelativeCoordinate/CrosshairExample.tsx (content)
⚔️ www/src/docs/apiExamples/index.tsx (content)
⚔️ www/src/docs/exampleComponents/TreeMap/CustomContentTreemap.tsx (content)
⚔️ www/src/views/IndexView/index.tsx (content)

These conflicts must be resolved before merging into main.
Resolve conflicts locally and push changes to this branch.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: syncing pie tooltip/legend color with per-sector fill, which is the core objective of the PR.
Description check ✅ Passed The PR description comprehensively covers all required sections with clear context, motivation, testing approach, and appropriate checkboxes selected.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
⚔️ Resolve merge conflicts (beta)
  • Auto-commit resolved conflicts to branch fix/6966
  • Post resolved changes as copyable diffs in a comment

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

🤖 Fix all issues with AI agents
In `@storybook/stories/Examples/Pie/PieColorSync.stories.tsx`:
- Around line 3-5: The story imports Recharts internals from '../../../../src'
and '../../../../src/polar/Pie' instead of the public package; update imports
for components (Legend, Pie, PieChart, ResponsiveContainer, Sector, Tooltip) to
come from the public 'recharts' entrypoint and replace the internal type
PieSectorDataItem with a publicly exported type or derive it from public props
(or add a re-export in the library if absent); also import RechartsHookInspector
from the storybook addon package rather than internal paths so all references
(Legend, Pie, PieChart, ResponsiveContainer, Sector, Tooltip, PieSectorDataItem,
RechartsHookInspector) use the public API.

In `@test/polar/Pie/Pie.spec.tsx`:
- Around line 218-220: The test currently uses a TypeScript assertion
"(tooltipItem as HTMLElement)" which violates the no-`as` rule; instead narrow
the runtime type after selecting the element: keep the querySelector and
assertNotNull(tooltipItem) (or replace it with a runtime check), then add a
guard like "if (!(tooltipItem instanceof HTMLElement)) throw new Error(...)" or
use an existing helper (e.g. assertIsHTMLElement) so you can safely call
tooltipItem.style.color in the expect; update the assertion line to use the
narrowed tooltipItem without any `as` cast.
🧹 Nitpick comments (1)
test/state/selectors/selectors.spec.tsx (1)

1418-1421: Consider asserting specific color values for stronger regression coverage.

Using expect.any(String) avoids coupling to the default palette, which is reasonable. However, it won't catch regressions where sectors receive wrong or swapped colors. If the default palette is stable, matching against actual expected hex values (e.g., from RECHARTS_DEFAULT_COLORS or equivalent) would strengthen the test.

Comment on lines +3 to +5
import { Legend, Pie, PieChart, ResponsiveContainer, Sector, Tooltip } from '../../../../src';
import { PieSectorDataItem } from '../../../../src/polar/Pie';
import { RechartsHookInspector } from '../../../storybook-addon-recharts';
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use public API imports for Recharts components/types.

Storybook files should import from the public entry point (recharts), not src or internal module paths. If PieSectorDataItem isn’t exported, consider re-exporting it or deriving the type from public props.

🔧 Suggested direction
- import { Legend, Pie, PieChart, ResponsiveContainer, Sector, Tooltip } from '../../../../src';
- import { PieSectorDataItem } from '../../../../src/polar/Pie';
+ import { Legend, Pie, PieChart, ResponsiveContainer, Sector, Tooltip, PieSectorDataItem } from 'recharts';

As per coding guidelines: "All imports from recharts must use the public API entry point (recharts), not internal paths like recharts/types/* or recharts/src/*".

🤖 Prompt for AI Agents
In `@storybook/stories/Examples/Pie/PieColorSync.stories.tsx` around lines 3 - 5,
The story imports Recharts internals from '../../../../src' and
'../../../../src/polar/Pie' instead of the public package; update imports for
components (Legend, Pie, PieChart, ResponsiveContainer, Sector, Tooltip) to come
from the public 'recharts' entrypoint and replace the internal type
PieSectorDataItem with a publicly exported type or derive it from public props
(or add a re-export in the library if absent); also import RechartsHookInspector
from the storybook addon package rather than internal paths so all references
(Legend, Pie, PieChart, ResponsiveContainer, Sector, Tooltip, PieSectorDataItem,
RechartsHookInspector) use the public API.

if (!(tooltipItem instanceof HTMLElement)) {
throw new Error(`Expected instance of HTMLElement, instead received: [${tooltipItem}]`);
}
expect(tooltipItem.style.color).toBe('rgb(255, 0, 0)');
Copy link
Collaborator

Choose a reason for hiding this comment

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

expect().toHaveStyle() also works

stroke: '#fff',
tooltipPayload: [
{
color: expect.any(String),
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm with the robot on this one. Let's have specific color here, instead of expect.any.

@PavelVanecek
Copy link
Collaborator

The visual diff looks all reasonable except for the active shape Pie. It feels that this should show the active shape color, not the inactive shape. What do you think @2YH02 ?

image

@2YH02
Copy link
Contributor Author

2YH02 commented Feb 13, 2026

The visual diff looks all reasonable except for the active shape Pie. It feels that this should show the active shape color, not the inactive shape. What do you think @2YH02 ?

image

@PavelVanecek I agree. I reviewed the VR diffs and missed that part.
If you agree, I’ll update this so active Pie sectors use the active shape color for tooltip color matching.

@2YH02
Copy link
Contributor Author

2YH02 commented Feb 13, 2026

Updated, @PavelVanecek

Pie tooltip now uses activeShape fill, so tooltip color matches the active sector.
Added a test for this.
250d945

// color and fill are erased to keep 100% the identical behaviour to recharts 2.x - but there's nothing stopping us from returning them here. It's technically a breaking change.
fill: undefined,
// Preserve item-level color/fill from graphical items.
// @ts-expect-error we're assuming that item has color property
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's parse it out, without assumptions and without ts expect error

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I changed this to parse array items from unknown before reading name/unit/dataKey/payload/color/fill
b49a59c

Copy link
Collaborator

Choose a reason for hiding this comment

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

The "as" keyword is doing exactly the same thing as ts-expect-error.

@PavelVanecek
Copy link
Collaborator

Does this fix #6957 too?

fill: string | undefined;
};

function isRecord(value: unknown): value is Record<string, unknown> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

And, you're not going to believe this, but the is keyword also disables the type system completely, same as @ts-expect-error and as. At least the runtime check is right this time.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the feedback!
Updated in 045367f.

@PavelVanecek
Copy link
Collaborator

Let's update the snapshots and then we can merge.

@PavelVanecek PavelVanecek merged commit 0788273 into recharts:main Feb 15, 2026
43 of 46 checks passed
@PavelVanecek
Copy link
Collaborator

@PavelVanecek
Copy link
Collaborator

Hm but that's a completely custom shape. Should we dig into that? Or should we assume that whoever got that far, can also modify the tooltip text color?

@2YH02
Copy link
Contributor Author

2YH02 commented Feb 15, 2026

I agree. Since this is a fully custom shape case, I think it’s reasonable to leave tooltip color to user control for now. In practice, users who customize charts this much usually customize tooltip content/styles too. If we later decide this should be auto-synced as well, I can follow up with a separate PR.

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.

Tooltip/Legend colors don't match pie chart when using custom shape Scatter tooltip text color is always black instead of legend/series color

3 participants