From c5f4982b540eaaa930a1fb029f100be375b2f6f4 Mon Sep 17 00:00:00 2001 From: Philipp Schneider Date: Wed, 11 Feb 2026 09:52:33 +0100 Subject: [PATCH 1/3] test --- src/core/chart-api/chart-extra-pointer.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/chart-api/chart-extra-pointer.tsx b/src/core/chart-api/chart-extra-pointer.tsx index 305f3e12..3b8173c2 100644 --- a/src/core/chart-api/chart-extra-pointer.tsx +++ b/src/core/chart-api/chart-extra-pointer.tsx @@ -34,12 +34,12 @@ export class ChartExtraPointer { public onChartLoad = (chart: Highcharts.Chart) => { chart.container.addEventListener("mousemove", this.onChartMousemove); - chart.container.addEventListener("mouseout", this.onChartMouseout); + chart.container.addEventListener("mouseleave", this.onChartMouseout); }; public onChartDestroy = () => { this.context.chartOrNull?.container?.removeEventListener("mousemove", this.onChartMousemove); - this.context.chartOrNull?.container?.removeEventListener("mouseout", this.onChartMouseout); + this.context.chartOrNull?.container?.removeEventListener("mouseleave", this.onChartMouseout); }; // This event is triggered by Highcharts when the cursor is over a Highcharts point. We leave this to From 286234642a5324d3e1e025a56f892b5a3a0c3ce4 Mon Sep 17 00:00:00 2001 From: Philipp Schneider Date: Wed, 18 Feb 2026 10:04:53 +0100 Subject: [PATCH 2/3] line chart fix --- .../__tests__/chart-core-tooltip.test.tsx | 42 +++++++++++++++++++ src/core/chart-api/chart-extra-pointer.tsx | 10 ++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/core/__tests__/chart-core-tooltip.test.tsx b/src/core/__tests__/chart-core-tooltip.test.tsx index cb2559a3..cf77f402 100644 --- a/src/core/__tests__/chart-core-tooltip.test.tsx +++ b/src/core/__tests__/chart-core-tooltip.test.tsx @@ -819,6 +819,48 @@ describe("CoreChart: tooltip", () => { }); }); + test("hides tooltip when mouse moves outside plot area to the left", async () => { + const onHighlight = vi.fn(); + const onClearHighlight = vi.fn(); + const { wrapper } = renderChart({ + highcharts, + options: { + series: lineSeries, + chart: { + events: { + load() { + this.plotTop = 0; + this.plotLeft = 50; + this.plotWidth = 100; + this.plotHeight = 100; + }, + }, + }, + }, + onHighlight, + onClearHighlight, + getTooltipContent: () => ({ + header: () => "Tooltip title", + body: () => "Tooltip body", + }), + }); + + // Move mouse inside plot area to show tooltip + act(() => hc.getChart().container.dispatchEvent(createMouseMoveEvent({ pageX: 75, pageY: 50 }))); + + await waitFor(() => { + expect(wrapper.findTooltip()).not.toBe(null); + }); + + // Move mouse to the left, outside the plot area (plotLeft=50, so pageX=30 is outside) + act(() => hc.getChart().container.dispatchEvent(createMouseMoveEvent({ pageX: 30, pageY: 50 }))); + + await waitFor(() => { + expect(onClearHighlight).toHaveBeenCalled(); + expect(wrapper.findTooltip()).toBe(null); + }); + }); + describe("Escape key dismissal", () => { test("dismisses hover tooltip with Escape key when keyboard navigation is disabled", async () => { const { wrapper } = renderChart({ diff --git a/src/core/chart-api/chart-extra-pointer.tsx b/src/core/chart-api/chart-extra-pointer.tsx index 3b8173c2..d12af5b7 100644 --- a/src/core/chart-api/chart-extra-pointer.tsx +++ b/src/core/chart-api/chart-extra-pointer.tsx @@ -110,10 +110,16 @@ export class ChartExtraPointer { this.setHoveredGroup(matchedGroup); } // If the plotX, plotY are outside of the series area (e.g. if the pointer is above axis titles or ticks), - // we clear the group hover state and trigger the on-hover-lost after a short delay. + // we immediately clear all hover state. Unlike transitions between points/groups within the plot area, + // there is no need to debounce here as the cursor has definitively left the data region. else { + this.hoveredPoint = null; this.hoveredGroup = null; - this.clearHover(); + this.hoverLostCall.cancelPrevious(); + if (!this.tooltipHovered) { + this.handlers.onHoverLost(); + this.applyCursorStyle(); + } } }; From f840145fe3c803abc2e50ecca75632ffdde8eddf Mon Sep 17 00:00:00 2001 From: Philipp Schneider Date: Thu, 19 Feb 2026 12:03:37 +0100 Subject: [PATCH 3/3] fix line chart --- .../__tests__/chart-core-tooltip.test.tsx | 4 +++- src/core/chart-api/chart-extra-pointer.tsx | 22 ++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/core/__tests__/chart-core-tooltip.test.tsx b/src/core/__tests__/chart-core-tooltip.test.tsx index cf77f402..95fff339 100644 --- a/src/core/__tests__/chart-core-tooltip.test.tsx +++ b/src/core/__tests__/chart-core-tooltip.test.tsx @@ -181,7 +181,9 @@ describe("CoreChart: tooltip", () => { expect(wrapper.findTooltip()!.findFooter()!.getElement().textContent).toBe("Tooltip footer"); }); - act(() => hc.getChart().container.dispatchEvent(new MouseEvent("mouseout", { bubbles: true, cancelable: true }))); + act(() => + hc.getChart().container.dispatchEvent(new MouseEvent("mouseleave", { bubbles: false, cancelable: false })), + ); await waitFor(() => { expect(onClearHighlight).toHaveBeenCalled(); diff --git a/src/core/chart-api/chart-extra-pointer.tsx b/src/core/chart-api/chart-extra-pointer.tsx index d12af5b7..f0da44cb 100644 --- a/src/core/chart-api/chart-extra-pointer.tsx +++ b/src/core/chart-api/chart-extra-pointer.tsx @@ -65,11 +65,16 @@ export class ChartExtraPointer { this.hoveredGroup = null; }; - // When the pointer leaves the tooltip it can hover another point or group. If that does not happen, - // the on-hover-lost handler is called after a short delay. + // When the pointer leaves the tooltip, we immediately check if any point or group is still hovered. + // If not, we fire onHoverLost immediately to prevent the tooltip from staying visible when the mouse + // exits the chart area through the tooltip (e.g., moving left). public onMouseLeaveTooltip = () => { this.tooltipHovered = false; - this.clearHover(); + this.hoverLostCall.cancelPrevious(); + if (!this.hoveredPoint && !this.hoveredGroup) { + this.handlers.onHoverLost(); + this.applyCursorStyle(); + } }; // The mouse-move handler takes all move events inside the chart, and its purpose is to capture hover for groups @@ -123,11 +128,16 @@ export class ChartExtraPointer { } }; - // This event is triggered when the pointer leaves the chart area. Here, it is technically not necessary to add - // a delay before calling the on-hover-lost handler, but it is done for consistency in the UX. + // This event is triggered when the pointer leaves the chart container entirely. + // We immediately clear all hover state since the cursor has definitively left the chart. private onChartMouseout = () => { + this.hoveredPoint = null; this.hoveredGroup = null; - this.clearHover(); + this.hoverLostCall.cancelPrevious(); + if (!this.tooltipHovered) { + this.handlers.onHoverLost(); + this.applyCursorStyle(); + } }; // This event is triggered by Highcharts when there is a click inside the chart plot. It might or might not include