Feat: Implement 'equidistantPreserveEnd' interval option for XAxis and YAxis (#6642)#6661
Conversation
WalkthroughAdds a new AxisInterval value Changes
Sequence Diagram(s)sequenceDiagram
participant Caller as getTicks(...)
participant Resolver as getEquidistantPreserveEndTicks
participant Helpers as isVisible / getTickSize / getEveryNthWithCondition
Caller->>Resolver: interval === "equidistantPreserveEnd", pass sign, boundaries, ticks, params
Resolver->>Helpers: compute initial tick size & minTickGap
loop iterate ticks with dynamic stepsize
Resolver->>Helpers: isVisible(tick, { start, end }) [checks isLastTick for end]
alt visible
Resolver->>Resolver: select tick, advance by stepsize
else not visible
Resolver->>Resolver: reset/adjust stepsize, skip tick
end
end
Resolver->>Helpers: fallback -> getEveryNthWithCondition if needed
Resolver-->>Caller: return selected ticks (ensuring end tick preserved when applicable)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ 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/util/types.ts (1)
747-759: Update AxisInterval docs to describeequidistantPreserveEndsemanticsYou’ve extended
AxisIntervalwith'equidistantPreserveEnd', but the preceding comment only documents theequidistantPreserveStartvariant. It would be good to explicitly describe howequidistantPreserveEndbehaves (e.g., “selects an N and pattern so that ticks are equidistant and the end tick is preserved”), to keep the type docs self‑contained and discoverable.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/cartesian/getEquidistantTicks.ts(1 hunks)src/cartesian/getTicks.ts(2 hunks)src/util/types.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/cartesian/getEquidistantTicks.ts (4)
src/cartesian/getTicks.ts (1)
Sign(9-9)src/util/types.ts (1)
CartesianTickItem(775-779)src/util/getEveryNthWithCondition.ts (1)
getEveryNthWithCondition(12-24)src/util/TickUtils.ts (1)
isVisible(26-42)
src/cartesian/getTicks.ts (1)
src/cartesian/getEquidistantTicks.ts (1)
getEquidistantPreserveEndTicks(66-126)
🔇 Additional comments (1)
src/cartesian/getTicks.ts (1)
6-7: Wiring for newequidistantPreserveEndinterval looks consistentThe import of
getEquidistantPreserveEndTicksand the newinterval === 'equidistantPreserveEnd'branch are correctly aligned with the existingequidistantPreserveStarthandling and keep the rest of the control flow unchanged. No issues from the integration side; behavior now depends entirely on the correctness ofgetEquidistantPreserveEndTicks.If you have tests around XAxis/YAxis
interval="equidistantPreserveStart", consider cloning them for"equidistantPreserveEnd"to validate the new path end‑to‑end.Also applies to: 171-176
|
Thanks for the detailed review and guidance! I've reworked the getEquidistantPreserveEndTicks function to use the end-anchored stride pattern as suggested, ensuring the last tick is correctly preserved. The updated code is pushed to the branch and should be ready for another review. |
|
Hi @Om-Mishra09 , thanks for the PR. We're going to need a unit test, and I would like to see a visual regression test too: https://github.com/recharts/recharts/blob/main/DEVELOPING.md |
PavelVanecek
left a comment
There was a problem hiding this comment.
Let's have couple of tests. Is the AxisInterval mentioned somewhere in docs, where we should add this new variant? Is it somewhere in storybook?
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #6661 +/- ##
==========================================
- Coverage 94.04% 94.03% -0.02%
==========================================
Files 497 497
Lines 42564 42659 +95
Branches 4859 4885 +26
==========================================
+ Hits 40031 40115 +84
- Misses 2528 2539 +11
Partials 5 5 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
test/cartesian/getEquidistantTicks.spec.ts (1)
36-75: Good test coverage for the new function.The test suite covers the empty array case and multiple end-anchored scenarios. The parameterized tests clearly demonstrate the end-preservation behavior.
Consider adding test cases for:
- Mixed scenarios where early ticks collide but later ones don't
- Boundary conditions where minTickGap is non-zero
- Negative sign parameter to verify reversed direction handling
- Single tick in array (edge case)
These would further strengthen confidence in the implementation, though current coverage validates core functionality.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/util/types.ts(1 hunks)storybook/stories/Examples/EquidistantPreserveEnd.stories.tsx(1 hunks)test/cartesian/getEquidistantTicks.spec.ts(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
test/cartesian/getEquidistantTicks.spec.ts (2)
src/util/types.ts (2)
CartesianTickItem(776-780)TickItem(769-774)src/cartesian/getEquidistantTicks.ts (1)
getEquidistantPreserveEndTicks(66-137)
🔇 Additional comments (3)
src/util/types.ts (1)
752-760: LGTM! Documentation updated as requested.The JSDoc comment clearly describes the new
equidistantPreserveEndoption, and the multi-line union format improves readability. This addresses the previous review comment requesting documentation updates.storybook/stories/Examples/EquidistantPreserveEnd.stories.tsx (1)
1-33: Verify visual regression test coverage.The Storybook example effectively demonstrates the new
equidistantPreserveEndinterval option. However, please confirm whether this story fulfills the visual regression test requirement mentioned in the PR comments, or if additional test infrastructure is needed per DEVELOPING.md guidelines.Consider adding additional story variants to demonstrate edge cases:
- Very narrow container width (many ticks need to be hidden)
- Few data points (all ticks fit)
- Different tick label sizes
test/cartesian/getEquidistantTicks.spec.ts (1)
1-1: LGTM!The import correctly adds the new
getEquidistantPreserveEndTicksfunction for testing.
|
I've addressed all the feedback! Unit Tests: Added comprehensive tests in test/cartesian/getEquidistantTicks.spec.ts covering edge cases. Visual Test: Added a Storybook example in storybook/stories/Examples/EquidistantPreserveEnd.stories.tsx for visual regression testing. Documentation: Updated the JSDoc in src/util/types.ts to describe the new option. Ready for review! |
|
Great catch regarding the unit tests! You were right that the previous tests relied too heavily on manipulating tick size. I have updated the test case in getEquidistantTicks.spec.ts to use standard tick sizes and rely purely on minTickGap to force the step calculation logic (e.g. forcing a step size of 2). This properly verifies the math without manually engineering the tick sizes. Ready for review! |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
test/cartesian/getEquidistantTicks.spec.ts (2)
36-74: Test suite properly validates the new function and addresses past review concerns.The test at lines 46-73 correctly validates that the function skips ticks to satisfy
minTickGapwhile preserving the end tick. Tracing through the logic confirms that with the given parameters (10px coordinates apart, 10px tick size, 8px minTickGap), stepsize=1 would cause collisions while stepsize=2 succeeds, returning indices 0, 2, 4 as expected. This would NOT pass with an identity function, which addresses the previous reviewer's concern.Consider adding test cases for better coverage:
- All ticks fit: When minTickGap allows all ticks to be shown, verify all are returned.
- Only last tick fits: When minTickGap is very large, verify only the last tick is returned.
- Sign = -1: Validate the function works correctly with negative sign (reverse direction).
- Edge cases: Single tick, two ticks, etc.
Example test case for "all ticks fit":
it('should return all ticks when they all fit within minTickGap constraints', () => { const ticks = [ { value: 'A', coordinate: 0, index: 0 }, { value: 'B', coordinate: 50, index: 1 }, { value: 'C', coordinate: 100, index: 2 }, ]; const getTickSizeStatic = () => 10; const result = getEquidistantPreserveEndTicks( 1, { start: 0, end: 150 }, getTickSizeStatic, ticks, 5, // minTickGap - ticks are far enough apart ); expect(result.map(t => t.value)).toEqual(['A', 'B', 'C']); });
59-61: Clarify comment for better readability.The comment could be more precise:
- Line 59: "The ticks are 10px apart" is slightly ambiguous. Consider: "The tick coordinates are 10px apart."
- Lines 60-61: "we expect indices 4, 2, 0" suggests reverse order, but the result is in forward order [0, 2, 4]. Consider: "Since we anchor at the end (Index 4) with stepsize 2 and offset 0, the sequence includes indices 0, 2, 4."
Apply this diff for clarity:
- // The ticks are 10px apart. We set minTickGap to 8px. + // The tick coordinates are 10px apart. We set minTickGap to 8px. // This forces the algorithm to skip every other tick (Step Size 2) to avoid overlap. - // Since we anchor at the end (Index 4), we expect indices 4, 2, 0. + // Since we anchor at the end (Index 4) with stepsize 2, the sequence includes indices 0, 2, 4.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
test/cartesian/getEquidistantTicks.spec.ts(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
test/cartesian/getEquidistantTicks.spec.ts (2)
src/util/types.ts (1)
CartesianTickItem(776-780)src/cartesian/getEquidistantTicks.ts (1)
getEquidistantPreserveEndTicks(66-137)
|
Going to hold off on merging this until I can make a quick change and get a patch out for 4.5.1 |
Fixes #6642
Description
This PR introduces a new string literal
'equidistantPreserveEnd'for theintervalprop onXAxisandYAxis. This option addresses the request in Issue #6642 by ensuring that ticks are displayed equidistantly while guaranteeing the last tick is always visible.Implementation Details
getEquidistantPreserveEndTicks, which modifies the collision detection logic to prioritize the display of the final tick (index === ticks.length - 1).src/cartesian/getTicks.ts.AxisIntervaltype definition insrc/util/types.ts.Motivation and Context
This enhancement provides users with greater control over tick placement, allowing for non-overlapping, equidistant spacing with the certainty that the end of the axis range is marked.
Summary by CodeRabbit
New Features
Documentation / Examples
Tests
✏️ Tip: You can customize this high-level summary in your review settings.