diff --git a/resources/js/components/ui/chart-tooltip-portal.test.tsx b/resources/js/components/ui/chart-tooltip-portal.test.tsx new file mode 100644 index 00000000..0ba6e2eb --- /dev/null +++ b/resources/js/components/ui/chart-tooltip-portal.test.tsx @@ -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 ( +
+ + + tooltip + +
+ ); +} + +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(); + + 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(); + + const afterMount = rectSpy.mock.calls.length; + + rerender(); + + expect(rectSpy.mock.calls.length).toBeGreaterThan(afterMount); + }); +}); diff --git a/resources/js/components/ui/chart.tsx b/resources/js/components/ui/chart.tsx index f01acb9a..886200cd 100644 --- a/resources/js/components/ui/chart.tsx +++ b/resources/js/components/ui/chart.tsx @@ -686,6 +686,7 @@ export { ChartContainer, ChartTooltip, ChartTooltipContent, + ChartTooltipPortal, ChartLegend, ChartLegendContent, ChartStyle,