test(YAxis): failing repro for #7362 — function domain doesn't render ticks on empty/all-null data#7384
Conversation
WalkthroughAdds a Vitest RTL spec that renders a LineChart with a numeric YAxis using explicit ticks and allowDataOverflow, defines two function-based domain variants, and includes three intentionally failing tests asserting the explicit tick ladder for empty/all-null datasets. ChangesYAxis
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
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)
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.
🧹 Nitpick comments (1)
test/cartesian/YAxis/YAxis.7362.derivedDomain.spec.tsx (1)
47-60: ⚡ Quick winAdvance timers after rendering to account for Redux batching.
The helper renders a chart but doesn't call
vi.runOnlyPendingTimers()afterward. According to coding guidelines, timer advancement is needed after renders due to Redux autoBatchEnhancer dependency on timers andrequestAnimationFrame, even whenisAnimationActive={false}.⏱️ Proposed fix
function renderDerivedDomainChart({ data, domain }: { data: ReadonlyArray<Datum>; domain: AxisDomain }) { const axisDomainSpy = vi.fn(); const renderResult = render( <LineChart width={500} height={300} data={data}> <XAxis dataKey="name" /> <YAxis type="number" domain={domain} allowDataOverflow ticks={[...explicitTicks]} /> <Line type="monotone" dataKey="value" stroke="`#8884d8`" isAnimationActive={false} connectNulls /> <ExpectAxisDomain assert={axisDomainSpy} axisType="yAxis" /> </LineChart>, ); + + vi.runOnlyPendingTimers(); return { ...renderResult, axisDomainSpy }; }As per coding guidelines: Call
vi.runOnlyPendingTimers()to advance timers after renders when not usingcreateSelectorTestCasehelper due to Redux autoBatchEnhancer dependency on timers andrequestAnimationFrame.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/cartesian/YAxis/YAxis.7362.derivedDomain.spec.tsx` around lines 47 - 60, The renderDerivedDomainChart helper does not advance timers after rendering, which is required because Redux autoBatchEnhancer and requestAnimationFrame can queue work even when isAnimationActive={false}; after calling render(...) inside renderDerivedDomainChart, call vi.runOnlyPendingTimers() to flush pending timers so ExpectAxisDomain (axisDomainSpy) observes the settled state—add the vi.runOnlyPendingTimers() invocation immediately after the render(...) call in renderDerivedDomainChart.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@test/cartesian/YAxis/YAxis.7362.derivedDomain.spec.tsx`:
- Around line 47-60: The renderDerivedDomainChart helper does not advance timers
after rendering, which is required because Redux autoBatchEnhancer and
requestAnimationFrame can queue work even when isAnimationActive={false}; after
calling render(...) inside renderDerivedDomainChart, call
vi.runOnlyPendingTimers() to flush pending timers so ExpectAxisDomain
(axisDomainSpy) observes the settled state—add the vi.runOnlyPendingTimers()
invocation immediately after the render(...) call in renderDerivedDomainChart.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 20376359-4727-4f7f-905a-57eaecb4268a
📒 Files selected for processing (1)
test/cartesian/YAxis/YAxis.7362.derivedDomain.spec.tsx
Bundle ReportBundle size has no change ✅ |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #7384 +/- ##
=======================================
Coverage 89.05% 89.05%
=======================================
Files 548 548
Lines 40577 40577
Branches 5581 5580 -1
=======================================
Hits 36136 36136
Misses 4432 4432
Partials 9 9 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
TDD red test documenting the unresolved half of recharts#7362. PR recharts#7379 showed a LITERAL domain ([0,100]) + ticks + allowDataOverflow already renders the ladder on empty/ all-null data. This spec covers a FUNCTION domain (e.g. [0, (dataMax) => finite fallback], or a whole-domain function), which recharts skips when there is no data domain — so the ladder does not render. Uses it.fails: the assertions express the desired behavior and currently throw, so the suite stays green while documenting the gap; they flip to FAILING once recharts invokes a function domain without data, signalling it's time to drop .fails. Refs recharts#7362. Signed-off-by: Nicolas <[email protected]>
ffaf7c1 to
411ffc1
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
test/cartesian/YAxis/YAxis.7362.derivedDomain.spec.tsx (1)
76-91: ⚡ Quick winConsider adding a test for empty data with wholeDomainFunction.
The suite tests
functionUpperBoundwith bothallNullDataand emptydata: [], but only testswholeDomainFunctionwithallNullData. Adding anit.failstest for empty data withwholeDomainFunctionwould provide symmetric coverage and more thoroughly document the bug.➕ Suggested additional test case
it.fails('renders the ladder for all-null data with a whole-domain function', () => { const { container } = renderChart({ data: allNullData, domain: wholeDomainFunction }); expectYAxisTicks(container, ladder); }); + + it.fails('renders the ladder for empty data with a whole-domain function', () => { + const { container } = renderChart({ data: [], domain: wholeDomainFunction }); + expectYAxisTicks(container, ladder); + }); });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/cartesian/YAxis/YAxis.7362.derivedDomain.spec.tsx` around lines 76 - 91, Add a symmetric failing test for the empty-data case using wholeDomainFunction: mirror the existing it.fails that uses allNullData with wholeDomainFunction by adding an it.fails that calls renderChart({ data: [], domain: wholeDomainFunction }) and asserts expectYAxisTicks(container, ladder); locate the other tests around the describe('YAxis issue 7362 …') block and use the same pattern as the functionUpperBound empty-data test to ensure coverage for wholeDomainFunction with empty data.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@test/cartesian/YAxis/YAxis.7362.derivedDomain.spec.tsx`:
- Around line 76-91: Add a symmetric failing test for the empty-data case using
wholeDomainFunction: mirror the existing it.fails that uses allNullData with
wholeDomainFunction by adding an it.fails that calls renderChart({ data: [],
domain: wholeDomainFunction }) and asserts expectYAxisTicks(container, ladder);
locate the other tests around the describe('YAxis issue 7362 …') block and use
the same pattern as the functionUpperBound empty-data test to ensure coverage
for wholeDomainFunction with empty data.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ea803c5f-7e29-4488-983e-e58d7f2967f0
📒 Files selected for processing (1)
test/cartesian/YAxis/YAxis.7362.derivedDomain.spec.tsx
…olve on empty data (#7362) (#7393) > Draft — proposes the approach @PavelVanecek suggested on #7362 (derive the domain from explicit `ticks` when data is absent). Opening as a draft for a steer on the approach before polishing. ## What Follow-up to #7384 (the failing repro). When a numeric axis supplies an explicit `ticks` array but its `domain` cannot be resolved without data — a **function** bound, or `'auto'`/`'dataMin'`/`'dataMax'` — the scale never builds on empty/all-null data, so the axis renders **zero ticks**. A literal numeric domain already works; this extends that to derived domains. This avoids the typing problem raised on the issue (the `AxisDomain` function form is typed `(NumberDomain, boolean) => NumberDomain`, so feeding it `undefined`/`NaN` would be a breaking change): instead of calling the user's function without data, we fall back to the explicit ticks' extent. ## How `combineNumericalDomain` falls back to `[Math.min(...ticks), Math.max(...ticks)]` of the explicit numeric ticks **only when all of**: - the user domain could not be resolved without data (`numericalDomainSpecifiedWithoutRequiringData` → `undefined`), and - `parseNumericalUserDomain` also returned `undefined` (no data domain — empty/all-null data and no domain-extending reference elements), and - `allowDataOverflow` is set, and - the axis is `type="number"` with ≥1 numeric tick. So it **never** overrides a resolvable literal domain, **never** engages when data is present, and leaves category / polar / tooltip paths untouched. - adds `combineNumericTicksDomain` + `selectNumericTicksDomain` (the ticks `[min,max]` extent) - threads it as an optional input to `combineNumericalDomain` (polar/tooltip pass nothing → unchanged) - **does not** touch `parseNumericalUserDomain` / `numericalDomainSpecifiedWithoutRequiringData` — their "undefined without data" contract is preserved - converts the #7384 `it.fails` repro into passing tests + adds edge-case coverage (unsorted/non-numeric/single ticks, tickCount-only, literal-wins, no-overflow, data-present guards) ## Test plan - [x] `YAxis.7362.derivedDomain.spec.tsx` — 13 cases (positive + negative guards) pass - [x] Affected selectors/axis/scatter/polar inventory + full `unit:lib` suite green locally (vitest 4) - [x] format / lint / typecheck clean - [ ] Maintainer steer on the approach - [ ] Open question: should the fallback also apply without `allowDataOverflow`? (kept it required, for parity with the literal-domain path) Refs #7362. Follows #7384. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Resolved an issue where axis ticks would not render properly when no data is available but explicit numeric ticks are specified. The axis domain now intelligently falls back to the explicit tick range when `allowDataOverflow` is enabled. * **Tests** * Expanded test coverage to verify axis behavior with explicit ticks on empty or null data scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: Nicolas <[email protected]>
What
A single, TDD-style red spec —
test/cartesian/YAxis/YAxis.7362.derivedDomain.spec.tsx— documenting the unresolved half of #7362. Tests only; nosrc/changes and no behavior/contract change.Context
PR #7379 (
YAxis.7362.spec.tsx) showed the literal case already works:type="number"+domain={[0, 100]}+ticks(ortickCount) +allowDataOverflowrenders the0/25/50/75/100ladder even when every series value isnull(ordata={[]}).This spec covers the case that does not work yet: a function
domain. A function bound already decides its own value — e.g.[0, (dataMax) => (Number.isFinite(dataMax) && dataMax > 0 ? dataMax : 100)]returns a finite100when there is no data. The desired behavior is that recharts invokes that function even when the data domain is empty, so the returned finite bounds resolve the scale and the explicitticksrender — exactly like the literal case.Current behavior (the gap)
recharts skips function domains when there is no data domain:
numericalDomainSpecifiedWithoutRequiringDatareturnsundefinedfor any function, andparseNumericalUserDomainonly calls the function whendataDomain != null(
src/util/isDomainSpecifiedByUser.ts). With empty/all-null data the data domain isnull, so the function is never called and the axis renders 0 ticks.Why
it.failsThe three specs assert the desired behavior (the ladder renders), which currently throws — so
it.failspasses and the suite stays green while documenting the gap. They flip to FAILING the moment recharts resolves a function domain without data, signalling it's time to drop.failsand lock the behavior in.Cases covered:
[0, (dataMax) => …], all-null datadata={[]}() => [0, 100], all-null dataRunning
Refs #7362. Related: #7379, #4433, #4426.