From 2396722d9989191b52544098d2ef8049ad8d52ad Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 26 Jan 2026 22:23:40 +0000 Subject: [PATCH 1/6] Fix cell inspector showing incorrect data when opened via keyboard shortcut The toggle function was overwriting the store's value (set by hover's updateValue call) with a potentially stale local value from the CellInspector component. Now when opening, toggle() preserves the store's existing value if one was set via updateValue(), falling back to the passed value only if the store's value is empty. Fixes: APP-693 Co-authored-by: eric.okuma --- .../src/features/dashboards/stores/cell-inspector-store.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/web-common/src/features/dashboards/stores/cell-inspector-store.ts b/web-common/src/features/dashboards/stores/cell-inspector-store.ts index b75979bdc1e..3f5ebd684aa 100644 --- a/web-common/src/features/dashboards/stores/cell-inspector-store.ts +++ b/web-common/src/features/dashboards/stores/cell-inspector-store.ts @@ -34,7 +34,9 @@ function createCellInspectorStore() { update((state) => ({ ...state, isOpen: !state.isOpen, - value: state.isOpen ? state.value : value, + // When opening: prefer store's existing value (from hover) if set, fall back to passed value + // When closing: keep the current value + value: state.isOpen ? state.value : state.value || value, })), }; } From 1157c3b20ba9f70e99188b22d0293060520dd4fc Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 26 Jan 2026 22:27:28 +0000 Subject: [PATCH 2/6] Support null values in cell inspector - Updated cell-inspector-store to accept null values (string | null type) - Changed toggle to use nullish coalescing (??) to preserve null values - Updated CellInspector to show 'null' instead of 'No value' for null cells - Updated subscription to sync null values when inspector is open - Modified all cell components to call updateValue even for null values: - LeaderboardCell.svelte - Cell.svelte (virtualized-table) - VirtualTableCell.svelte - FlatTable.svelte - NestedTable.svelte - RegularTable.svelte - KPI.svelte - MeasureBigNumber.svelte Fixes: APP-693 Co-authored-by: eric.okuma --- .../src/components/CellInspector.svelte | 5 +-- .../virtualized-table/VirtualTableCell.svelte | 16 ++++----- .../virtualized-table/core/Cell.svelte | 6 ++-- .../features/canvas/components/kpi/KPI.svelte | 32 ++++++++++------- .../big-number/MeasureBigNumber.svelte | 16 ++++----- .../leaderboard/LeaderboardCell.svelte | 16 ++++----- .../dashboards/pivot/FlatTable.svelte | 16 ++++----- .../dashboards/pivot/NestedTable.svelte | 16 ++++----- .../dashboards/pivot/RegularTable.svelte | 36 ++++++++++--------- .../dashboards/stores/cell-inspector-store.ts | 10 +++--- 10 files changed, 91 insertions(+), 78 deletions(-) diff --git a/web-common/src/components/CellInspector.svelte b/web-common/src/components/CellInspector.svelte index 2431ccfd5c7..f2cca0bee06 100644 --- a/web-common/src/components/CellInspector.svelte +++ b/web-common/src/components/CellInspector.svelte @@ -21,7 +21,8 @@ // Subscribe to the cellInspectorStore to keep the component in sync const unsubscribe = cellInspectorStore.subscribe((state) => { isOpen = state.isOpen; - if (state.value && state.isOpen && !isLocked) { + // Update value when open and not locked, including null values + if (state.isOpen && !isLocked && state.value !== "") { value = state.value; } }); @@ -175,7 +176,7 @@ class:items-center={!isJson} > {#if value === null} - No value + null {:else} diff --git a/web-common/src/components/virtualized-table/core/Cell.svelte b/web-common/src/components/virtualized-table/core/Cell.svelte index baaf4df4498..4e7a09ea266 100644 --- a/web-common/src/components/virtualized-table/core/Cell.svelte +++ b/web-common/src/components/virtualized-table/core/Cell.svelte @@ -49,9 +49,9 @@ onInspect(row.index); cellActive = true; // Update the cell inspector store with the cell value - if (value !== undefined && value !== null) { - cellInspectorStore.updateValue(value.toString()); - } + cellInspectorStore.updateValue( + value === null || value === undefined ? null : value.toString(), + ); } function onSelect(e: MouseEvent) { diff --git a/web-common/src/features/canvas/components/kpi/KPI.svelte b/web-common/src/features/canvas/components/kpi/KPI.svelte index de6165f9186..78624768cb5 100644 --- a/web-common/src/features/canvas/components/kpi/KPI.svelte +++ b/web-common/src/features/canvas/components/kpi/KPI.svelte @@ -89,29 +89,37 @@ function handleBigNumberMouseOver() { const displayValue = hoveredPoints?.[0]?.value != null ? currentValue : primaryTotal; - if (displayValue !== undefined && displayValue !== null) { - cellInspectorStore.updateValue(displayValue.toString()); - } + cellInspectorStore.updateValue( + displayValue === null || displayValue === undefined + ? null + : displayValue.toString(), + ); } function handleBigNumberFocus() { const displayValue = hoveredPoints?.[0]?.value != null ? currentValue : primaryTotal; - if (displayValue !== undefined && displayValue !== null) { - cellInspectorStore.updateValue(displayValue.toString()); - } + cellInspectorStore.updateValue( + displayValue === null || displayValue === undefined + ? null + : displayValue.toString(), + ); } function handleComparisonMouseOver() { - if (comparisonVal !== undefined && comparisonVal !== null) { - cellInspectorStore.updateValue(comparisonVal.toString()); - } + cellInspectorStore.updateValue( + comparisonVal === null || comparisonVal === undefined + ? null + : comparisonVal.toString(), + ); } function handleComparisonFocus() { - if (comparisonVal !== undefined && comparisonVal !== null) { - cellInspectorStore.updateValue(comparisonVal.toString()); - } + cellInspectorStore.updateValue( + comparisonVal === null || comparisonVal === undefined + ? null + : comparisonVal.toString(), + ); } diff --git a/web-common/src/features/dashboards/big-number/MeasureBigNumber.svelte b/web-common/src/features/dashboards/big-number/MeasureBigNumber.svelte index 7af8e36a4cb..8401d1a1a38 100644 --- a/web-common/src/features/dashboards/big-number/MeasureBigNumber.svelte +++ b/web-common/src/features/dashboards/big-number/MeasureBigNumber.svelte @@ -94,17 +94,17 @@ $: useDiv = isMeasureExpanded || !withTimeseries; function handleMouseOver() { - if (value !== undefined && value !== null) { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue(value.toString()); - } + // Always update the value in the store, but don't change visibility + cellInspectorStore.updateValue( + value === null || value === undefined ? null : value.toString(), + ); } function handleFocus() { - if (value !== undefined && value !== null) { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue(value.toString()); - } + // Always update the value in the store, but don't change visibility + cellInspectorStore.updateValue( + value === null || value === undefined ? null : value.toString(), + ); } diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardCell.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardCell.svelte index d6d4b0db72f..a18a6070156 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardCell.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardCell.svelte @@ -78,16 +78,16 @@ shift: () => shiftClickHandler(value), })} on:pointerover={() => { - if (value?.toString) { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue(value.toString()); - } + // Always update the value in the store, but don't change visibility + cellInspectorStore.updateValue( + value === null || value === undefined ? null : value.toString(), + ); }} on:focus={() => { - if (value?.toString) { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue(value.toString()); - } + // Always update the value in the store, but don't change visibility + cellInspectorStore.updateValue( + value === null || value === undefined ? null : value.toString(), + ); }} on:mouseleave={() => (tooltipActive = false)} style:background diff --git a/web-common/src/features/dashboards/pivot/FlatTable.svelte b/web-common/src/features/dashboards/pivot/FlatTable.svelte index 865a70c6958..7ce05e6162b 100644 --- a/web-common/src/features/dashboards/pivot/FlatTable.svelte +++ b/web-common/src/features/dashboards/pivot/FlatTable.svelte @@ -204,17 +204,17 @@ data-columnid={cell.column.id} on:mouseover={() => { const value = cell.getValue(); - if (value !== undefined && value !== null) { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue(String(value)); - } + // Always update the value in the store, but don't change visibility + cellInspectorStore.updateValue( + value === null || value === undefined ? null : String(value), + ); }} on:focus={() => { const value = cell.getValue(); - if (value !== undefined && value !== null) { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue(String(value)); - } + // Always update the value in the store, but don't change visibility + cellInspectorStore.updateValue( + value === null || value === undefined ? null : String(value), + ); }} > {#if result?.component && result?.props} diff --git a/web-common/src/features/dashboards/pivot/NestedTable.svelte b/web-common/src/features/dashboards/pivot/NestedTable.svelte index b6cedd68ae9..10c39bdcf57 100644 --- a/web-common/src/features/dashboards/pivot/NestedTable.svelte +++ b/web-common/src/features/dashboards/pivot/NestedTable.svelte @@ -348,17 +348,17 @@ class:totals-column={i > 0 && i <= measureCount} on:mouseover={() => { const value = cell.getValue(); - if (value !== undefined && value !== null) { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue(String(value)); - } + // Always update the value in the store, but don't change visibility + cellInspectorStore.updateValue( + value === null || value === undefined ? null : String(value), + ); }} on:focus={() => { const value = cell.getValue(); - if (value !== undefined && value !== null) { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue(String(value)); - } + // Always update the value in the store, but don't change visibility + cellInspectorStore.updateValue( + value === null || value === undefined ? null : String(value), + ); }} > {#if result?.component && result?.props} diff --git a/web-common/src/features/dashboards/pivot/RegularTable.svelte b/web-common/src/features/dashboards/pivot/RegularTable.svelte index 0360f3e97b4..c728a128ea3 100644 --- a/web-common/src/features/dashboards/pivot/RegularTable.svelte +++ b/web-common/src/features/dashboards/pivot/RegularTable.svelte @@ -115,18 +115,22 @@ // Add mouseover event to update the value in the store without changing visibility th.onmouseover = () => { - if (value?.value !== undefined && value?.value !== null) { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue(String(value.value)); - } + // Always update the value in the store, but don't change visibility + cellInspectorStore.updateValue( + value?.value === null || value?.value === undefined + ? null + : String(value.value), + ); }; // Add focus event to update the value in the store without changing visibility th.onfocus = () => { - if (value?.value !== undefined && value?.value !== null) { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue(String(value.value)); - } + // Always update the value in the store, but don't change visibility + cellInspectorStore.updateValue( + value?.value === null || value?.value === undefined + ? null + : String(value.value), + ); }; const maybeWidth = getRowHeaderWidth(x); if (maybeWidth) { @@ -163,17 +167,17 @@ // Add mouseover event to update the value in the store without changing visibility td.onmouseover = () => { - if (value !== undefined && value !== null) { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue(String(value)); - } + // Always update the value in the store, but don't change visibility + cellInspectorStore.updateValue( + value === null || value === undefined ? null : String(value), + ); }; td.onfocus = () => { - if (value !== undefined && value !== null) { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue(String(value)); - } + // Always update the value in the store, but don't change visibility + cellInspectorStore.updateValue( + value === null || value === undefined ? null : String(value), + ); }; const maybeWidth = getColumnWidth(x); diff --git a/web-common/src/features/dashboards/stores/cell-inspector-store.ts b/web-common/src/features/dashboards/stores/cell-inspector-store.ts index 3f5ebd684aa..e0e2d0b713f 100644 --- a/web-common/src/features/dashboards/stores/cell-inspector-store.ts +++ b/web-common/src/features/dashboards/stores/cell-inspector-store.ts @@ -2,7 +2,7 @@ import { writable } from "svelte/store"; interface CellInspectorState { isOpen: boolean; - value: string; + value: string | null; } function createCellInspectorStore() { @@ -13,7 +13,7 @@ function createCellInspectorStore() { return { subscribe, - open: (value: string) => + open: (value: string | null) => update((state) => ({ ...state, isOpen: true, @@ -25,18 +25,18 @@ function createCellInspectorStore() { isOpen: false, })), // Update the value without changing visibility - updateValue: (value: string) => + updateValue: (value: string | null) => update((state) => ({ ...state, value, })), - toggle: (value: string) => + toggle: (value: string | null) => update((state) => ({ ...state, isOpen: !state.isOpen, // When opening: prefer store's existing value (from hover) if set, fall back to passed value // When closing: keep the current value - value: state.isOpen ? state.value : state.value || value, + value: state.isOpen ? state.value : state.value ?? value, })), }; } From 622a934447df74a418b5b0bb36a00a70843d9102 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 26 Jan 2026 22:34:41 +0000 Subject: [PATCH 3/6] Fix prettier formatting in cell-inspector-store Co-authored-by: eric.okuma --- .../src/features/dashboards/stores/cell-inspector-store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web-common/src/features/dashboards/stores/cell-inspector-store.ts b/web-common/src/features/dashboards/stores/cell-inspector-store.ts index e0e2d0b713f..b206997975d 100644 --- a/web-common/src/features/dashboards/stores/cell-inspector-store.ts +++ b/web-common/src/features/dashboards/stores/cell-inspector-store.ts @@ -36,7 +36,7 @@ function createCellInspectorStore() { isOpen: !state.isOpen, // When opening: prefer store's existing value (from hover) if set, fall back to passed value // When closing: keep the current value - value: state.isOpen ? state.value : state.value ?? value, + value: state.isOpen ? state.value : (state.value ?? value), })), }; } From 1f8b58e2f1420fd35dea79d59de1978e7a694021 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 26 Jan 2026 22:49:02 +0000 Subject: [PATCH 4/6] Fix toggle to preserve null values from hover The nullish coalescing operator (??) was treating null as a fallback case, causing the inspector to show a stale value instead of null. Changed to check for empty string (the initial state) instead, so that null values from hovering over null cells are properly preserved when toggling the inspector open. Co-authored-by: eric.okuma --- .../src/features/dashboards/stores/cell-inspector-store.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/web-common/src/features/dashboards/stores/cell-inspector-store.ts b/web-common/src/features/dashboards/stores/cell-inspector-store.ts index b206997975d..411839716a1 100644 --- a/web-common/src/features/dashboards/stores/cell-inspector-store.ts +++ b/web-common/src/features/dashboards/stores/cell-inspector-store.ts @@ -36,7 +36,12 @@ function createCellInspectorStore() { isOpen: !state.isOpen, // When opening: prefer store's existing value (from hover) if set, fall back to passed value // When closing: keep the current value - value: state.isOpen ? state.value : (state.value ?? value), + // Note: check for empty string (initial state) rather than nullish, to preserve null values + value: state.isOpen + ? state.value + : state.value !== "" + ? state.value + : value, })), }; } From 0bd0b15477ba7c11ebaee1d09fe6cc5d2fcd102e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 26 Jan 2026 23:03:56 +0000 Subject: [PATCH 5/6] Fix empty string cell values displaying stale data Changed sentinel value from empty string to undefined: - undefined = no value set yet (initial state / not hovered) - null = cell contains null value - string (including '') = cell contains a string value Also added distinct display for empty strings showing '(empty string)' to distinguish from null or missing values. Co-authored-by: eric.okuma --- web-common/src/components/CellInspector.svelte | 7 +++++-- .../features/dashboards/stores/cell-inspector-store.ts | 9 +++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/web-common/src/components/CellInspector.svelte b/web-common/src/components/CellInspector.svelte index f2cca0bee06..7ad0f208fb6 100644 --- a/web-common/src/components/CellInspector.svelte +++ b/web-common/src/components/CellInspector.svelte @@ -21,8 +21,9 @@ // Subscribe to the cellInspectorStore to keep the component in sync const unsubscribe = cellInspectorStore.subscribe((state) => { isOpen = state.isOpen; - // Update value when open and not locked, including null values - if (state.isOpen && !isLocked && state.value !== "") { + // Update value when open and not locked, including null and empty string values + // Only skip if value is undefined (meaning no value was set via hover) + if (state.isOpen && !isLocked && state.value !== undefined) { value = state.value; } }); @@ -177,6 +178,8 @@ > {#if value === null} null + {:else if value === ""} + (empty string) {:else} ({ isOpen: false, - value: "", + value: undefined, }); return { @@ -36,10 +37,10 @@ function createCellInspectorStore() { isOpen: !state.isOpen, // When opening: prefer store's existing value (from hover) if set, fall back to passed value // When closing: keep the current value - // Note: check for empty string (initial state) rather than nullish, to preserve null values + // undefined means no value was set via hover, so fall back to passed value value: state.isOpen ? state.value - : state.value !== "" + : state.value !== undefined ? state.value : value, })), From 69072f6e0cc8146dacb27d9dbc5b1b194a3679db Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 27 Jan 2026 17:47:24 +0000 Subject: [PATCH 6/6] Refactor cell inspector store based on code review - Added hasValue boolean field instead of using undefined as sentinel - Moved null check logic into updateValue() which now accepts unknown - Simplified all cell components to just pass raw value to updateValue() - normalizeValue() helper converts null/undefined to null, else to string Co-authored-by: eric.okuma --- .../src/components/CellInspector.svelte | 5 +-- .../virtualized-table/VirtualTableCell.svelte | 10 +---- .../virtualized-table/core/Cell.svelte | 5 +-- .../features/canvas/components/kpi/KPI.svelte | 24 ++--------- .../big-number/MeasureBigNumber.svelte | 10 +---- .../leaderboard/LeaderboardCell.svelte | 14 +------ .../dashboards/pivot/FlatTable.svelte | 16 +------- .../dashboards/pivot/NestedTable.svelte | 16 +------- .../dashboards/pivot/RegularTable.svelte | 37 ++--------------- .../dashboards/stores/cell-inspector-store.ts | 41 +++++++++++++------ 10 files changed, 50 insertions(+), 128 deletions(-) diff --git a/web-common/src/components/CellInspector.svelte b/web-common/src/components/CellInspector.svelte index 7ad0f208fb6..03e2e1d3b61 100644 --- a/web-common/src/components/CellInspector.svelte +++ b/web-common/src/components/CellInspector.svelte @@ -21,9 +21,8 @@ // Subscribe to the cellInspectorStore to keep the component in sync const unsubscribe = cellInspectorStore.subscribe((state) => { isOpen = state.isOpen; - // Update value when open and not locked, including null and empty string values - // Only skip if value is undefined (meaning no value was set via hover) - if (state.isOpen && !isLocked && state.value !== undefined) { + // Update value when open and not locked, and a value has been set via hover + if (state.isOpen && !isLocked && state.hasValue) { value = state.value; } }); diff --git a/web-common/src/components/virtualized-table/VirtualTableCell.svelte b/web-common/src/components/virtualized-table/VirtualTableCell.svelte index a3c80b79dc1..e39d8513faa 100644 --- a/web-common/src/components/virtualized-table/VirtualTableCell.svelte +++ b/web-common/src/components/virtualized-table/VirtualTableCell.svelte @@ -16,17 +16,11 @@ | undefined; function handleMouseOver() { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue( - value === null || value === undefined ? null : value.toString(), - ); + cellInspectorStore.updateValue(value); } function handleFocus() { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue( - value === null || value === undefined ? null : value.toString(), - ); + cellInspectorStore.updateValue(value); } diff --git a/web-common/src/components/virtualized-table/core/Cell.svelte b/web-common/src/components/virtualized-table/core/Cell.svelte index 4e7a09ea266..19f0df7c7d4 100644 --- a/web-common/src/components/virtualized-table/core/Cell.svelte +++ b/web-common/src/components/virtualized-table/core/Cell.svelte @@ -48,10 +48,7 @@ function onFocus() { onInspect(row.index); cellActive = true; - // Update the cell inspector store with the cell value - cellInspectorStore.updateValue( - value === null || value === undefined ? null : value.toString(), - ); + cellInspectorStore.updateValue(value); } function onSelect(e: MouseEvent) { diff --git a/web-common/src/features/canvas/components/kpi/KPI.svelte b/web-common/src/features/canvas/components/kpi/KPI.svelte index 78624768cb5..dbc1a637b14 100644 --- a/web-common/src/features/canvas/components/kpi/KPI.svelte +++ b/web-common/src/features/canvas/components/kpi/KPI.svelte @@ -89,37 +89,21 @@ function handleBigNumberMouseOver() { const displayValue = hoveredPoints?.[0]?.value != null ? currentValue : primaryTotal; - cellInspectorStore.updateValue( - displayValue === null || displayValue === undefined - ? null - : displayValue.toString(), - ); + cellInspectorStore.updateValue(displayValue); } function handleBigNumberFocus() { const displayValue = hoveredPoints?.[0]?.value != null ? currentValue : primaryTotal; - cellInspectorStore.updateValue( - displayValue === null || displayValue === undefined - ? null - : displayValue.toString(), - ); + cellInspectorStore.updateValue(displayValue); } function handleComparisonMouseOver() { - cellInspectorStore.updateValue( - comparisonVal === null || comparisonVal === undefined - ? null - : comparisonVal.toString(), - ); + cellInspectorStore.updateValue(comparisonVal); } function handleComparisonFocus() { - cellInspectorStore.updateValue( - comparisonVal === null || comparisonVal === undefined - ? null - : comparisonVal.toString(), - ); + cellInspectorStore.updateValue(comparisonVal); } diff --git a/web-common/src/features/dashboards/big-number/MeasureBigNumber.svelte b/web-common/src/features/dashboards/big-number/MeasureBigNumber.svelte index 8401d1a1a38..68e2aa85964 100644 --- a/web-common/src/features/dashboards/big-number/MeasureBigNumber.svelte +++ b/web-common/src/features/dashboards/big-number/MeasureBigNumber.svelte @@ -94,17 +94,11 @@ $: useDiv = isMeasureExpanded || !withTimeseries; function handleMouseOver() { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue( - value === null || value === undefined ? null : value.toString(), - ); + cellInspectorStore.updateValue(value); } function handleFocus() { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue( - value === null || value === undefined ? null : value.toString(), - ); + cellInspectorStore.updateValue(value); } diff --git a/web-common/src/features/dashboards/leaderboard/LeaderboardCell.svelte b/web-common/src/features/dashboards/leaderboard/LeaderboardCell.svelte index a18a6070156..f2af9dde6ad 100644 --- a/web-common/src/features/dashboards/leaderboard/LeaderboardCell.svelte +++ b/web-common/src/features/dashboards/leaderboard/LeaderboardCell.svelte @@ -77,18 +77,8 @@ on:click={modified({ shift: () => shiftClickHandler(value), })} - on:pointerover={() => { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue( - value === null || value === undefined ? null : value.toString(), - ); - }} - on:focus={() => { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue( - value === null || value === undefined ? null : value.toString(), - ); - }} + on:pointerover={() => cellInspectorStore.updateValue(value)} + on:focus={() => cellInspectorStore.updateValue(value)} on:mouseleave={() => (tooltipActive = false)} style:background class="{cellType}-cell {className}" diff --git a/web-common/src/features/dashboards/pivot/FlatTable.svelte b/web-common/src/features/dashboards/pivot/FlatTable.svelte index 7ce05e6162b..1de73d28a60 100644 --- a/web-common/src/features/dashboards/pivot/FlatTable.svelte +++ b/web-common/src/features/dashboards/pivot/FlatTable.svelte @@ -202,20 +202,8 @@ data-value={cell.getValue()} data-rowid={cell.row.id} data-columnid={cell.column.id} - on:mouseover={() => { - const value = cell.getValue(); - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue( - value === null || value === undefined ? null : String(value), - ); - }} - on:focus={() => { - const value = cell.getValue(); - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue( - value === null || value === undefined ? null : String(value), - ); - }} + on:mouseover={() => cellInspectorStore.updateValue(cell.getValue())} + on:focus={() => cellInspectorStore.updateValue(cell.getValue())} > {#if result?.component && result?.props} 0 && i <= measureCount} - on:mouseover={() => { - const value = cell.getValue(); - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue( - value === null || value === undefined ? null : String(value), - ); - }} - on:focus={() => { - const value = cell.getValue(); - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue( - value === null || value === undefined ? null : String(value), - ); - }} + on:mouseover={() => cellInspectorStore.updateValue(cell.getValue())} + on:focus={() => cellInspectorStore.updateValue(cell.getValue())} > {#if result?.component && result?.props} { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue( - value?.value === null || value?.value === undefined - ? null - : String(value.value), - ); - }; - - // Add focus event to update the value in the store without changing visibility - th.onfocus = () => { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue( - value?.value === null || value?.value === undefined - ? null - : String(value.value), - ); - }; + th.onmouseover = () => cellInspectorStore.updateValue(value?.value); + th.onfocus = () => cellInspectorStore.updateValue(value?.value); const maybeWidth = getRowHeaderWidth(x); if (maybeWidth) { th.style.width = `${maybeWidth}px`; @@ -165,20 +148,8 @@ td.setAttribute("__col", String(x)); td.setAttribute("__row", String(y)); - // Add mouseover event to update the value in the store without changing visibility - td.onmouseover = () => { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue( - value === null || value === undefined ? null : String(value), - ); - }; - - td.onfocus = () => { - // Always update the value in the store, but don't change visibility - cellInspectorStore.updateValue( - value === null || value === undefined ? null : String(value), - ); - }; + td.onmouseover = () => cellInspectorStore.updateValue(value); + td.onfocus = () => cellInspectorStore.updateValue(value); const maybeWidth = getColumnWidth(x); if (maybeWidth) { diff --git a/web-common/src/features/dashboards/stores/cell-inspector-store.ts b/web-common/src/features/dashboards/stores/cell-inspector-store.ts index 74bed20fdc7..5f3c78c9991 100644 --- a/web-common/src/features/dashboards/stores/cell-inspector-store.ts +++ b/web-common/src/features/dashboards/stores/cell-inspector-store.ts @@ -2,14 +2,26 @@ import { writable } from "svelte/store"; interface CellInspectorState { isOpen: boolean; - // undefined = no value set yet, null = cell contains null, string = cell value (including empty string) - value: string | null | undefined; + hasValue: boolean; + value: string | null; +} + +/** + * Converts a raw cell value to the format expected by the store. + * Returns null for null/undefined values, string for everything else. + */ +function normalizeValue(value: unknown): string | null { + if (value === null || value === undefined) { + return null; + } + return String(value); } function createCellInspectorStore() { const { subscribe, update } = writable({ isOpen: false, - value: undefined, + hasValue: false, + value: null, }); return { @@ -18,6 +30,7 @@ function createCellInspectorStore() { update((state) => ({ ...state, isOpen: true, + hasValue: true, value, })), close: () => @@ -25,11 +38,15 @@ function createCellInspectorStore() { ...state, isOpen: false, })), - // Update the value without changing visibility - updateValue: (value: string | null) => + /** + * Update the value without changing visibility. + * Accepts any value type and normalizes it internally. + */ + updateValue: (value: unknown) => update((state) => ({ ...state, - value, + hasValue: true, + value: normalizeValue(value), })), toggle: (value: string | null) => update((state) => ({ @@ -37,12 +54,12 @@ function createCellInspectorStore() { isOpen: !state.isOpen, // When opening: prefer store's existing value (from hover) if set, fall back to passed value // When closing: keep the current value - // undefined means no value was set via hover, so fall back to passed value - value: state.isOpen - ? state.value - : state.value !== undefined - ? state.value - : value, + ...(state.isOpen + ? {} + : { + hasValue: true, + value: state.hasValue ? state.value : value, + }), })), }; }