From 2aebd269bc16c051dfb72d532db7c4e7ff76a493 Mon Sep 17 00:00:00 2001 From: Julius Volz Date: Wed, 4 Mar 2026 14:51:26 +0100 Subject: [PATCH] UI: Fix tooltip Y-offset drift for multiple graph panels getBoundingClientRect() was cached in the setSize hook, which only fires on chart creation/resize. The cached viewport-relative coordinates became stale after scrolling, causing the tooltip to appear increasingly offset on charts further down the page. Fixed by calling getBoundingClientRect() on every setCursor invocation to always get accurate viewport-relative coordinates. Signed-off-by: Julius Volz --- .../src/pages/query/uPlotChartHelpers.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/web/ui/mantine-ui/src/pages/query/uPlotChartHelpers.ts b/web/ui/mantine-ui/src/pages/query/uPlotChartHelpers.ts index ba6cdbae41..816ddf7578 100644 --- a/web/ui/mantine-ui/src/pages/query/uPlotChartHelpers.ts +++ b/web/ui/mantine-ui/src/pages/query/uPlotChartHelpers.ts @@ -83,8 +83,6 @@ const formatLabels = (labels: { [key: string]: string }): string => ` const tooltipPlugin = (useLocalTime: boolean, data: AlignedData) => { let over: HTMLDivElement; - let boundingLeft: number; - let boundingTop: number; let selectedSeriesIdx: number | null = null; const overlay = document.createElement("div"); @@ -111,12 +109,6 @@ const tooltipPlugin = (useLocalTime: boolean, data: AlignedData) => { destroy: () => { overlay.remove(); }, - // When the chart is resized, store the bounding box of the overlay. - setSize: () => { - const bbox = over.getBoundingClientRect(); - boundingLeft = bbox.left; - boundingTop = bbox.top; - }, // When a series is selected by hovering close to it, store the // index of the selected series, so we can update the hover tooltip // in setCursor. @@ -150,8 +142,12 @@ const tooltipPlugin = (useLocalTime: boolean, data: AlignedData) => { } const color = series.stroke(u, selectedSeriesIdx); - const x = left + boundingLeft; - const y = top + boundingTop; + // Get the bounding rect fresh on every cursor move to account for + // page scrolling, which would otherwise cause a growing Y offset + // for charts further down the page. + const bbox = over.getBoundingClientRect(); + const x = left + bbox.left; + const y = top + bbox.top; overlay.innerHTML = `
${formatTimestamp(ts, useLocalTime)}