Skip to content

Commit c66c33c

Browse files
committed
Resolve TODOs
1 parent 2856456 commit c66c33c

4 files changed

Lines changed: 156 additions & 98 deletions

File tree

packages/dashboard-core-plugins/src/panels/IrisGridPanel.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,6 @@ export class IrisGridPanel extends PureComponent<
325325
const { model } = this.state;
326326
const { makeModel } = this.props;
327327
if (model !== prevState.model) {
328-
log.debug('[0] componentDidUpdate model changed');
329328
if (prevState.model != null) {
330329
this.stopModelListening(prevState.model);
331330
prevState.model.close();

packages/iris-grid/src/IrisGridSimplePivotModel.ts

Lines changed: 86 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/* eslint class-methods-use-this: "off" */
2+
/* eslint no-underscore-dangle: "off" */
23
import memoize from 'memoize-one';
34
import type { dh as DhType } from '@deephaven/jsapi-types';
45
import Log from '@deephaven/log';
@@ -10,7 +11,7 @@ import {
1011
type CancelablePromise,
1112
} from '@deephaven/utils';
1213
import { GridRange, type ModelIndex } from '@deephaven/grid';
13-
import { type ColumnName, type UITotalsTableConfig } from './CommonTypes';
14+
import { type ColumnName } from './CommonTypes';
1415
import type { DisplayColumn } from './IrisGridModel';
1516
import ColumnHeaderGroup from './ColumnHeaderGroup';
1617
import IrisGridModel from './IrisGridModel';
@@ -20,8 +21,8 @@ import IrisGridUtils from './IrisGridUtils';
2021
import type { IrisGridThemeType } from './IrisGridTheme';
2122
import {
2223
getSimplePivotColumnMap,
24+
isColumnMapComplete,
2325
KEY_TABLE_PIVOT_COLUMN,
24-
PIVOT_COLUMN_PREFIX,
2526
TOTALS_COLUMN,
2627
type KeyColumnArray,
2728
type KeyTableSubscriptionData,
@@ -74,6 +75,8 @@ class IrisGridSimplePivotModel extends IrisGridModel {
7475

7576
private totalsRowMap: Map<string, unknown>;
7677

78+
private _layoutHints: DhType.LayoutHints | null | undefined;
79+
7780
constructor(
7881
dh: typeof DhType,
7982
table: DhType.Table,
@@ -114,6 +117,16 @@ class IrisGridSimplePivotModel extends IrisGridModel {
114117
this.pivotWidget = pivotWidget;
115118
this.schema = schema;
116119

120+
this._layoutHints = {
121+
backColumns: [TOTALS_COLUMN],
122+
hiddenColumns: [],
123+
frozenColumns: [],
124+
columnGroups: [],
125+
areSavedLayoutsAllowed: false,
126+
frontColumns: [],
127+
searchDisplayMode: this.dh.SearchDisplayMode.SEARCH_DISPLAY_HIDE,
128+
};
129+
117130
this.startListeningToKeyTable();
118131

119132
this.startListeningToSchema();
@@ -165,7 +178,7 @@ class IrisGridSimplePivotModel extends IrisGridModel {
165178
}
166179

167180
/**
168-
* Add displayName property from the column map to the given column
181+
* Add displayName property to the given column
169182
* @param column Column to add displayName to
170183
* @param columnMap Column name map
171184
* @returns Column with the displayName
@@ -177,12 +190,7 @@ class IrisGridSimplePivotModel extends IrisGridModel {
177190
return new Proxy(column, {
178191
get: (target, prop) => {
179192
if (prop === 'displayName') {
180-
if (!columnMap.has(column.name)) {
181-
return column.name.startsWith(PIVOT_COLUMN_PREFIX)
182-
? ''
183-
: column.name;
184-
}
185-
return columnMap.get(column.name);
193+
return columnMap.get(column.name) ?? column.name;
186194
}
187195
return Reflect.get(target, prop);
188196
},
@@ -193,27 +201,20 @@ class IrisGridSimplePivotModel extends IrisGridModel {
193201
(
194202
columnMap: SimplePivotColumnMap,
195203
schema: SimplePivotSchema
196-
): readonly ColumnHeaderGroup[] => {
197-
log.debug('getPivotColumnHeaderGroups', schema.pivotDescription, {
198-
schema,
199-
columnMap: JSON.stringify([...columnMap]),
200-
modelColumns: JSON.stringify(this.model.columns.map(c => c.name)),
201-
});
202-
return [
203-
new ColumnHeaderGroup({
204-
name: schema.pivotDescription,
205-
children: schema.rowColNames,
206-
depth: 1,
207-
childIndexes: schema.rowColNames.map((_, index) => index),
208-
}),
209-
new ColumnHeaderGroup({
210-
name: schema.columnColNames.join(', '),
211-
children: [...columnMap.keys()],
212-
depth: 1,
213-
childIndexes: [...columnMap.keys()].map((_, index) => index),
214-
}),
215-
];
216-
}
204+
): readonly ColumnHeaderGroup[] => [
205+
new ColumnHeaderGroup({
206+
name: schema.pivotDescription,
207+
children: schema.rowColNames,
208+
depth: 1,
209+
childIndexes: schema.rowColNames.map((_, index) => index),
210+
}),
211+
new ColumnHeaderGroup({
212+
name: schema.columnColNames.join(', '),
213+
children: [...columnMap.keys()],
214+
depth: 1,
215+
childIndexes: [...columnMap.keys()].map((_, index) => index),
216+
}),
217+
]
217218
);
218219

219220
get initialColumnHeaderGroups(): readonly ColumnHeaderGroup[] {
@@ -275,24 +276,6 @@ class IrisGridSimplePivotModel extends IrisGridModel {
275276
return false;
276277
}
277278

278-
set totalsConfig(_: UITotalsTableConfig | null) {
279-
// no-op
280-
}
281-
282-
get columnHeaderGroupMap(): ReadonlyMap<string, ColumnHeaderGroup> {
283-
log.debug('get columnHeaderGroupMap');
284-
return this.model.columnHeaderGroupMap;
285-
}
286-
287-
get columnHeaderGroups(): readonly ColumnHeaderGroup[] {
288-
log.debug('get columnHeaderGroups');
289-
return this.model.columnHeaderGroups;
290-
}
291-
292-
set columnHeaderGroups(columnHeaderGroups: readonly ColumnHeaderGroup[]) {
293-
this.model.columnHeaderGroups = columnHeaderGroups;
294-
}
295-
296279
get rowCount(): number {
297280
return this.model.rowCount + (this.schema.hasTotals ? 1 : 0);
298281
}
@@ -379,6 +362,7 @@ class IrisGridSimplePivotModel extends IrisGridModel {
379362
}
380363

381364
handleKeyTableUpdate(e: { detail: KeyTableSubscriptionData }): void {
365+
log.debug('Key table updated');
382366
const pivotIdColumn = this.keyTable.findColumn(KEY_TABLE_PIVOT_COLUMN);
383367
const columns = this.keyTable.columns.filter(
384368
c => c.name !== KEY_TABLE_PIVOT_COLUMN
@@ -392,43 +376,46 @@ class IrisGridSimplePivotModel extends IrisGridModel {
392376
keyColumns.push([TOTALS_COLUMN, 'Totals']);
393377
}
394378
const columnMap = new Map(keyColumns);
395-
log.debug(
396-
'Key table updated',
397-
this.model.columns.map(c => c.name),
398-
JSON.stringify([...columnMap]),
399-
Boolean(this.nextModel),
400-
Boolean(this.nextColumnMap)
401-
);
402-
if (this.nextModel != null) {
403-
if (
404-
this.nextModel.columns.some(
405-
c => c.name.startsWith(PIVOT_COLUMN_PREFIX) && !columnMap.has(c.name)
406-
)
407-
) {
408-
log.debug('next model not null, but columns do not match');
409-
this.nextColumnMap = columnMap;
379+
380+
if (this.nextModel == null) {
381+
if (isColumnMapComplete(columnMap, this.model.columns)) {
382+
log.debug2(
383+
'Key table update matches the existing model, update columns'
384+
);
385+
this.columnMap = columnMap;
386+
this.columnHeaderGroups = this.getCachedColumnHeaderGroups(
387+
this.columnMap,
388+
this.schema
389+
);
390+
this.dispatchEvent(
391+
new EventShimCustomEvent(IrisGridModel.EVENT.COLUMNS_CHANGED, {
392+
detail: this.columns,
393+
})
394+
);
410395
} else {
411-
log.debug('next model not null, all columns match');
412-
assertNotNull(this.nextTotalsTable);
413-
this.setModel(this.nextModel, columnMap, this.nextTotalsTable);
414-
this.nextModel = null;
396+
log.debug2(
397+
'Key table update does not match the existing model, save column map for the next schema update'
398+
);
399+
this.nextColumnMap = columnMap;
415400
}
416-
} else if (
417-
this.model.columns.some(
418-
c => c.name.startsWith(PIVOT_COLUMN_PREFIX) && !columnMap.has(c.name)
419-
)
420-
) {
421-
log.debug(
422-
'next model is null, columns do not match - save nextColumnMap'
401+
return;
402+
}
403+
if (isColumnMapComplete(columnMap, this.nextModel.columns)) {
404+
log.debug2('Key table update matches the saved model, update the model');
405+
assertNotNull(this.nextTotalsTable);
406+
this.setModel(this.nextModel, columnMap, this.nextTotalsTable);
407+
this.nextModel = null;
408+
this.nextTotalsTable = null;
409+
} else {
410+
log.debug2(
411+
'Key table update does not match the saved model, save column map for the next schema update'
423412
);
424-
// TODO: check if current model columns match?
425413
this.nextColumnMap = columnMap;
426414
}
427415
}
428416

429417
async handleSchemaUpdate(e: DhType.Event<DhType.Widget>): Promise<void> {
430418
log.debug('Schema updated');
431-
// Get the object, and make sure the keytable is fetched and usable
432419
const tables = e.detail.exportedObjects;
433420
const tablePromise = tables[0].fetch();
434421
const totalsTablePromise = tables.length === 2 ? tables[1].fetch() : null;
@@ -456,17 +443,7 @@ class IrisGridSimplePivotModel extends IrisGridModel {
456443
);
457444

458445
get layoutHints(): DhType.LayoutHints | null | undefined {
459-
// log.debug('get layoutHints');
460-
// TODO: memoize
461-
return {
462-
backColumns: [TOTALS_COLUMN],
463-
hiddenColumns: [],
464-
frozenColumns: [],
465-
columnGroups: [],
466-
areSavedLayoutsAllowed: false,
467-
frontColumns: [],
468-
searchDisplayMode: this.dh.SearchDisplayMode.SEARCH_DISPLAY_HIDE,
469-
};
446+
return this._layoutHints;
470447
}
471448

472449
/**
@@ -544,16 +521,14 @@ class IrisGridSimplePivotModel extends IrisGridModel {
544521
}
545522
this.dispatchEvent(
546523
new EventShimCustomEvent(IrisGridModel.EVENT.COLUMNS_CHANGED, {
547-
detail: model.columns,
524+
detail: this.columns,
548525
})
549526
);
550527
}
551528

552529
setNextSchema(
553530
pivotTablesPromise: Promise<[DhType.Table, DhType.Table]>
554531
): void {
555-
log.debug2('setNextSchema');
556-
557532
if (this.schemaPromise) {
558533
this.schemaPromise.cancel();
559534
}
@@ -567,17 +542,31 @@ class IrisGridSimplePivotModel extends IrisGridModel {
567542
);
568543
this.schemaPromise
569544
.then(([table, totalsTable]) => {
545+
log.debug('Schema updated');
570546
this.schemaPromise = null;
571547
const model = makeModel(this.dh, table, this.formatter);
572548
if (this.nextColumnMap != null) {
573-
log.debug(
574-
'Schema updated, nextColumnMap is not null, setting new model'
575-
);
576-
this.setModel(model, this.nextColumnMap, totalsTable);
577-
this.nextColumnMap = null;
549+
if (isColumnMapComplete(this.nextColumnMap, model.columns)) {
550+
log.debug2(
551+
'Schema updated, set new model with the saved column map'
552+
);
553+
this.setModel(model, this.nextColumnMap, totalsTable);
554+
this.nextColumnMap = null;
555+
} else {
556+
log.debug2(
557+
'Saved column map does not match the new model, save the model for the next key table update'
558+
);
559+
this.nextModel = model;
560+
this.nextTotalsTable = totalsTable;
561+
}
562+
return;
563+
}
564+
if (isColumnMapComplete(this.columnMap, model.columns)) {
565+
log.debug2('Schema updated, set new model with existing column map');
566+
this.setModel(model, this.columnMap, totalsTable);
578567
} else {
579-
log.debug(
580-
'Schema updated, nextColumnMap is null, save new model as nextModel'
568+
log.debug2(
569+
'Existing column map does not match the new model, save the model for the next key table update'
581570
);
582571
this.nextModel = model;
583572
this.nextTotalsTable = totalsTable;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import type { dh as DhType } from '@deephaven/jsapi-types';
2+
import {
3+
PIVOT_COLUMN_PREFIX,
4+
isColumnMapComplete,
5+
type SimplePivotColumnMap,
6+
} from './SimplePivotUtils';
7+
8+
describe('isColumnMapComplete', () => {
9+
it('returns true if all PIVOT_C_ columns exist in the map', () => {
10+
const mockColumns = [
11+
{ name: `${PIVOT_COLUMN_PREFIX}A` } as DhType.Column,
12+
{ name: `${PIVOT_COLUMN_PREFIX}B` } as DhType.Column,
13+
];
14+
const mockColumnMap: SimplePivotColumnMap = new Map([
15+
[`${PIVOT_COLUMN_PREFIX}A`, 'ValA'],
16+
[`${PIVOT_COLUMN_PREFIX}B`, 'ValB'],
17+
]);
18+
19+
const result = isColumnMapComplete(mockColumnMap, mockColumns);
20+
expect(result).toBe(true);
21+
});
22+
23+
it('returns false if a pivot column is missing in the map', () => {
24+
const mockColumns = [
25+
{ name: `${PIVOT_COLUMN_PREFIX}A` } as DhType.Column,
26+
{ name: `${PIVOT_COLUMN_PREFIX}B` } as DhType.Column,
27+
];
28+
const mockColumnMap: SimplePivotColumnMap = new Map([
29+
[`${PIVOT_COLUMN_PREFIX}A`, 'ValA'],
30+
]);
31+
32+
const result = isColumnMapComplete(mockColumnMap, mockColumns);
33+
expect(result).toBe(false);
34+
});
35+
36+
it('ignores non-pivot columns when checking completeness', () => {
37+
const mockColumns = [
38+
{ name: 'RandomColumn' } as DhType.Column,
39+
{ name: `${PIVOT_COLUMN_PREFIX}A` } as DhType.Column,
40+
];
41+
const mockColumnMap: SimplePivotColumnMap = new Map([
42+
[`${PIVOT_COLUMN_PREFIX}A`, 'ValA'],
43+
]);
44+
45+
const result = isColumnMapComplete(mockColumnMap, mockColumns);
46+
expect(result).toBe(true);
47+
});
48+
});

packages/iris-grid/src/SimplePivotUtils.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ export interface KeyTableSubscriptionData {
2222
getData: (rowKey: DhType.Row, column: DhType.Column) => string;
2323
}
2424

25+
/**
26+
* Get a column map for a simple pivot table based on the key table data
27+
* @param data Data from the key table
28+
* @param columns Columns to include in the column map
29+
* @param pivotIdColumn Key table column containing display names for the pivot columns
30+
* @returns Column map for the simple pivot table
31+
*/
2532
export function getSimplePivotColumnMap(
2633
data: KeyTableSubscriptionData,
2734
columns: DhType.Column[],
@@ -42,3 +49,18 @@ export function getSimplePivotColumnMap(
4249
}
4350
return columnMap;
4451
}
52+
53+
/**
54+
* Check if the column map has entries for all pivot columns
55+
* @param columnMap Column map to check
56+
* @param columns Columns to check against
57+
* @returns True if the column map has entries for all pivot columns
58+
*/
59+
export function isColumnMapComplete(
60+
columnMap: SimplePivotColumnMap,
61+
columns: readonly DhType.Column[]
62+
): boolean {
63+
return !columns.some(
64+
c => c.name.startsWith(PIVOT_COLUMN_PREFIX) && !columnMap.has(c.name)
65+
);
66+
}

0 commit comments

Comments
 (0)