From d4473c0e97bbe1cec7c8c067c5b95e48d655fb83 Mon Sep 17 00:00:00 2001 From: saket3395 Date: Fri, 31 Jul 2026 12:07:28 +0530 Subject: [PATCH 1/3] fix: correct DataTable tooltip position on paginated pages Fixes #1848. Tooltip row index was resolved against the virtualized (page-local) row twice: once when handling the mouse event, and again when matching the tooltip condition/data lookup. On page 1 these two resolutions coincide, masking the bug; on later pages they diverge, so tooltips render using the wrong row's position/data. Ports the fix from plotly/dash-table#906 (pre-merge into this repo): - cellEvents.ts: store the raw virtualized idx in currentTooltip.row instead of pre-resolving it, matching how handleMoveHeader already behaves for header tooltips. - tooltip.ts: resolve virtualized.indices[row - offset.rows] once, at lookup time in getSelectedTooltip, instead of relying on a pre-resolved value. Manually verified the diff is a faithful port of #906 and consistent with the surrounding code (handleMoveHeader already stores the raw idx). Not yet run through the JS build/test suite locally. --- .../src/dash-table/derived/table/tooltip.ts | 2 +- .../dash-table/src/dash-table/handlers/cellEvents.ts | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/components/dash-table/src/dash-table/derived/table/tooltip.ts b/components/dash-table/src/dash-table/derived/table/tooltip.ts index 54620dea99..f2651df322 100644 --- a/components/dash-table/src/dash-table/derived/table/tooltip.ts +++ b/components/dash-table/src/dash-table/derived/table/tooltip.ts @@ -42,7 +42,7 @@ function getSelectedTooltip( return ( !tt.if || (ifColumnId(tt.if, id) && - ifRowIndex(tt.if, row) && + ifRowIndex(tt.if, virtualized.indices[row - virtualized.offset.rows]) && ifFilter( tt.if, virtualized.data[row - virtualized.offset.rows] diff --git a/components/dash-table/src/dash-table/handlers/cellEvents.ts b/components/dash-table/src/dash-table/handlers/cellEvents.ts index 8ece59511e..6fc6d02dfa 100644 --- a/components/dash-table/src/dash-table/handlers/cellEvents.ts +++ b/components/dash-table/src/dash-table/handlers/cellEvents.ts @@ -160,13 +160,13 @@ export const handleEnter = ( idx: number, i: number ) => { - const {setState, virtualized, visibleColumns} = propsFn(); + const {setState, visibleColumns} = propsFn(); setState({ currentTooltip: { header: false, id: visibleColumns[i].id, - row: virtualized.indices[idx - virtualized.offset.rows] + row: idx } }); }; @@ -202,15 +202,14 @@ export const handleMove = ( idx: number, i: number ) => { - const {currentTooltip, setState, virtualized, visibleColumns} = propsFn(); + const {currentTooltip, setState, visibleColumns} = propsFn(); const c = visibleColumns[i]; - const realIdx = virtualized.indices[idx - virtualized.offset.rows]; if ( currentTooltip && currentTooltip.id === c.id && - currentTooltip.row === realIdx && + currentTooltip.row === idx && !currentTooltip.header ) { return; @@ -220,7 +219,7 @@ export const handleMove = ( currentTooltip: { header: false, id: c.id, - row: realIdx + row: idx } }); }; From e3a18668ab522d7748bb55dc46e2d955b04bda93 Mon Sep 17 00:00:00 2001 From: saket3395 Date: Sun, 2 Aug 2026 10:58:43 +0530 Subject: [PATCH 2/3] fix: resolve tooltip_data lookup to absolute row index Addresses review feedback from @AnnMarieW: after fixing tooltip position, tooltip *text* on page 2+ still showed page 1's content. tooltip_data is indexed by each row's absolute position in the full, unpaginated dataset. getSelectedTooltip's non-header branch read it as tooltip_data[row], but row is now the page-local virtualized index (needed for the position fix). The ifRowIndex check a few lines above already resolves this same row via virtualized.indices[row - offset.rows] to get the absolute index -- the tooltip_data lookup needs the same resolution, which it was missing. --- .../dash-table/src/dash-table/derived/table/tooltip.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components/dash-table/src/dash-table/derived/table/tooltip.ts b/components/dash-table/src/dash-table/derived/table/tooltip.ts index f2651df322..34e3f46639 100644 --- a/components/dash-table/src/dash-table/derived/table/tooltip.ts +++ b/components/dash-table/src/dash-table/derived/table/tooltip.ts @@ -62,7 +62,10 @@ function getSelectedTooltip( ? headerTooltip?.[row] : headerTooltip; } else { - tooltip = tooltip_data?.[row]?.[id]; + tooltip = + tooltip_data?.[virtualized.indices[row - virtualized.offset.rows]]?.[ + id + ]; } if (tooltip) { From 3c14df7c7c50d3846f1b929faeecbd307ea32146 Mon Sep 17 00:00:00 2001 From: saket3395 Date: Tue, 4 Aug 2026 08:30:56 +0530 Subject: [PATCH 3/3] fix: clear stale tooltip when data changes (e.g. row deletion) Addresses review feedback from @AnnMarieW: after fixing tooltip position and content lookup for pagination, deleting a row left the deleted row's tooltip visible, with all subsequent rows' tooltips shifted and showing the wrong text. currentTooltip.row is a virtualized/page-local row index captured at hover/move time (handleEnter/handleMove in cellEvents.ts). It is never invalidated when the underlying dataset changes shape -- so after a row is added, removed, or reordered, the same stored index now maps to a different row via virtualized.indices, both for DOM position lookup (data-dash-row) and for the tooltip_data/tooltip_conditional content lookup in getSelectedTooltip. Rather than try to remap the stale index (fragile: deletion can shift every subsequent row by a different amount depending on where it happened), this clears currentTooltip whenever data or tooltip_data changes identity while a tooltip is showing -- the same outcome a user would expect from moving off the cell, since the cell's underlying data has effectively changed out from under them. Guarded on 'currentTooltip &&' so this is a no-op (and doesn't loop) once currentTooltip is already undefined, including on the re-render triggered by the setState call itself. --- .../components/ControlledTable/index.tsx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/components/dash-table/src/dash-table/components/ControlledTable/index.tsx b/components/dash-table/src/dash-table/components/ControlledTable/index.tsx index 7be2006ba5..de84d401d3 100644 --- a/components/dash-table/src/dash-table/components/ControlledTable/index.tsx +++ b/components/dash-table/src/dash-table/components/ControlledTable/index.tsx @@ -180,7 +180,21 @@ export default class ControlledTable extends PureComponent document.removeEventListener('copy', this.handleCopy); } - componentDidUpdate() { + componentDidUpdate(prevProps: ControlledTableProps) { + const {currentTooltip, data, setState, tooltip_data} = this.props; + + // Row indices captured in currentTooltip (set on hover/move) are only + // valid for the data snapshot they were captured against. If rows are + // added/removed/reordered, those indices point at different rows now, + // so the tooltip would show stale position/content for a row that may + // no longer even be the one the user is hovering (dash#1848). + if ( + currentTooltip && + (data !== prevProps.data || tooltip_data !== prevProps.tooltip_data) + ) { + setState({currentTooltip: undefined}); + } + this.updateStylesheet(); this.updateUiViewport();