Skip to content

Add label style prop in Legend#7012

Merged
ckifer merged 1 commit intorecharts:mainfrom
devoldemar:legend_labelstyle
Feb 20, 2026
Merged

Add label style prop in Legend#7012
ckifer merged 1 commit intorecharts:mainfrom
devoldemar:legend_labelstyle

Conversation

@devoldemar
Copy link
Contributor

@devoldemar devoldemar commented Feb 15, 2026

Description

Customize text style of each legend item like it's made in DefaultTooltipContent.

Motivation and Context

Use of single color for all text labels instead of item colors in Legend. Fit style accordingly to font size and line height in the document.

How Has This Been Tested?

Unit tests added, existing ones passed

Screenshots (if appropriate):

linechart piechart AgePieChart

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

    • Per-item legend label styling: add a labelStyle option so legend entries can have custom text styling (including color); inactive/hidden items reflect the configured inactive color.
    • Legend default props are now public and included in component defaults.
  • Documentation

    • Default legend props are surfaced in documentation metadata.
  • Tests

    • Updated tests to verify legend label styling and correct color handling for active vs. inactive items.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 15, 2026

Walkthrough

Adds an optional labelStyle?: CSSProperties prop to DefaultLegendContent, exposes its default props constant, merges labelStyle with per-item color (or inactiveColor when inactive) to compute legend text style, and updates tests/helpers to assert legend label text colors.

Changes

Cohort / File(s) Summary
Legend component styling
src/component/DefaultLegendContent.tsx
Export defaultLegendContentDefaultProps; add labelStyle?: CSSProperties to DefaultLegendContentProps; compute finalLabelStyle per item by merging labelStyle with item color or inactiveColor when inactive; apply finalLabelStyle to legend text spans.
Tests & test helper
test/component/Legend.spec.tsx, test/helper/expectLegendLabels.tsx
Extend expectLegendLabels to accept optional textColor and detect/assert label text color; update and add tests to validate textColor propagation and behavior with labelStyle and inactive states.
Omnidoc defaults map
omnidoc/componentsAndDefaultPropsMap.ts
Import and register the newly exported defaultLegendContentDefaultProps in componentMetaMap under DefaultLegendContent.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • ckifer
  • PavelVanecek
🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add label style prop in Legend' clearly and concisely describes the main change: adding a new labelStyle property to the Legend component.
Description check ✅ Passed The PR description includes Description, Motivation and Context, How Has This Been Tested, Screenshots, Types of changes, and Checklist sections matching the template requirements.

✏️ 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

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

🤖 Fix all issues with AI agents
In `@test/helper/expectLegendLabels.tsx`:
- Line 30: The test helper expectLegendLabels accesses
legend.querySelector('.recharts-legend-item-text').style.color directly which
can throw if querySelector returns null; update the logic in expectLegendLabels
to use optional chaining or a null check when reading .style.color (i.e., read
legend.querySelector('.recharts-legend-item-text')?.style.color or guard the
element before accessing .style.color) so the assertion produces a clear failure
instead of a TypeError.
🧹 Nitpick comments (2)
src/component/DefaultLegendContent.tsx (2)

113-117: JSDoc @defaultValue {} is misleading.

The prop's actual default is undefined (it's not listed in defaultLegendContentDefaultProps). The {} is only the internal fallback computed at line 228. Consider @defaultValue undefined or removing the @defaultValue line.


228-229: Simplify the guard and consider labelStyle.color === '' edge case.

The typeof labelStyle === 'object' && labelStyle !== null check can be simplified to a truthiness check since labelStyle is typed as CSSProperties | undefined. Also, on line 229 the || operator means a color: '' in labelStyle would fall through to entry.color — this is likely fine, but worth being aware of.

Suggested simplification
-    const finalLabelStyle = typeof labelStyle === 'object' && labelStyle !== null ? { ...labelStyle } : {};
+    const finalLabelStyle: CSSProperties = labelStyle ? { ...labelStyle } : {};

@ckifer
Copy link
Member

ckifer commented Feb 16, 2026

There's no way to change this with the current API? This seems fine to me but just double checking

@ckifer
Copy link
Member

ckifer commented Feb 17, 2026

@devoldemar can you fix the failing unit tests?

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

🧹 Nitpick comments (1)
src/component/DefaultLegendContent.tsx (1)

123-130: Export name deviates from the established default[ComponentName]Props convention.

The analogous export for DefaultTooltipContent (added at line 38 of componentsAndDefaultPropsMap.ts) is named defaultDefaultTooltipContentProps, following the pattern default + ComponentName + Props. The new export is defaultLegendContentDefaultProps, which inverts the word order. While not a functional issue, it is inconsistent with the nearest comparable export and will appear in the public API surface consumed by omnidoc.

♻️ Proposed rename
-export const defaultLegendContentDefaultProps = {
+export const defaultDefaultLegendContentProps = {

Also update the import in omnidoc/componentsAndDefaultPropsMap.ts:

-import { defaultLegendContentDefaultProps } from '../src/component/DefaultLegendContent';
+import { defaultDefaultLegendContentProps } from '../src/component/DefaultLegendContent';

And the entry:

-  DefaultLegendContent: { defaultProps: defaultLegendContentDefaultProps },
+  DefaultLegendContent: { defaultProps: defaultDefaultLegendContentProps },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/component/DefaultLegendContent.tsx` around lines 123 - 130, Rename the
exported constant defaultLegendContentDefaultProps to follow the established
pattern defaultDefaultLegendContentProps so the public API matches other exports
(e.g., defaultDefaultTooltipContentProps); update the export name in
src/component/DefaultLegendContent.tsx (the constant currently named
defaultLegendContentDefaultProps) and update any imports/usages such as the
entry in componentsAndDefaultPropsMap.ts to import the new name (and remove or
update the old export to avoid a duplicate or broken import).
🤖 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/component/DefaultLegendContent.tsx`:
- Around line 229-231: Remove the redundant runtime type guard around labelStyle
and instead spread labelStyle directly into finalLabelStyle typed as
CSSProperties (e.g., annotate finalLabelStyle as CSSProperties) to avoid the
union inference; then set finalLabelStyle.color = entry.inactive ? inactiveColor
: (finalLabelStyle.color ?? entry.color) using the nullish coalescing operator
so an explicit empty string in labelStyle.color is preserved; refer to
finalLabelStyle, labelStyle, inactiveColor, and entry.color when making the
change.

In `@test/helper/expectLegendLabels.tsx`:
- Line 30: The expression setting textColor uses
legend.querySelector('.recharts-legend-item-text') and then accesses .style
which TypeScript flags because querySelector returns Element | null; change the
logic in the textColor assignment in expectLegendLabels (use the local variable
legend and the same selector) to first capture the result into a variable, check
that the variable instanceof HTMLElement, and only then read .style.color;
otherwise return undefined—this narrows the type so .style is safe to access.

---

Nitpick comments:
In `@src/component/DefaultLegendContent.tsx`:
- Around line 123-130: Rename the exported constant
defaultLegendContentDefaultProps to follow the established pattern
defaultDefaultLegendContentProps so the public API matches other exports (e.g.,
defaultDefaultTooltipContentProps); update the export name in
src/component/DefaultLegendContent.tsx (the constant currently named
defaultLegendContentDefaultProps) and update any imports/usages such as the
entry in componentsAndDefaultPropsMap.ts to import the new name (and remove or
update the old export to avoid a duplicate or broken import).


const actualLabels = assertHasLegend(container).map(legend => ({
textContent: legend.textContent,
textColor: expectsColor ? legend.querySelector('.recharts-legend-item-text')?.style.color : undefined,
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, confirm the file exists and check line 30 context
echo "=== File content around line 30 ==="
sed -n '20,40p' test/helper/expectLegendLabels.tsx

echo -e "\n=== Search for querySelector with recharts-legend-item-text ==="
rg -n "querySelector.*recharts-legend-item-text" test/

echo -e "\n=== Check tsconfig files and their strictness ==="
fd "tsconfig" --type f | grep -E "\.json$" | head -5 | while read f; do
  echo "File: $f"
  grep -E '"strict"|"noImplicitAny"|"strictNullChecks"' "$f" || echo "  (no strict flags found)"
done

Repository: recharts/recharts

Length of output: 1772


🏁 Script executed:

#!/bin/bash
# Find assertHasLegend definition to understand legend type
echo "=== Looking for assertHasLegend definition ==="
rg -n "function assertHasLegend|const assertHasLegend|export.*assertHasLegend" test/

echo -e "\n=== Full definition of assertHasLegend ==="
rg -A 10 "function assertHasLegend|const assertHasLegend" test/helper/expectLegendLabels.tsx

Repository: recharts/recharts

Length of output: 713


Add instanceof HTMLElement guard before accessing .style property.

On line 30, legend.querySelector() returns Element | null, and Element does not have a .style property—that's only on HTMLElement. The optional chaining ?. prevents a null-deref crash but does not satisfy TypeScript's strict type checker. Add an instanceof guard to narrow the type:

- textColor: expectsColor ? legend.querySelector('.recharts-legend-item-text')?.style.color : undefined,
+ textColor: expectsColor
+   ? (() => {
+       const el = legend.querySelector('.recharts-legend-item-text');
+       return el instanceof HTMLElement ? el.style.color : undefined;
+     })()
+   : undefined,
📝 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
textColor: expectsColor ? legend.querySelector('.recharts-legend-item-text')?.style.color : undefined,
textColor: expectsColor
? (() => {
const el = legend.querySelector('.recharts-legend-item-text');
return el instanceof HTMLElement ? el.style.color : undefined;
})()
: undefined,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/helper/expectLegendLabels.tsx` at line 30, The expression setting
textColor uses legend.querySelector('.recharts-legend-item-text') and then
accesses .style which TypeScript flags because querySelector returns Element |
null; change the logic in the textColor assignment in expectLegendLabels (use
the local variable legend and the same selector) to first capture the result
into a variable, check that the variable instanceof HTMLElement, and only then
read .style.color; otherwise return undefined—this narrows the type so .style is
safe to access.

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.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/component/DefaultLegendContent.tsx`:
- Around line 229-230: The branch check on labelStyle is dead because
RequiresDefaultProps ensures labelStyle is always a CSSProperties object; remove
the unnecessary typeof labelStyle === 'object' guard and simply clone labelStyle
into finalLabelStyle, and fix the falsy-color override by using the nullish
coalescing operator (finalLabelStyle.color = entry.inactive ? inactiveColor :
finalLabelStyle.color ?? entry.color) so an explicit empty string or other falsy
but defined color value on labelStyle is not overwritten; update the code that
constructs finalLabelStyle (referencing finalLabelStyle, labelStyle,
entry.color, and inactiveColor) accordingly and remove the unreachable fallback
branch to restore branch coverage.

In `@test/helper/expectLegendLabels.tsx`:
- Line 30: The code accesses .style on
legend.querySelector('.recharts-legend-item-text') which is typed as Element |
null; add an instanceof HTMLElement type guard to narrow the result before
reading .style.color. Locate the expression using
legend.querySelector('.recharts-legend-item-text') in expectLegendLabels.tsx and
replace the direct property access with a guarded expression (e.g., store the
result in a local variable and use if (el instanceof HTMLElement) or a
conditional expression) so textColor returns the element's style.color only when
the query result is an HTMLElement, otherwise undefined.

@devoldemar
Copy link
Contributor Author

@devoldemar can you fix the failing unit tests?

Fixed.
Please, let me know if CodeRabbit comments matter. The proposed fix won't work as intended, a chart won't be rendered if labelStyle is not a valid object.

@devoldemar devoldemar requested a review from ckifer February 19, 2026 14:35
@codecov
Copy link

codecov bot commented Feb 20, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.12%. Comparing base (4437af2) to head (0cc7b82).
⚠️ Report is 13 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff            @@
##             main    #7012    +/-   ##
========================================
  Coverage   90.11%   90.12%            
========================================
  Files         524      526     +2     
  Lines       39071    39185   +114     
  Branches     5399     5424    +25     
========================================
+ Hits        35209    35314   +105     
- Misses       3853     3862     +9     
  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 20, 2026

Bundle Report

Changes will decrease total bundle size by 1.88kB (-0.06%) ⬇️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
recharts/bundle-cjs 1.26MB 244 bytes (0.02%) ⬆️
recharts/bundle-es6* 1.09MB -1.63kB (-0.15%) ⬇️
recharts/bundle-umd* 541.4kB -496 bytes (-0.09%) ⬇️

ℹ️ *Bundle size includes cached data from a previous commit

Affected Assets, Files, and Routes:

view changes for bundle: recharts/bundle-umd

Assets Changed:

Asset Name Size Change Total Size Change (%)
Recharts.js -496 bytes 541.4kB -0.09%
view changes for bundle: recharts/bundle-cjs

Assets Changed:

Asset Name Size Change Total Size Change (%)
component/DefaultLegendContent.js 244 bytes 7.65kB 3.29%
view changes for bundle: recharts/bundle-es6

Assets Changed:

Asset Name Size Change Total Size Change (%)
cartesian/Area.js 121 bytes 27.46kB 0.44%
cartesian/Bar.js -74 bytes 26.39kB -0.28%
component/TooltipBoundingBox.js -273 bytes 5.25kB -4.94%
animation/CSSTransitionAnimate.js -156 bytes 2.38kB -6.14%
animation/JavascriptAnimate.js -156 bytes 1.9kB -7.58%
util/usePrefersReducedMotion.js (Deleted) -1.09kB 0 bytes -100.0% 🗑️

@ckifer ckifer merged commit a68f5f2 into recharts:main Feb 20, 2026
40 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.

3 participants