Conversation
WalkthroughAdds a rendered-ticks Redux slice, threads Changes
Sequence DiagramsequenceDiagram
participant CartesianAxis as CartesianAxis
participant Ticks as Ticks Component
participant Reporter as RenderedTicksReporter
participant Redux as Redux Store
participant Selector as Selector / Hook
CartesianAxis->>Ticks: render(axisId, tick data)
Ticks->>Reporter: mount/update(ticks, axisType, axisId)
Reporter->>Reporter: useEffect (map ticks -> TickItem[])
Reporter->>Redux: dispatch setRenderedTicks(axisType, axisId, TickItem[])
Redux->>Redux: store at state.renderedTicks[axisType][axisId]
Selector->>Redux: selectRenderedTicksOfAxis(state, axisType, axisId)
Redux-->>Selector: TickItem[] | undefined
Reporter->>Reporter: cleanup on unmount
Reporter->>Redux: dispatch removeRenderedTicks(axisType, axisId)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
No actionable comments were generated in the recent review. 🎉 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 |
…s in YAxis and XAxis components
…instead of raw ticks
a7ea7aa to
d229b6e
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@test/cartesian/XAxis/XAxis.state.spec.tsx`:
- Around line 343-373: The rendered-ticks test relies on DOM measurements but
JSDOM returns zero sizes; import and use the test helper
mockGetBoundingClientRect (from test/helper/MockGetBoundingClientRect.ts) inside
the XAxis.state.spec.tsx case that uses
createSelectorTestCase/selectRenderedTicksOfAxis so the tick elements have
stable bounding rects before rendering (call mockGetBoundingClientRect() prior
to render and ensure it is restored/cleaned up after the test to avoid side
effects), leaving the rest of the test (expectedTicks and
expectLastCalledWith(spy, ...)) unchanged.
🧹 Nitpick comments (5)
test/cartesian/ReferenceLine/ReferenceLine.spec.tsx (1)
32-36: Missingvi.useFakeTimers()in the test suite.The coding guidelines require
vi.useFakeTimers()in all tests due to ReduxautoBatchEnhancerdependency on timers. ThisbeforeEachblock doesn't set up fake timers. While this is a pre-existing gap, it's worth addressing since you're already touching this file.Proposed fix
beforeEach(() => { + vi.useFakeTimers(); consoleSpy = vi.spyOn(console, 'warn').mockImplementation((): void => undefined); });As per coding guidelines: "Use
vi.useFakeTimers()in all tests due to Redux autoBatchEnhancer dependency on timers andrequestAnimationFrame"test/component/Tooltip/Tooltip.multipleDataArrays.spec.tsx (1)
131-132: Hardcoded call index may be stale after the count bump.Line 132 accesses
spy.mock.calls[2][0]— previously this was the last call (count=3), but now with count=4 it's the second-to-last. The test still passes because the scale is identical, but consider usingspy.mock.lastCall[0]orspy.mock.calls[spy.mock.calls.length - 1][0]to be resilient to future count changes.Suggested improvement
expect(spy).toHaveBeenCalledTimes(4); - const scale = spy.mock.calls[2][0]; + const scale = spy.mock.lastCall[0];test/state/selectors/selectAxisScale.spec.tsx (1)
40-40: Commented-out assertion reduces render-count verification coverage.The call-count assertion is commented out instead of being updated to the new expected value. Per coding guidelines, selector call counts should be verified to spot unnecessary re-renders. If the count is now known (e.g., 3 as in the sibling test), update the assertion; otherwise, add a TODO explaining why it's deferred.
Proposed fix
- // expect(spy).toHaveBeenCalledTimes(3); + expect(spy).toHaveBeenCalledTimes(3);Based on learnings: "Verify the number of selector calls using the spy object from
createSelectorTestCaseto spot unnecessary re-renders and improve performance"src/cartesian/CartesianAxis.tsx (2)
339-362:axisIdadded toTicksProps— verify JSDoc/type documentation.The
axisIdfield is added toTicksPropswithout a JSDoc comment. While this is an internal type (not public API), the coding guidelines ask to keep TypeScript definitions up-to-date for documentation purposes. Consider adding a brief comment.As per coding guidelines: "JSDoc comments and TypeScript definitions in source files must be kept up-to-date as they are used to autogenerate API documentation via 'npm run omnidoc'."
45-48:axisIdprop added toCartesianAxisPropswithout JSDoc.Other props in this interface have comments. A brief JSDoc would be consistent and helpful for the autogenerated API docs.
As per coding guidelines: "JSDoc comments and TypeScript definitions in source files must be kept up-to-date as they are used to autogenerate API documentation via 'npm run omnidoc'."
Suggested addition
axisType?: 'xAxis' | 'yAxis'; + /** Unique identifier for this axis instance, used to track rendered ticks in state. */ axisId?: AxisId;
| it('should publish rendered ticks to the store', () => { | ||
| const renderTestCase = createSelectorTestCase(({ children }) => ( | ||
| <BarChart width={100} height={100} data={[{ x: 'x-1' }, { x: 'x-2' }, { x: 'x-3' }]}> | ||
| <XAxis xAxisId="foo" dataKey="x" /> | ||
| {children} | ||
| </BarChart> | ||
| )); | ||
|
|
||
| const { spy } = renderTestCase(state => selectRenderedTicksOfAxis(state, 'xAxis', 'foo')); | ||
| const expectedTicks: ReadonlyArray<TickItem> = [ | ||
| { | ||
| coordinate: 20, | ||
| index: 0, | ||
| offset: 15, | ||
| value: 'x-1', | ||
| }, | ||
| { | ||
| coordinate: 50, | ||
| index: 1, | ||
| offset: 15, | ||
| value: 'x-2', | ||
| }, | ||
| { | ||
| coordinate: 80, | ||
| index: 2, | ||
| offset: 15, | ||
| value: 'x-3', | ||
| }, | ||
| ]; | ||
| expectLastCalledWith(spy, expectedTicks); | ||
| }); |
There was a problem hiding this comment.
Add mockGetBoundingClientRect for rendered-ticks test stability.
This test exercises DOM-based tick measurement; without a mock, JSDOM returns zero sizes and can make rendered-tick selection brittle.
Proposed fix
@@
-import { createSelectorTestCase, rechartsTestRender } from '../../helper/createSelectorTestCase';
+import { createSelectorTestCase, rechartsTestRender } from '../../helper/createSelectorTestCase';
+import { mockGetBoundingClientRect } from '../../helper/mockGetBoundingClientRect';
@@
it('should publish rendered ticks to the store', () => {
+ mockGetBoundingClientRect({ width: 10, height: 10 });
const renderTestCase = createSelectorTestCase(({ children }) => (
<BarChart width={100} height={100} data={[{ x: 'x-1' }, { x: 'x-2' }, { x: 'x-3' }]}>
<XAxis xAxisId="foo" dataKey="x" />
{children}
</BarChart>
));As per coding guidelines: Mock getBoundingClientRect in tests using the helper function provided in test/helper/MockGetBoundingClientRect.ts.
📝 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.
| it('should publish rendered ticks to the store', () => { | |
| const renderTestCase = createSelectorTestCase(({ children }) => ( | |
| <BarChart width={100} height={100} data={[{ x: 'x-1' }, { x: 'x-2' }, { x: 'x-3' }]}> | |
| <XAxis xAxisId="foo" dataKey="x" /> | |
| {children} | |
| </BarChart> | |
| )); | |
| const { spy } = renderTestCase(state => selectRenderedTicksOfAxis(state, 'xAxis', 'foo')); | |
| const expectedTicks: ReadonlyArray<TickItem> = [ | |
| { | |
| coordinate: 20, | |
| index: 0, | |
| offset: 15, | |
| value: 'x-1', | |
| }, | |
| { | |
| coordinate: 50, | |
| index: 1, | |
| offset: 15, | |
| value: 'x-2', | |
| }, | |
| { | |
| coordinate: 80, | |
| index: 2, | |
| offset: 15, | |
| value: 'x-3', | |
| }, | |
| ]; | |
| expectLastCalledWith(spy, expectedTicks); | |
| }); | |
| it('should publish rendered ticks to the store', () => { | |
| mockGetBoundingClientRect({ width: 10, height: 10 }); | |
| const renderTestCase = createSelectorTestCase(({ children }) => ( | |
| <BarChart width={100} height={100} data={[{ x: 'x-1' }, { x: 'x-2' }, { x: 'x-3' }]}> | |
| <XAxis xAxisId="foo" dataKey="x" /> | |
| {children} | |
| </BarChart> | |
| )); | |
| const { spy } = renderTestCase(state => selectRenderedTicksOfAxis(state, 'xAxis', 'foo')); | |
| const expectedTicks: ReadonlyArray<TickItem> = [ | |
| { | |
| coordinate: 20, | |
| index: 0, | |
| offset: 15, | |
| value: 'x-1', | |
| }, | |
| { | |
| coordinate: 50, | |
| index: 1, | |
| offset: 15, | |
| value: 'x-2', | |
| }, | |
| { | |
| coordinate: 80, | |
| index: 2, | |
| offset: 15, | |
| value: 'x-3', | |
| }, | |
| ]; | |
| expectLastCalledWith(spy, expectedTicks); | |
| }); |
🤖 Prompt for AI Agents
In `@test/cartesian/XAxis/XAxis.state.spec.tsx` around lines 343 - 373, The
rendered-ticks test relies on DOM measurements but JSDOM returns zero sizes;
import and use the test helper mockGetBoundingClientRect (from
test/helper/MockGetBoundingClientRect.ts) inside the XAxis.state.spec.tsx case
that uses createSelectorTestCase/selectRenderedTicksOfAxis so the tick elements
have stable bounding rects before rendering (call mockGetBoundingClientRect()
prior to render and ensure it is restored/cleaned up after the test to avoid
side effects), leaving the rest of the test (expectedTicks and
expectLastCalledWith(spy, ...)) unchanged.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/hooks.ts`:
- Around line 263-265: Change the return type annotations of useXAxisTicks and
useYAxisTicks to match selectRenderedTicksOfAxis by replacing
ReadonlyArray<CartesianTickItem> | undefined with ReadonlyArray<TickItem> |
undefined; update the type import/usage so the hooks return TickItem arrays (not
CartesianTickItem) and ensure any callers are compatible with TickItem
properties.
🧹 Nitpick comments (3)
test/cartesian/XAxis/XAxis.state.spec.tsx (1)
351-372: MissingtoHaveBeenCalledTimesassertion on the spy.The test validates the last-called value but does not verify how many times the selector was invoked. Adding this assertion helps catch unnecessary re-renders.
Proposed fix
const { spy } = renderTestCase(state => selectRenderedTicksOfAxis(state, 'xAxis', 'foo')); + expect(spy).toHaveBeenCalledTimes(3); const expectedTicks: ReadonlyArray<TickItem> = [Based on learnings: "Verify the number of selector calls using the spy object from
createSelectorTestCaseto spot unnecessary re-renders and improve performance."src/cartesian/CartesianAxis.tsx (2)
45-49: Missing JSDoc on newaxisIdprop.
CartesianAxisProps.axisIdis a new public-facing prop but lacks a JSDoc comment. As per coding guidelines, JSDoc should be kept up-to-date since it's used for auto-generated API documentation.Add JSDoc
axisType?: 'xAxis' | 'yAxis'; + /** + * The unique identifier of the axis this component belongs to. + * Used internally to track rendered ticks per axis in the Redux store. + */ axisId?: AxisId;
304-337: Potential unnecessary re-dispatches due to unstableticksreference in the dependency array.
finalTicksis recomputed viagetTicks(...)on every render ofTicks, producing a new array reference even when the tick content hasn't changed. This causes theuseEffectcleanup + re-dispatch cycle on every render, which will trigger subscribers of therenderedTicksstate to re-render as well.Consider either:
- Memoizing
finalTicksin theTickscomponent (e.g., viauseMemowith stable inputs), or- Doing a shallow comparison of the previous ticks before dispatching inside the effect.
Option 2: Guard dispatch with shallow equality check
function RenderedTicksReporter({ ticks, axisType, axisId, }: { ticks: ReadonlyArray<TickItemType>; axisType: 'xAxis' | 'yAxis' | undefined; axisId: AxisId | undefined; }) { const dispatch = useAppDispatch(); + const prevTicksRef = useRef<ReadonlyArray<TickItemType> | null>(null); useEffect(() => { if (axisId == null || axisType == null) { return noop; } // Filter out irrelevant internal properties before exposing externally const tickItems = ticks.map(tick => ({ value: tick.value, coordinate: tick.coordinate, offset: tick.offset, index: tick.index, })); + // Skip dispatch if ticks haven't meaningfully changed + if ( + prevTicksRef.current != null && + prevTicksRef.current.length === ticks.length && + prevTicksRef.current.every((t, i) => t.value === ticks[i].value && t.coordinate === ticks[i].coordinate) + ) { + return noop; + } + prevTicksRef.current = ticks; dispatch(setRenderedTicks({ ticks: tickItems, axisId, axisType })); return () => { dispatch( removeRenderedTicks({ axisId, axisType, }), ); }; }, [dispatch, ticks, axisId, axisType]); return null; }#!/bin/bash # Check how finalTicks is produced and whether it could be memoized rg -n "finalTicks" --type=ts --type=tsx -C3
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #6990 +/- ##
==========================================
+ Coverage 90.10% 90.12% +0.02%
==========================================
Files 522 523 +1
Lines 38848 38914 +66
Branches 5347 5357 +10
==========================================
+ Hits 35004 35073 +69
+ Misses 3835 3832 -3
Partials 9 9 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…ead of CartesianTickItem
Bundle ReportChanges will increase total bundle size by 6.03kB (0.21%) ⬆️. This is within the configured threshold ✅ Detailed changes
Affected Assets, Files, and Routes:view changes for bundle: recharts/bundle-umdAssets Changed:
view changes for bundle: recharts/bundle-es6Assets Changed:
view changes for bundle: recharts/bundle-cjsAssets Changed:
|
|
Staging Deployment Details
These deployments will remain available for 30 days. To update snapshots: Comment |
1 similar comment
|
Staging Deployment Details
These deployments will remain available for 30 days. To update snapshots: Comment |
Fixes a regression introduced in #6990 where the `textAnchor` prop on XAxis/YAxis was being ignored. The calculated `textAnchor` (from orientation/mirror) was overriding the user-provided value because it was assigned after spreading `axisProps`. **Change:** Now uses user-provided `textAnchor` if present, otherwise falls back to the calculated value. Fixes #7027 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Improvements** * Chart tick labels now respect an explicit horizontal text alignment when provided; otherwise they fall back to the previous automatic horizontal alignment. Vertical alignment behavior remains unchanged. * **Tests** * Added tests that verify tick label horizontal alignment when a user-specified alignment is present and when defaults are used, and confirm the expected tick count. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Jarvis <jarvis@pierreeurope.github>
Description
The problem is that CartesianAxis uses DOM to calculate the actual font size and overlap, and then uses that to decide which ticks get rendered. That's legit but doesn't fit in the reducer nor selector. So I added a new slice where we effectively cache the calculation and return it from the tick hooks.
I have also removed the incorrect domain as the robot told me in the other PR.
Related Issue
Fixes #6978
Screenshots (if appropriate):
Screen.Recording.2026-02-11.at.20.14.03.mov
Types of changes
Checklist:
Summary by CodeRabbit