test(chart): guard the tooltip effect against the render-loop regression
The unit test for computeTooltipPosition covers the flip/clamp math, but the math was never broken — the crash came from the effect re-running on its own setPos. Add a component test (both review agents' top request) that renders ChartTooltipPortal and asserts the positioning effect does NOT re-run when the component re-renders with an unchanged coordinate, and DOES re-run when the coordinate changes. Verified it fails against the pre-fix dependency-less effect. ChartTooltipPortal is exported for this, matching how StackedBarShape is exported for its test.
This commit is contained in:
parent
651c7d725c
commit
1c1602db87
|
|
@ -0,0 +1,58 @@
|
|||
import { fireEvent, render } from '@testing-library/react';
|
||||
import * as React from 'react';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { ChartTooltipPortal } from './chart';
|
||||
|
||||
/**
|
||||
* Renders the portal inside a `.recharts-wrapper` (the ancestor its layout
|
||||
* effect looks up) and exposes a button that bumps unrelated state to force a
|
||||
* re-render without touching `coordinate`.
|
||||
*/
|
||||
function Harness({ coordinate }: { coordinate: { x: number; y: number } }) {
|
||||
const [, setTick] = React.useState(0);
|
||||
|
||||
return (
|
||||
<div className="recharts-wrapper">
|
||||
<button onClick={() => setTick((t) => t + 1)}>rerender</button>
|
||||
<ChartTooltipPortal coordinate={coordinate}>
|
||||
<span>tooltip</span>
|
||||
</ChartTooltipPortal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
describe('ChartTooltipPortal', () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
// The positioning effect reads the wrapper's rect, so a call to
|
||||
// getBoundingClientRect is a proxy for "the effect ran".
|
||||
it('does not recompute its position on a re-render with an unchanged coordinate (regression: PHP-LARAVEL-3B render loop)', () => {
|
||||
const rectSpy = vi.spyOn(Element.prototype, 'getBoundingClientRect');
|
||||
const { getByText } = render(<Harness coordinate={{ x: 10, y: 20 }} />);
|
||||
|
||||
const afterMount = rectSpy.mock.calls.length;
|
||||
expect(afterMount).toBeGreaterThan(0);
|
||||
|
||||
fireEvent.click(getByText('rerender'));
|
||||
fireEvent.click(getByText('rerender'));
|
||||
|
||||
// With the pre-fix dependency-less effect this count would climb on
|
||||
// every render and the setPos feedback loop would eventually throw
|
||||
// "Maximum update depth exceeded".
|
||||
expect(rectSpy.mock.calls.length).toBe(afterMount);
|
||||
});
|
||||
|
||||
it('recomputes its position when the coordinate changes', () => {
|
||||
const rectSpy = vi.spyOn(Element.prototype, 'getBoundingClientRect');
|
||||
const { rerender } = render(<Harness coordinate={{ x: 10, y: 20 }} />);
|
||||
|
||||
const afterMount = rectSpy.mock.calls.length;
|
||||
|
||||
rerender(<Harness coordinate={{ x: 200, y: 300 }} />);
|
||||
|
||||
expect(rectSpy.mock.calls.length).toBeGreaterThan(afterMount);
|
||||
});
|
||||
});
|
||||
|
|
@ -686,6 +686,7 @@ export {
|
|||
ChartContainer,
|
||||
ChartTooltip,
|
||||
ChartTooltipContent,
|
||||
ChartTooltipPortal,
|
||||
ChartLegend,
|
||||
ChartLegendContent,
|
||||
ChartStyle,
|
||||
|
|
|
|||
Loading…
Reference in New Issue