Skip to content

Commit d08b089

Browse files
committed
WIP
1 parent eab52ac commit d08b089

4 files changed

Lines changed: 349 additions & 153 deletions

File tree

packages/dashboard-core-plugins/src/TableHistoryPlugin.tsx

Lines changed: 73 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,21 @@ import {
1515
} from '@deephaven/components';
1616
import { vsEdit, vsHistory, vsTrash } from '@deephaven/icons';
1717
import { usePersistentState } from '@deephaven/dashboard';
18-
import { type MoveOperation } from '@deephaven/grid';
1918
import {
2019
type TableOption,
2120
type TableOptionPanelProps,
2221
type GridStateSnapshot,
2322
useTableOptionsHost,
2423
defaultTableOptionsRegistry,
2524
IrisGridUtils,
26-
type DehydratedQuickFilter,
27-
type DehydratedAdvancedFilter,
28-
type DehydratedSort,
25+
type DehydratedIrisGridState,
26+
type DehydratedGridState,
2927
} from '@deephaven/iris-grid';
30-
import type { ColumnName } from '@deephaven/iris-grid';
3128

3229
/**
3330
* Dehydrated (JSON-serializable) snapshot of table state.
3431
* Stored via usePersistentState for cross-session persistence.
32+
* Uses the same structure as IrisGridUtils dehydration for proper hydration.
3533
*/
3634
interface DehydratedStateSnapshot {
3735
/** Unique identifier */
@@ -40,26 +38,10 @@ interface DehydratedStateSnapshot {
4038
timestamp: string;
4139
/** User-defined name for the snapshot (optional) */
4240
name?: string;
43-
/** Quick filters state (dehydrated) */
44-
quickFilters: readonly DehydratedQuickFilter[];
45-
/** Advanced filters state (dehydrated) */
46-
advancedFilters: readonly DehydratedAdvancedFilter[];
47-
/** Sort configuration (dehydrated) */
48-
sorts: readonly DehydratedSort[];
49-
/** Reverse sort order */
50-
reverse: boolean;
51-
/** Cross-column search value */
52-
searchValue: string;
53-
/** Columns for cross-column search */
54-
selectedSearchColumns: readonly ColumnName[];
55-
/** Invert search column selection */
56-
invertSearchColumns: boolean;
57-
/** Select distinct columns */
58-
selectDistinctColumns: readonly ColumnName[];
59-
/** Custom columns */
60-
customColumns: readonly ColumnName[];
61-
/** Re-arranged columns (move operations) */
62-
movedColumns: readonly MoveOperation[];
41+
/** Dehydrated IrisGrid state (filters, sorts, custom columns, etc.) */
42+
irisGridState: Partial<DehydratedIrisGridState>;
43+
/** Dehydrated Grid state (movedColumns, movedRows, etc.) */
44+
gridState: Partial<DehydratedGridState>;
6345
}
6446

6547
/**
@@ -76,16 +58,20 @@ const TABLE_HISTORY_OPTION_TYPE = 'table-history-option';
7658

7759
/**
7860
* Dehydrates current grid state to a JSON-serializable snapshot.
61+
* Uses IrisGridUtils dehydration methods for proper serialization.
7962
*/
8063
function dehydrateSnapshot(
8164
gridState: GridStateSnapshot,
8265
irisGridUtils: IrisGridUtils
83-
): Omit<DehydratedStateSnapshot, 'id' | 'timestamp'> {
66+
): Omit<DehydratedStateSnapshot, 'id' | 'timestamp' | 'name'> {
8467
const { model, quickFilters, advancedFilters, sorts } = gridState;
85-
return {
68+
const { columns } = model;
69+
70+
// Dehydrate IrisGrid state (filters, sorts, custom columns, etc.)
71+
const irisGridDehydrated: Partial<DehydratedIrisGridState> = {
8672
quickFilters: IrisGridUtils.dehydrateQuickFilters(quickFilters),
8773
advancedFilters: irisGridUtils.dehydrateAdvancedFilters(
88-
model.columns,
74+
columns,
8975
advancedFilters
9076
),
9177
sorts: IrisGridUtils.dehydrateSort(sorts),
@@ -95,7 +81,20 @@ function dehydrateSnapshot(
9581
invertSearchColumns: gridState.invertSearchColumns,
9682
selectDistinctColumns: [...gridState.selectDistinctColumns],
9783
customColumns: [...gridState.customColumns],
84+
};
85+
86+
// Dehydrate Grid state (movedColumns)
87+
// Use dehydrateGridState to properly convert column indices to names
88+
const gridDehydrated = IrisGridUtils.dehydrateGridState(model, {
89+
isStuckToBottom: false,
90+
isStuckToRight: false,
9891
movedColumns: [...gridState.movedColumns],
92+
movedRows: [],
93+
});
94+
95+
return {
96+
irisGridState: irisGridDehydrated,
97+
gridState: gridDehydrated,
9998
};
10099
}
101100

@@ -130,7 +129,7 @@ function TableHistoryPanel(_props: TableOptionPanelProps): JSX.Element {
130129
}
131130
);
132131

133-
const snapshots = state.snapshots;
132+
const { snapshots } = state;
134133

135134
const handleSaveSnapshot = useCallback(() => {
136135
const snapshot: DehydratedStateSnapshot = {
@@ -145,80 +144,18 @@ function TableHistoryPanel(_props: TableOptionPanelProps): JSX.Element {
145144

146145
const handleRestoreSnapshot = useCallback(
147146
(snapshot: DehydratedStateSnapshot) => {
148-
const { columns, formatter } = model;
149-
150-
// Get timezone from the model's formatter
151-
const timeZone = formatter.timeZone;
152-
153-
// Hydrate quick filters
154-
const hydratedQuickFilters = irisGridUtils.hydrateQuickFilters(
155-
columns,
156-
snapshot.quickFilters,
157-
timeZone
158-
);
159-
160-
// Hydrate advanced filters
161-
const hydratedAdvancedFilters = irisGridUtils.hydrateAdvancedFilters(
162-
columns,
163-
snapshot.advancedFilters,
164-
timeZone
165-
);
166-
167-
// Hydrate sorts
168-
const hydratedSorts = irisGridUtils.hydrateSort(columns, snapshot.sorts);
169-
170-
// Note: Dispatch order matters! SET_SELECT_DISTINCT_COLUMNS and SET_CUSTOM_COLUMNS
171-
// trigger handlers that reset sorts/filters, so we dispatch them first (if changing),
172-
// then restore everything else.
173-
174-
// Only dispatch SELECT_DISTINCT if actually changing (to avoid clearing everything)
175-
const currentSelectDistinct = gridState.selectDistinctColumns;
176-
const newSelectDistinct = snapshot.selectDistinctColumns;
177-
const selectDistinctChanging =
178-
currentSelectDistinct.length !== newSelectDistinct.length ||
179-
!currentSelectDistinct.every((col, i) => col === newSelectDistinct[i]);
180-
181-
if (selectDistinctChanging) {
182-
dispatch({
183-
type: 'SET_SELECT_DISTINCT_COLUMNS',
184-
columns: [...snapshot.selectDistinctColumns],
185-
});
186-
}
187-
188-
dispatch({
189-
type: 'SET_CUSTOM_COLUMNS',
190-
columns: [...snapshot.customColumns],
191-
});
192-
193-
// Now restore filters, sorts, and search (after select distinct is handled)
194-
dispatch({
195-
type: 'SET_QUICK_FILTERS',
196-
filters: hydratedQuickFilters,
197-
});
198-
dispatch({
199-
type: 'SET_ADVANCED_FILTERS',
200-
filters: hydratedAdvancedFilters,
201-
});
147+
// Use RESTORE_DEHYDRATED_STATE action which handles hydration properly,
148+
// including graceful handling of missing columns when the table structure
149+
// has changed (e.g., columns hidden via Organize Columns).
202150
dispatch({
203-
type: 'SET_CROSS_COLUMN_SEARCH',
204-
searchValue: snapshot.searchValue,
205-
selectedSearchColumns: [...snapshot.selectedSearchColumns],
206-
invertSearchColumns: snapshot.invertSearchColumns,
207-
});
208-
209-
// SET_SORTS and SET_REVERSE must be last since other dispatches can clear them
210-
dispatch({ type: 'SET_SORTS', sorts: hydratedSorts });
211-
dispatch({ type: 'SET_REVERSE', reverse: snapshot.reverse });
212-
213-
// Restore moved columns (re-arranged column order)
214-
dispatch({
215-
type: 'SET_MOVED_COLUMNS',
216-
columns: [...snapshot.movedColumns],
151+
type: 'RESTORE_DEHYDRATED_STATE',
152+
irisGridState: snapshot.irisGridState,
153+
gridState: snapshot.gridState,
217154
});
218155

219156
// Stay on the Table History screen after restoring
220157
},
221-
[dispatch, model, irisGridUtils, gridState.selectDistinctColumns]
158+
[dispatch]
222159
);
223160

224161
const handleDeleteSnapshot = useCallback(
@@ -314,74 +251,82 @@ function TableHistoryPanel(_props: TableOptionPanelProps): JSX.Element {
314251
}
315252

316253
const currentDehydrated = dehydrateSnapshot(gridState, irisGridUtils);
254+
const currentIris = currentDehydrated.irisGridState;
255+
const currentGrid = currentDehydrated.gridState;
256+
257+
// Handle backward compatibility with old snapshot format
258+
// Old format had properties at top level, new format nests them in irisGridState/gridState
259+
const lastIris = lastSnapshot.irisGridState ?? {};
260+
const lastGrid = lastSnapshot.gridState ?? {};
317261
const changes: string[] = [];
318262

319263
// Compare sorts
320264
const sortsChanged =
321-
JSON.stringify(currentDehydrated.sorts) !==
322-
JSON.stringify(lastSnapshot.sorts);
323-
if (sortsChanged) {
324-
changes.push(`Sorts: ${currentDehydrated.sorts.length} column(s)`);
265+
JSON.stringify(currentIris.sorts) !== JSON.stringify(lastIris.sorts);
266+
if (sortsChanged && currentIris.sorts != null) {
267+
changes.push(`Sorts: ${currentIris.sorts.length} column(s)`);
325268
}
326269

327270
// Compare quick filters
328271
const quickFiltersChanged =
329-
JSON.stringify(currentDehydrated.quickFilters) !==
330-
JSON.stringify(lastSnapshot.quickFilters);
331-
if (quickFiltersChanged) {
272+
JSON.stringify(currentIris.quickFilters) !==
273+
JSON.stringify(lastIris.quickFilters);
274+
if (quickFiltersChanged && currentIris.quickFilters != null) {
332275
changes.push(
333-
`Quick Filters: ${currentDehydrated.quickFilters.length} filter(s)`
276+
`Quick Filters: ${currentIris.quickFilters.length} filter(s)`
334277
);
335278
}
336279

337280
// Compare advanced filters
338281
const advancedFiltersChanged =
339-
JSON.stringify(currentDehydrated.advancedFilters) !==
340-
JSON.stringify(lastSnapshot.advancedFilters);
341-
if (advancedFiltersChanged) {
282+
JSON.stringify(currentIris.advancedFilters) !==
283+
JSON.stringify(lastIris.advancedFilters);
284+
if (advancedFiltersChanged && currentIris.advancedFilters != null) {
342285
changes.push(
343-
`Advanced Filters: ${currentDehydrated.advancedFilters.length} filter(s)`
286+
`Advanced Filters: ${currentIris.advancedFilters.length} filter(s)`
344287
);
345288
}
346289

347290
// Compare search
348-
if (currentDehydrated.searchValue !== lastSnapshot.searchValue) {
349-
changes.push(`Search: "${currentDehydrated.searchValue || '(empty)'}"`);
291+
if (currentIris.searchValue !== lastIris.searchValue) {
292+
const searchDisplay =
293+
currentIris.searchValue != null && currentIris.searchValue.length > 0
294+
? currentIris.searchValue
295+
: '(empty)';
296+
changes.push(`Search: "${searchDisplay}"`);
350297
}
351298

352299
// Compare reverse
353-
if (currentDehydrated.reverse !== lastSnapshot.reverse) {
354-
changes.push(`Reverse: ${currentDehydrated.reverse}`);
300+
if (currentIris.reverse !== lastIris.reverse) {
301+
changes.push(`Reverse: ${currentIris.reverse}`);
355302
}
356303

357304
// Compare select distinct
358305
const selectDistinctChanged =
359-
JSON.stringify(currentDehydrated.selectDistinctColumns) !==
360-
JSON.stringify(lastSnapshot.selectDistinctColumns);
361-
if (selectDistinctChanged) {
306+
JSON.stringify(currentIris.selectDistinctColumns) !==
307+
JSON.stringify(lastIris.selectDistinctColumns);
308+
if (selectDistinctChanged && currentIris.selectDistinctColumns != null) {
362309
changes.push(
363-
`Select Distinct: ${currentDehydrated.selectDistinctColumns.length} column(s)`
310+
`Select Distinct: ${currentIris.selectDistinctColumns.length} column(s)`
364311
);
365312
}
366313

367314
// Compare custom columns
368315
const customColumnsChanged =
369-
JSON.stringify(currentDehydrated.customColumns) !==
370-
JSON.stringify(lastSnapshot.customColumns);
371-
if (customColumnsChanged) {
316+
JSON.stringify(currentIris.customColumns) !==
317+
JSON.stringify(lastIris.customColumns);
318+
if (customColumnsChanged && currentIris.customColumns != null) {
372319
changes.push(
373-
`Custom Columns: ${currentDehydrated.customColumns.length} column(s)`
320+
`Custom Columns: ${currentIris.customColumns.length} column(s)`
374321
);
375322
}
376323

377324
// Compare moved columns
378325
const movedColumnsChanged =
379-
JSON.stringify(currentDehydrated.movedColumns) !==
380-
JSON.stringify(lastSnapshot.movedColumns);
381-
if (movedColumnsChanged) {
382-
changes.push(
383-
`Column Order: ${currentDehydrated.movedColumns.length} move(s)`
384-
);
326+
JSON.stringify(currentGrid.movedColumns) !==
327+
JSON.stringify(lastGrid.movedColumns);
328+
if (movedColumnsChanged && currentGrid.movedColumns != null) {
329+
changes.push(`Column Order: ${currentGrid.movedColumns.length} move(s)`);
385330
}
386331

387332
return changes;

0 commit comments

Comments
 (0)