Skip to content

Commit 8aa51c1

Browse files
enmanuelduranEnmanuel Duran
andauthored
chore: avoid re-rendering tooltips on each cursor pixel change over charts (#82)
* chore: avoid re-rendering tooltips on each cursor pixel change over charts * chore: using getSeriesId as a better way to check series --------- Co-authored-by: Enmanuel Duran <[email protected]>
1 parent 1cbf328 commit 8aa51c1

File tree

2 files changed

+56
-3
lines changed

2 files changed

+56
-3
lines changed

src/core/__tests__/chart-core-tooltip.test.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,45 @@ describe("CoreChart: tooltip", () => {
316316
}
317317
});
318318

319+
test("only re-renders when group has changed", () => {
320+
const getTooltipContentMock = vi.fn(() => ({
321+
header: () => "Tooltip title",
322+
body: () => "Tooltip body",
323+
footer: () => "Tooltip footer",
324+
}));
325+
renderChart({
326+
highcharts,
327+
options: {
328+
series: lineSeries,
329+
chart: {
330+
events: {
331+
load() {
332+
this.plotTop = 0;
333+
this.plotLeft = 0;
334+
this.plotWidth = 100;
335+
this.plotHeight = 100;
336+
},
337+
},
338+
},
339+
},
340+
getTooltipContent: getTooltipContentMock,
341+
});
342+
343+
act(() => {
344+
hc.getChart().container.dispatchEvent(createMouseMoveEvent({ pageX: 1, pageY: 0 }));
345+
});
346+
347+
act(() => {
348+
hc.getChart().container.dispatchEvent(createMouseMoveEvent({ pageX: 1, pageY: 2 }));
349+
});
350+
351+
act(() => {
352+
hc.getChart().container.dispatchEvent(createMouseMoveEvent({ pageX: 1, pageY: 4 }));
353+
});
354+
355+
expect(getTooltipContentMock).toHaveBeenCalledTimes(2);
356+
});
357+
319358
test("renders highlight markers", async () => {
320359
const { wrapper } = renderChart({
321360
highcharts,

src/core/chart-api/chart-extra-tooltip.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import * as Styles from "../../internal/chart-styles";
1010
import { renderMarker } from "../../internal/components/series-marker/render-marker";
1111
import AsyncStore from "../../internal/utils/async-store";
1212
import { SVGRendererPool, SVGRendererSingle } from "../../internal/utils/renderer-utils";
13-
import { DebouncedCall } from "../../internal/utils/utils";
13+
import { DebouncedCall, isEqualArrays } from "../../internal/utils/utils";
1414
import { Rect } from "../interfaces";
15-
import { getGroupRect, getPointRect, isXThreshold } from "../utils";
15+
import { getGroupRect, getPointRect, getSeriesId, isXThreshold } from "../utils";
1616
import { ChartExtraContext } from "./chart-extra-context";
1717

1818
import testClasses from "../test-classes/styles.css.js";
@@ -78,7 +78,7 @@ export class ChartExtraTooltip extends AsyncStore<ReactiveTooltipState> {
7878

7979
public showTooltipOnGroup(group: readonly Highcharts.Point[], ignoreLock = false) {
8080
if (!this.tooltipLock || ignoreLock) {
81-
this.set(() => ({ visible: true, pinned: false, point: null, group }));
81+
this.setGroupIfDifferent(group);
8282
this.onRenderTooltip({ point: null, group });
8383
}
8484
}
@@ -97,6 +97,20 @@ export class ChartExtraTooltip extends AsyncStore<ReactiveTooltipState> {
9797
}
9898
}
9999

100+
// Avoid re-rendering the tooltip on every cursor position change when hovering over the graph.
101+
// Just re-render when the groups are different.
102+
private setGroupIfDifferent(group: readonly Highcharts.Point[]) {
103+
function isGroupEqual(a: Highcharts.Point, b: Highcharts.Point) {
104+
return a?.x === b?.x && a?.y === b?.y && getSeriesId(a?.series) === getSeriesId(b?.series);
105+
}
106+
107+
this.set((prev) => {
108+
return isEqualArrays(prev.group, group, isGroupEqual)
109+
? prev
110+
: { visible: true, pinned: false, point: null, group };
111+
});
112+
}
113+
100114
private onRenderTooltip = (props: { point: null | Highcharts.Point; group: readonly Highcharts.Point[] }) => {
101115
const chartType =
102116
this.context.chart().series.find((s) => s.type === "pie" || s.type === "solidgauge")?.type ?? "cartesian";

0 commit comments

Comments
 (0)