fix: filter textBreakAll from props passed to custom Label content element#7208
fix: filter textBreakAll from props passed to custom Label content element#7208supermonkeyguys wants to merge 2 commits into
Conversation
…ement When Label's content prop is a React element, textBreakAll (a recharts-specific prop) was being forwarded via cloneElement along with other props. If the custom content renders a DOM element (e.g. div), React warns: Warning: React does not recognize the `textBreakAll` prop on a DOM element. Fix: destructure textBreakAll out before spreading propsWithoutLabelRef and propsForContent, so it never reaches DOM elements. Fixes recharts#3177
WalkthroughWhen Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/component/Label.tsx (1)
431-439: Please add regression tests for both updated custom-content branches.This change touches both
cloneElementandcreateElementpaths, so we should lock behavior with tests:
- no React unknown-prop warning for DOM-rendering custom content, and
- expected behavior for custom component content that needs label wrapping semantics.
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 `@src/component/Label.tsx` around lines 431 - 439, Add unit tests for the Label component to cover both custom-content branches touched: (1) when content is a React element (cloneElement path) ensure no unknown DOM prop warnings are emitted for DOM-rendering custom content and that propsWithViewBox filters out labelRef and textBreakAll before cloning; (2) when content is a function/component (createElement path) verify the returned element receives propsForContent and that label wrapping semantics (e.g., positioning, className/text wrapping) behave as expected. Locate tests to target Label (use the content, propsWithViewBox, labelRef, textBreakAll symbols) and assert no React unknown-prop warnings and correct rendered structure/props for both cloneElement and createElement cases.
🤖 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/Label.tsx`:
- Around line 432-437: The current code unconditionally strips textBreakAll from
propsWithViewBox before passing to cloneElement or invoking content, which
prevents custom renderers from receiving it; update the logic in the Label
component around propsWithViewBox/content handling so textBreakAll is only
removed for intrinsic DOM elements (i.e., when content is a string or a React
element whose type is a string) but is preserved when content is a function or a
custom component, ensuring functions passed to content and custom components
still receive textBreakAll (refer to symbols: propsWithViewBox, content,
cloneElement).
---
Nitpick comments:
In `@src/component/Label.tsx`:
- Around line 431-439: Add unit tests for the Label component to cover both
custom-content branches touched: (1) when content is a React element
(cloneElement path) ensure no unknown DOM prop warnings are emitted for
DOM-rendering custom content and that propsWithViewBox filters out labelRef and
textBreakAll before cloning; (2) when content is a function/component
(createElement path) verify the returned element receives propsForContent and
that label wrapping semantics (e.g., positioning, className/text wrapping)
behave as expected. Locate tests to target Label (use the content,
propsWithViewBox, labelRef, textBreakAll symbols) and assert no React
unknown-prop warnings and correct rendered structure/props for both cloneElement
and createElement cases.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 8623489e-15dd-46bc-8b77-8e435d9924f6
📒 Files selected for processing (1)
src/component/Label.tsx
| const { labelRef: _, textBreakAll: __, ...propsWithoutLabelRef } = propsWithViewBox; | ||
| return cloneElement(content, propsWithoutLabelRef); | ||
| } | ||
|
|
||
| if (typeof content === 'function') { | ||
| const { content: _, ...propsForContent } = propsWithViewBox; | ||
| const { content: _, textBreakAll: __, ...propsForContent } = propsWithViewBox; |
There was a problem hiding this comment.
Potential regression: textBreakAll is now impossible for custom content to consume.
At Line 432 and Line 437, textBreakAll is stripped unconditionally. That fixes DOM warnings, but it also removes this value from all custom renderers (including components that intentionally pass it to nested Label/Text). Given LabelList still provides textBreakAll to Label, this breaks that propagation path for custom-content compositions.
Consider filtering it only for intrinsic DOM targets, or exposing a stable custom-content prop contract that preserves this behavior without leaking unknown props to DOM nodes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/component/Label.tsx` around lines 432 - 437, The current code
unconditionally strips textBreakAll from propsWithViewBox before passing to
cloneElement or invoking content, which prevents custom renderers from receiving
it; update the logic in the Label component around propsWithViewBox/content
handling so textBreakAll is only removed for intrinsic DOM elements (i.e., when
content is a string or a React element whose type is a string) but is preserved
when content is a function or a custom component, ensuring functions passed to
content and custom components still receive textBreakAll (refer to symbols:
propsWithViewBox, content, cloneElement).
Per code review feedback: - textBreakAll should only be stripped when content is a React element whose type is a native DOM string (e.g. 'div', 'span'). Custom components should still receive the prop so they can handle it themselves. - content function path: restore textBreakAll forwarding (was incorrectly stripped in the previous commit) - Add regression tests covering: 1. No React unknown-prop warning when content is a native DOM element 2. textBreakAll is preserved when content is a custom React component 3. textBreakAll is forwarded to content functions
|
Thanks for the thorough review @coderabbitai! Addressed both points in the latest commit:
|
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 877 🏁 Script executed: Length of output: 16189 🏁 Script executed: Length of output: 2085 🏁 Script executed: Length of output: 2091
Regression tests:
The 🧠 Learnings usedIf you found this review helpful, would you consider giving us a shout-out on X? Thank you for using CodeRabbit! |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@test/component/Label.spec.tsx`:
- Around line 737-740: The current assertion using
expect(contentFn).toHaveBeenCalledWith(...) can give false positives; change the
test to assert the function was called exactly once and that its last call
matches the exact arguments. Replace the call with
expect(contentFn).toHaveBeenCalledTimes(1) and
expect(contentFn).toHaveBeenLastCalledWith({ textBreakAll: true }, {}),
targeting the contentFn invocation and removing the loose
expect.objectContaining matcher so the assertion is strict.
- Around line 706-708: The test's warning detection only checks the first
console.error argument via consoleErrorSpy.mock.calls.find(args =>
String(args[0]).includes('textBreakAll')), which misses format-placeholder
warnings; update the detection (referencing the unknownPropWarning lookup) to
inspect all arguments for the string (e.g., use args.some(a =>
String(a).includes('textBreakAll')) or join all args into one string) so
warnings like console.error('Unknown prop %s', 'textBreakAll') are correctly
matched.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a474f891-95db-4fb4-8b90-8d8889c4f3ec
📒 Files selected for processing (2)
src/component/Label.tsxtest/component/Label.spec.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- src/component/Label.tsx
| const unknownPropWarning = consoleErrorSpy.mock.calls.find(args => | ||
| String(args[0]).includes('textBreakAll'), | ||
| ); |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In React 18.3.1 development warnings, can console.error messages use format placeholders with substituted values passed in later arguments (not only args[0])?
💡 Result:
Yes, in React 18.3.1 development warnings, console.error messages support format placeholders (%s, %d, etc.) with substituted values passed in later arguments, just like native console.error. React's development build transforms these calls to append component stacks while preserving native formatting behavior, as confirmed by source code analysis and recent changes removing custom stringification that could interfere (PR #29839). Examples from React source like console.error('Invalid attribute ref supplied to React.Fragment.') show simple strings, but the mechanism handles multiple args with placeholders natively.
Citations:
- 1: Remove Warning: prefix and toString on console Arguments react/react#29839
- 2: chore[react-devtools]: improve console arguments formatting before passing it to original console react/react#29873
- 3: Relying on console.log native string substitution interferes with typical patched console logging react/react#16637
- 4: Bug: Error message prints "Warning: ..." instead of "Error: ..." react/react#20981
- 5: RFC: Warning System Revamp react/react#16753
- 6: Remove Component Stack from React Logged Warnings and Error Reporting react/react#30308
- 7: Use console directly instead of warning() modules react/react#17599
- 8: React.warn() and React.error() react/react#15170
- 9: https://developer.mozilla.org/en-US/docs/Web/API/console.warn
🏁 Script executed:
# Inspect the actual code at lines 706-708 and surrounding context
sed -n '700,745p' test/component/Label.spec.tsxRepository: recharts/recharts
Length of output: 1525
🏁 Script executed:
# Search for similar console.error spy patterns in the test file to see if this is an isolated issue
rg 'consoleErrorSpy|console\.error' test/component/Label.spec.tsx -n -B 2 -A 2Repository: recharts/recharts
Length of output: 664
Warning detection check only inspects first argument and can miss React format-placeholder warnings
React 18.3.1 development warnings support format placeholders with substituted values passed in later arguments (e.g., console.error('Unknown prop %s', 'textBreakAll')). This test only checks args[0] and will false-pass if the actual warning contains the value in a later argument.
Proposed fix
- const unknownPropWarning = consoleErrorSpy.mock.calls.find(args =>
- String(args[0]).includes('textBreakAll'),
- );
+ const unknownPropWarning = consoleErrorSpy.mock.calls.find(args =>
+ args.some(arg => String(arg).includes('textBreakAll')),
+ );
expect(unknownPropWarning).toBeUndefined();📝 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.
| const unknownPropWarning = consoleErrorSpy.mock.calls.find(args => | |
| String(args[0]).includes('textBreakAll'), | |
| ); | |
| const unknownPropWarning = consoleErrorSpy.mock.calls.find(args => | |
| args.some(arg => String(arg).includes('textBreakAll')), | |
| ); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test/component/Label.spec.tsx` around lines 706 - 708, The test's warning
detection only checks the first console.error argument via
consoleErrorSpy.mock.calls.find(args =>
String(args[0]).includes('textBreakAll')), which misses format-placeholder
warnings; update the detection (referencing the unknownPropWarning lookup) to
inspect all arguments for the string (e.g., use args.some(a =>
String(a).includes('textBreakAll')) or join all args into one string) so
warnings like console.error('Unknown prop %s', 'textBreakAll') are correctly
matched.
| expect(contentFn).toHaveBeenCalledWith( | ||
| expect.objectContaining({ textBreakAll: true }), | ||
| {}, | ||
| ); |
There was a problem hiding this comment.
Make the content-function assertion strict to avoid false positives
toHaveBeenCalledWith passes if any call matches. For a regression test, assert single invocation and exact last call.
Proposed fix
- expect(contentFn).toHaveBeenCalledWith(
+ expect(contentFn).toHaveBeenCalledTimes(1);
+ expect(contentFn).toHaveBeenLastCalledWith(
expect.objectContaining({ textBreakAll: true }),
{},
);📝 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.
| expect(contentFn).toHaveBeenCalledWith( | |
| expect.objectContaining({ textBreakAll: true }), | |
| {}, | |
| ); | |
| expect(contentFn).toHaveBeenCalledTimes(1); | |
| expectLastCalledWith( | |
| contentFn, | |
| expect.objectContaining({ textBreakAll: true }), | |
| {}, | |
| ); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test/component/Label.spec.tsx` around lines 737 - 740, The current assertion
using expect(contentFn).toHaveBeenCalledWith(...) can give false positives;
change the test to assert the function was called exactly once and that its last
call matches the exact arguments. Replace the call with
expect(contentFn).toHaveBeenCalledTimes(1) and
expect(contentFn).toHaveBeenLastCalledWith({ textBreakAll: true }, {}),
targeting the contentFn invocation and removing the loose
expect.objectContaining matcher so the assertion is strict.
|
Same question as https://github.com/recharts/recharts/pull/7069/changes#r2899496695: people who rely on this prop being passed are going to see broken charts? |
Problem
When
Label'scontentprop is a React element or function,textBreakAll(a recharts-specific prop) was being forwarded viacloneElement/createElementalong with other props. If the custom content renders a DOM element (e.g.div), React emits a console warning:Fixes #3177
Root Cause
In
Label.tsx, when buildingpropsWithViewBox,textBreakAllwas included. Whencontentis a React element, the code does:Only
labelRefwas excluded —textBreakAllslipped through and got passed to whatever DOM element the custom content renders.Fix
Destructure
textBreakAllout in both theisValidElement(content)andtypeof content === 'function'branches, so it never reaches DOM elements:textBreakAllis already correctly consumed earlier in the component and passed asbreakAllto the<Text>component, so this change does not affect any functionality.Summary by CodeRabbit