Skip to content

test(YAxis): failing repro for #7362 — function domain doesn't render ticks on empty/all-null data#7384

Merged
PavelVanecek merged 1 commit into
recharts:mainfrom
blackfuel-ai:test/7362-derived-domain-repro
May 30, 2026
Merged

test(YAxis): failing repro for #7362 — function domain doesn't render ticks on empty/all-null data#7384
PavelVanecek merged 1 commit into
recharts:mainfrom
blackfuel-ai:test/7362-derived-domain-repro

Conversation

@nlenepveu

@nlenepveu nlenepveu commented May 29, 2026

Copy link
Copy Markdown
Contributor

What

A single, TDD-style red spec — test/cartesian/YAxis/YAxis.7362.derivedDomain.spec.tsx — documenting the unresolved half of #7362. Tests only; no src/ 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 (or tickCount) + allowDataOverflow renders the 0/25/50/75/100 ladder even when every series value is null (or data={[]}).

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 finite 100 when 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 explicit ticks render — exactly like the literal case.

Current behavior (the gap)

recharts skips function domains when there is no data domain:

  • numericalDomainSpecifiedWithoutRequiringData returns undefined for any function, and
  • parseNumericalUserDomain only calls the function when dataDomain != null

(src/util/isDomainSpecifiedByUser.ts). With empty/all-null data the data domain is null, so the function is never called and the axis renders 0 ticks.

Why it.fails

The three specs assert the desired behavior (the ladder renders), which currently throws — so it.fails passes 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 .fails and lock the behavior in.

Cases covered:

  • function upper bound [0, (dataMax) => …], all-null data
  • function upper bound, empty data={[]}
  • whole-domain function () => [0, 100], all-null data

Running

npm ci
npx vitest run --config vitest.config.mts test/cartesian/YAxis/YAxis.7362.derivedDomain.spec.tsx

Refs #7362. Related: #7379, #4433, #4426.

@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

Adds 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.

Changes

YAxis #7362 test coverage

Layer / File(s) Summary
Header, imports, and constants
test/cartesian/YAxis/YAxis.7362.derivedDomain.spec.tsx
File header documents the issue, imports React/Vitest/RTL, declares a Datum type, defines the explicit tick ladder, and an all-null dataset constant.
Render helper and domain variants
test/cartesian/YAxis/YAxis.7362.derivedDomain.spec.tsx
Adds renderChart helper that mounts LineChart/XAxis/YAxis(type="number", ticks, allowDataOverflow)/Line, and declares two function-based AxisDomain variants (function upper bound and fixed whole-domain).
Failing tests for derived domains
test/cartesian/YAxis/YAxis.7362.derivedDomain.spec.tsx
Adds describe with three it.fails tests asserting the explicit 0/25/50/75/100 tick ladder for all-null and empty data using the function-domain variants.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • #7362: Test file targets YAxis tick rendering with function-domain and empty/all-null data, directly exercising the behavior described in the issue.

Suggested reviewers

  • PavelVanecek
  • ckifer
🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title directly and specifically describes the main change: a failing test (repro) for issue #7362 covering function domain behavior on empty/null data.
Description check ✅ Passed The PR description covers key sections: a clear 'What' explaining it's a TDD-style red spec, context about related work (#7379), current behavior gap, reasoning for it.fails, specific test cases, and running instructions. It aligns well with the template's intent.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
test/cartesian/YAxis/YAxis.7362.derivedDomain.spec.tsx (1)

47-60: ⚡ Quick win

Advance 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 and requestAnimationFrame, even when isAnimationActive={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 using createSelectorTestCase helper due to Redux autoBatchEnhancer dependency on timers and requestAnimationFrame.

🤖 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

📥 Commits

Reviewing files that changed from the base of the PR and between 021ef7b and ffaf7c1.

📒 Files selected for processing (1)
  • test/cartesian/YAxis/YAxis.7362.derivedDomain.spec.tsx

@codecov

codecov Bot commented May 29, 2026

Copy link
Copy Markdown

Bundle Report

Bundle size has no change ✅

@codecov

codecov Bot commented May 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.05%. Comparing base (021ef7b) to head (411ffc1).

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.
📢 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.

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]>
@nlenepveu
nlenepveu force-pushed the test/7362-derived-domain-repro branch from ffaf7c1 to 411ffc1 Compare May 29, 2026 23:45
@nlenepveu nlenepveu changed the title test(YAxis): reproduce #7362 — non-literal domain + allowDataOverflow drops ticks on empty data test(YAxis): failing repro for #7362 — function domain doesn't render ticks on empty/all-null data May 29, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
test/cartesian/YAxis/YAxis.7362.derivedDomain.spec.tsx (1)

76-91: ⚡ Quick win

Consider adding a test for empty data with wholeDomainFunction.

The suite tests functionUpperBound with both allNullData and empty data: [], but only tests wholeDomainFunction with allNullData. Adding an it.fails test for empty data with wholeDomainFunction would 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

📥 Commits

Reviewing files that changed from the base of the PR and between ffaf7c1 and 411ffc1.

📒 Files selected for processing (1)
  • test/cartesian/YAxis/YAxis.7362.derivedDomain.spec.tsx

@PavelVanecek
PavelVanecek merged commit c973308 into recharts:main May 30, 2026
58 checks passed
@nlenepveu
nlenepveu deleted the test/7362-derived-domain-repro branch May 31, 2026 15:27
PavelVanecek pushed a commit that referenced this pull request Jun 24, 2026
…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]>
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.

2 participants