From dafb1117ee903d5b632ea64db595ef4b503a47e0 Mon Sep 17 00:00:00 2001 From: Vlad Babich Date: Wed, 3 May 2023 12:51:54 -0600 Subject: [PATCH 01/16] De-globalize JSAPI in IrisGrid --- .../dashboard-core-plugins/src/GridPlugin.tsx | 6 +- .../src/panels/IrisGridPanel.tsx | 36 +- packages/embed-grid/src/App.tsx | 7 +- .../iris-grid/src/AdvancedFilterCreator.tsx | 9 +- .../src/AdvancedFilterCreatorFilterItem.tsx | 11 +- .../src/AdvancedFilterCreatorSelectValue.tsx | 9 +- packages/iris-grid/src/IrisGrid.tsx | 61 +- .../iris-grid/src/IrisGridModelFactory.ts | 3 +- packages/iris-grid/src/IrisGridProxyModel.ts | 15 +- packages/iris-grid/src/IrisGridTableModel.ts | 9 +- .../src/IrisGridTableModelTemplate.ts | 25 +- packages/iris-grid/src/IrisGridTestUtils.ts | 6 +- packages/iris-grid/src/IrisGridUtils.test.ts | 12 +- packages/iris-grid/src/IrisGridUtils.ts | 76 +- .../src/sidebar/ChartBuilder.test.tsx | 7 +- .../iris-grid/src/sidebar/ChartBuilder.tsx | 183 ++--- packages/jsapi-utils/src/TableUtils.test.ts | 149 ++-- packages/jsapi-utils/src/TableUtils.ts | 673 +++++++++--------- 18 files changed, 717 insertions(+), 580 deletions(-) diff --git a/packages/dashboard-core-plugins/src/GridPlugin.tsx b/packages/dashboard-core-plugins/src/GridPlugin.tsx index f16dd00652..687df940ae 100644 --- a/packages/dashboard-core-plugins/src/GridPlugin.tsx +++ b/packages/dashboard-core-plugins/src/GridPlugin.tsx @@ -11,6 +11,7 @@ import { IrisGridModelFactory, IrisGridThemeType } from '@deephaven/iris-grid'; import { Table, VariableDefinition } from '@deephaven/jsapi-shim'; import shortid from 'shortid'; import { IrisGridPanel, IrisGridPanelProps } from './panels'; +import { TableUtils } from '@deephaven/jsapi-utils'; const SUPPORTED_TYPES: string[] = [ dh.VariableType.TABLE, @@ -54,8 +55,11 @@ export function GridPlugin(props: GridPluginProps): JSX.Element | null { } const metadata = { name, table: name, type: widget.type }; + const tableUtils = new TableUtils(dh); const makeModel = () => - fetch().then((table: Table) => IrisGridModelFactory.makeModel(table)); + fetch().then((table: Table) => + IrisGridModelFactory.makeModel(table, tableUtils) + ); const config = { type: 'react-component' as const, component: IrisGridPanel.COMPONENT, diff --git a/packages/dashboard-core-plugins/src/panels/IrisGridPanel.tsx b/packages/dashboard-core-plugins/src/panels/IrisGridPanel.tsx index 4845423cfd..61e22e8d74 100644 --- a/packages/dashboard-core-plugins/src/panels/IrisGridPanel.tsx +++ b/packages/dashboard-core-plugins/src/panels/IrisGridPanel.tsx @@ -61,8 +61,9 @@ import { PromiseUtils, } from '@deephaven/utils'; import { ContextAction, ContextMenuRoot } from '@deephaven/components'; -import { +import defaultDh, { Column, + dhType, FilterCondition, Sort, VariableTypeUnion, @@ -125,6 +126,7 @@ export interface PanelState { export interface IrisGridPanelProps { children?: ReactNode; + dh?: dhType; glContainer: Container; glEventHub: EventEmitter; metadata: Metadata; @@ -207,6 +209,7 @@ export class IrisGridPanel extends PureComponent< static defaultProps = { onStateChange: (): void => undefined, onPanelStateUpdate: (): void => undefined, + dh: defaultDh, }; static displayName = 'IrisGridPanel'; @@ -244,9 +247,10 @@ export class IrisGridPanel extends PureComponent< this.irisGrid = React.createRef(); this.pluginRef = React.createRef(); - const { panelState } = props; + const { panelState, dh = defaultDh } = props; this.pluginState = null; + this.tableUtils = new TableUtils(dh); this.state = { error: null, @@ -341,6 +345,8 @@ export class IrisGridPanel extends PureComponent< pluginState: unknown; + tableUtils: TableUtils; + getTableName(): string { const { metadata } = this.props; return metadata.table; @@ -439,6 +445,7 @@ export class IrisGridPanel extends PureComponent< getDehydratedIrisGridState = memoize( ( + dh: dhType, model: IrisGridModel, sorts: readonly Sort[], advancedFilters: ReadonlyAdvancedFilterMap, @@ -461,7 +468,7 @@ export class IrisGridPanel extends PureComponent< conditionalFormats: readonly SidebarFormattingRule[], columnHeaderGroups: readonly ColumnHeaderGroup[] ) => - IrisGridUtils.dehydrateIrisGridState(model, { + IrisGridUtils.dehydrateIrisGridState(dh, model, { advancedFilters, aggregationSettings, customColumnFormatMap, @@ -609,6 +616,7 @@ export class IrisGridPanel extends PureComponent< const { columns, formatter } = model; const pluginFilters = IrisGridUtils.getFiltersFromInputFilters( columns, + this.tableUtils, filters, formatter.timeZone ); @@ -944,11 +952,14 @@ export class IrisGridPanel extends PureComponent< quickFilters: IrisGridUtils.hydrateQuickFilters( columns, indexedQuickFilters, + this.tableUtils, formatter.timeZone ), advancedFilters: IrisGridUtils.hydrateAdvancedFilters( + dh, columns, indexedAdvancedFilters, + this.tableUtils, formatter.timeZone ), }); @@ -1037,10 +1048,15 @@ export class IrisGridPanel extends PureComponent< frozenColumns, conditionalFormats, columnHeaderGroups, - } = IrisGridUtils.hydrateIrisGridState(model, { - ...irisGridState, - ...irisGridStateOverrides, - }); + } = IrisGridUtils.hydrateIrisGridState( + dh, + model, + { + ...irisGridState, + ...irisGridStateOverrides, + }, + this.tableUtils + ); const { isStuckToBottom, isStuckToRight, @@ -1089,7 +1105,7 @@ export class IrisGridPanel extends PureComponent< savePanelState = debounce(() => { const { irisGridState, gridState, pluginState } = this; assertNotNull(irisGridState); - const { onPanelStateUpdate } = this.props; + const { onPanelStateUpdate, dh } = this.props; const { model, panelState: oldPanelState, @@ -1139,6 +1155,9 @@ export class IrisGridPanel extends PureComponent< advancedSettings ), this.getDehydratedIrisGridState( + // dh is set to defaultDh in defaultProps + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + dh!, model, sorts, advancedFilters, @@ -1295,6 +1314,7 @@ export class IrisGridPanel extends PureComponent< customColumnFormatMap={customColumnFormatMap} columnSelectionValidator={this.isColumnSelectionValid} conditionalFormats={conditionalFormats} + dh={dh} inputFilters={this.getGridInputFilters(model.columns, inputFilters)} applyInputFiltersOnInit={panelState == null} isFilterBarShown={isFilterBarShown} diff --git a/packages/embed-grid/src/App.tsx b/packages/embed-grid/src/App.tsx index 24cad16d25..77bf41415e 100644 --- a/packages/embed-grid/src/App.tsx +++ b/packages/embed-grid/src/App.tsx @@ -95,7 +95,12 @@ function App(): JSX.Element { const table = await loadTable(connection, name); // Create the `IrisGridModel` for use with the `IrisGrid` component log.debug(`Creating model...`); - const newModel = await IrisGridModelFactory.makeModel(table); + const tableUtils = new TableUtils(dh); + const newModel = await IrisGridModelFactory.makeModel( + table, + tableUtils + ); + setModel(newModel); log.debug('Table successfully loaded!'); } catch (e: unknown) { diff --git a/packages/iris-grid/src/AdvancedFilterCreator.tsx b/packages/iris-grid/src/AdvancedFilterCreator.tsx index 97b9a9c755..76f663df48 100644 --- a/packages/iris-grid/src/AdvancedFilterCreator.tsx +++ b/packages/iris-grid/src/AdvancedFilterCreator.tsx @@ -53,6 +53,7 @@ interface AdvancedFilterCreatorProps { options: AdvancedFilterOptions; sortDirection: SortDirection; formatter: Formatter; + tableUtils: TableUtils; } interface AdvancedFilterItem { @@ -409,7 +410,7 @@ class AdvancedFilterCreator extends PureComponent< invertSelection, selectedValues, } = this.state; - const { column, onFilterChange, model } = this.props; + const { column, onFilterChange, model, tableUtils } = this.props; const { formatter } = model; const items = filterItems.filter( @@ -435,7 +436,7 @@ class AdvancedFilterCreator extends PureComponent< selectedValues, }; - const filter = TableUtils.makeAdvancedFilter( + const filter = tableUtils.makeAdvancedFilter( column, options, formatter.timeZone @@ -445,7 +446,7 @@ class AdvancedFilterCreator extends PureComponent< } render(): JSX.Element { - const { column, model, sortDirection, formatter } = this.props; + const { column, model, sortDirection, formatter, tableUtils } = this.props; const { filterItems, filterOperators, @@ -475,6 +476,7 @@ class AdvancedFilterCreator extends PureComponent< selectedType={selectedType} value={value} formatter={formatter} + tableUtils={tableUtils} /> ); filterItemElements.push(element); @@ -595,6 +597,7 @@ class AdvancedFilterCreator extends PureComponent< formatter={formatter} showSearch={!isDateType} timeZone={formatter.timeZone} + tableUtils={tableUtils} /> diff --git a/packages/iris-grid/src/AdvancedFilterCreatorFilterItem.tsx b/packages/iris-grid/src/AdvancedFilterCreatorFilterItem.tsx index 3dfd2882f0..f60070f6b1 100644 --- a/packages/iris-grid/src/AdvancedFilterCreatorFilterItem.tsx +++ b/packages/iris-grid/src/AdvancedFilterCreatorFilterItem.tsx @@ -30,6 +30,7 @@ export interface AdvancedFilterCreatorFilterItemProps { selectedType?: FilterTypeValue; value?: string; formatter: Formatter; + tableUtils: TableUtils; } export type AdvancedFilterCreatorFilterItemState = AdvancedFilterItemType; @@ -135,13 +136,14 @@ export class AdvancedFilterCreatorFilterItem extends PureComponent< column: Column, operation: FilterTypeValue, value: string, - timeZone: string + timeZone: string, + tableUtils: TableUtils ): boolean => { try { // We don't want to show an error for an empty value return ( !value || - TableUtils.makeAdvancedValueFilter( + tableUtils.makeAdvancedValueFilter( column, operation, value, @@ -155,7 +157,7 @@ export class AdvancedFilterCreatorFilterItem extends PureComponent< ); render(): JSX.Element { - const { column, filterTypes, formatter } = this.props; + const { column, filterTypes, formatter, tableUtils } = this.props; const { selectedType, value } = this.state; const showValueInput = !TableUtils.isBooleanType(column.type); const typeOptionElements = []; @@ -163,7 +165,8 @@ export class AdvancedFilterCreatorFilterItem extends PureComponent< column, selectedType, value, - formatter.timeZone + formatter.timeZone, + tableUtils ); for (let i = 0; i < filterTypes.length; i += 1) { const type = filterTypes[i]; diff --git a/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx b/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx index ca7ef9a52f..fc6a63cd73 100644 --- a/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx +++ b/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx @@ -17,6 +17,7 @@ interface AdvancedFilterCreatorSelectValueProps { onChange: (selectedValues: T[], invertSelection: boolean) => void; showSearch: boolean; timeZone: string; + tableUtils: TableUtils; } interface AdvancedFilterCreatorSelectValueState { @@ -222,7 +223,7 @@ class AdvancedFilterCreatorSelectValue extends PureComponent< updateTableFilter(): void { const { table, searchText } = this.state; - const { timeZone } = this.props; + const { timeZone, tableUtils } = this.props; const column = table?.columns[0]; const filters = []; if (column == null) { @@ -233,14 +234,14 @@ class AdvancedFilterCreatorSelectValue extends PureComponent< let filter = null; if (TableUtils.isCharType(column.type)) { // Just exact match for char - filter = TableUtils.makeQuickFilter(column, searchText); + filter = tableUtils.makeQuickFilter(column, searchText); } else if (TableUtils.isTextType(column.type)) { // case insensitive & contains search text - filter = TableUtils.makeQuickFilter(column, `~${searchText}`, timeZone); + filter = tableUtils.makeQuickFilter(column, `~${searchText}`, timeZone); } else { // greater than or equal search for everything else // we may want to be smarter with some other types (like dates) - filter = TableUtils.makeQuickFilter( + filter = tableUtils.makeQuickFilter( column, `>=${searchText}`, timeZone diff --git a/packages/iris-grid/src/IrisGrid.tsx b/packages/iris-grid/src/IrisGrid.tsx index 3e8d1c5323..15bfb4ba86 100644 --- a/packages/iris-grid/src/IrisGrid.tsx +++ b/packages/iris-grid/src/IrisGrid.tsx @@ -61,7 +61,9 @@ import { vsSymbolOperator, vsTools, } from '@deephaven/icons'; -import dh, { +import { + dh as defaultDh, + dhType, Column, ColumnGroup, CustomColumn, @@ -251,6 +253,7 @@ export type FilterMap = Map< >; export interface IrisGridProps { children: React.ReactNode; + dh: dhType; advancedFilters: ReadonlyAdvancedFilterMap; advancedSettings: Map; alwaysFetchColumns: readonly ColumnName[]; @@ -438,6 +441,7 @@ export class IrisGrid extends Component { static defaultProps = { children: null, + dh: defaultDh, advancedFilters: EMPTY_MAP, advancedSettings: EMPTY_MAP, alwaysFetchColumns: EMPTY_ARRAY, @@ -505,19 +509,6 @@ export class IrisGrid extends Component { canToggleSearch: true, }; - static makeQuickFilter( - column: Column, - text: string, - timeZone: string - ): FilterCondition | null { - try { - return TableUtils.makeQuickFilter(column, text, timeZone); - } catch (err) { - log.error('Error creating quick filter', err); - } - return null; - } - constructor(props: IrisGridProps) { super(props); @@ -688,6 +679,7 @@ export class IrisGrid extends Component { ]; const { + dh, aggregationSettings, conditionalFormats, customColumnFormatMap, @@ -760,6 +752,8 @@ export class IrisGrid extends Component { invertSearchColumns ); + this.tableUtils = new TableUtils(dh); + this.state = { isFilterBarShown, isSelectingPartition, @@ -1025,6 +1019,8 @@ export class IrisGrid extends Component { contextActions: ContextAction[]; + tableUtils: TableUtils; + getAdvancedMenuOpenedHandler = memoize( (column: ModelIndex) => this.handleAdvancedMenuOpened.bind(this, column), { max: 100 } @@ -1036,7 +1032,8 @@ export class IrisGrid extends Component { column: Column, advancedFilterOptions: AdvancedFilterOptions | undefined, sortDirection: SortDirection | undefined, - formatter: Formatter + formatter: Formatter, + tableUtils: TableUtils ) => ( { onDone={this.handleAdvancedFilterDone} options={advancedFilterOptions} sortDirection={sortDirection} + // TODO: use formatter and tableUtils from the model? formatter={formatter} + tableUtils={tableUtils} /> ), { max: 50 } @@ -1424,6 +1423,19 @@ export class IrisGrid extends Component { return GridUtils.getVisibleIndex(modelIndex, movedColumns); } + makeQuickFilter( + column: Column, + text: string, + timeZone: string + ): FilterCondition | null { + try { + return this.tableUtils.makeQuickFilter(column, text, timeZone); + } catch (err) { + log.error('Error creating quick filter', err); + } + return null; + } + /** * Applies the provided input filters as quick filters, * and clears any existing quickFilters or advancedFilters on that column @@ -1491,7 +1503,7 @@ export class IrisGrid extends Component { } quickFilters.set(modelIndex, { text: value, - filter: IrisGrid.makeQuickFilter(column, value, formatter.timeZone), + filter: this.makeQuickFilter(column, value, formatter.timeZone), }); return true; } @@ -1583,7 +1595,7 @@ export class IrisGrid extends Component { const { formatter } = model; this.setQuickFilter( columnIndex, - IrisGrid.makeQuickFilter(column, combinedText, formatter.timeZone), + this.makeQuickFilter(column, combinedText, formatter.timeZone), `${combinedText}` ); } @@ -1678,7 +1690,7 @@ export class IrisGrid extends Component { advancedFilters.forEach((value, key) => { const { options } = value; const column = columns[key]; - const filter = TableUtils.makeAdvancedFilter( + const filter = this.tableUtils.makeAdvancedFilter( column, options, formatter.timeZone @@ -1694,7 +1706,7 @@ export class IrisGrid extends Component { const column = columns[key]; newQuickFilters.set(key, { text, - filter: IrisGrid.makeQuickFilter(column, text, formatter.timeZone), + filter: this.makeQuickFilter(column, text, formatter.timeZone), }); }); @@ -3292,7 +3304,7 @@ export class IrisGrid extends Component { gotoValueSelectedColumnName: selectedColumnName, gotoValueSelectedFilter, } = this.state; - const { model } = this.props; + const { model, dh } = this.props; if (!model.isSeekRowAvailable) { return; } @@ -3342,6 +3354,7 @@ export class IrisGrid extends Component { } case TableUtils.dataType.DATETIME: { const [startDate] = DateUtils.parseDateRange( + dh, inputString, formatter.timeZone ); @@ -3396,7 +3409,7 @@ export class IrisGrid extends Component { searchFromRow, selectedColumn, dh.ValueType.STRING, - TableUtils.makeValue( + this.tableUtils.makeValue( selectedColumn.type, inputString, formatter.timeZone @@ -3836,6 +3849,7 @@ export class IrisGrid extends Component { render(): ReactElement | null { const { + dh, children, customFilters, getDownloadWorker, @@ -4213,7 +4227,9 @@ export class IrisGrid extends Component { column, advancedFilterOptions, sortDirection, - formatter + formatter, + // TODO: + this.tableUtils )} @@ -4254,6 +4270,7 @@ export class IrisGrid extends Component { return ( { let inputTable = null; if (!TableUtils.isTreeTable(table) && table.hasInputTable) { inputTable = await table.inputTable(); } - return new IrisGridProxyModel(table, formatter, inputTable); + return new IrisGridProxyModel(table, tableUtils, formatter, inputTable); } } diff --git a/packages/iris-grid/src/IrisGridProxyModel.ts b/packages/iris-grid/src/IrisGridProxyModel.ts index 38f01f3a10..e25b753f61 100644 --- a/packages/iris-grid/src/IrisGridProxyModel.ts +++ b/packages/iris-grid/src/IrisGridProxyModel.ts @@ -44,13 +44,14 @@ const log = Log.module('IrisGridProxyModel'); function makeModel( table: Table | TreeTable, + tableUtils: TableUtils, formatter?: Formatter, inputTable?: InputTable | null ): IrisGridModel { if (TableUtils.isTreeTable(table)) { - return new IrisGridTreeTableModel(table, formatter); + return new IrisGridTreeTableModel(table, tableUtils, formatter); } - return new IrisGridTableModel(table, formatter, inputTable); + return new IrisGridTableModel(table, tableUtils, formatter, inputTable); } /** @@ -74,8 +75,11 @@ class IrisGridProxyModel extends IrisGridModel { selectDistinct: ColumnName[]; + tableUtils: TableUtils; + constructor( table: Table | TreeTable, + tableUtils: TableUtils, formatter = new Formatter(), inputTable: InputTable | null = null ) { @@ -83,12 +87,13 @@ class IrisGridProxyModel extends IrisGridModel { this.handleModelEvent = this.handleModelEvent.bind(this); - const model = makeModel(table, formatter, inputTable); + const model = makeModel(table, tableUtils, formatter, inputTable); this.originalModel = model; this.model = model; this.modelPromise = null; this.rollup = null; this.selectDistinct = []; + this.tableUtils = tableUtils; } close(): void { @@ -521,7 +526,7 @@ class IrisGridProxyModel extends IrisGridModel { ) { modelPromise = this.originalModel.table .rollup(rollupConfig) - .then(table => makeModel(table, this.formatter)); + .then(table => makeModel(table, this.tableUtils, this.formatter)); } this.setNextModel(modelPromise); } @@ -559,7 +564,7 @@ class IrisGridProxyModel extends IrisGridModel { ) { modelPromise = this.originalModel.table .selectDistinct(selectDistinctColumns) - .then(table => makeModel(table, this.formatter)); + .then(table => makeModel(table, this.tableUtils, this.formatter)); } this.setNextModel(modelPromise); } diff --git a/packages/iris-grid/src/IrisGridTableModel.ts b/packages/iris-grid/src/IrisGridTableModel.ts index 6b0375f613..906c4b9706 100644 --- a/packages/iris-grid/src/IrisGridTableModel.ts +++ b/packages/iris-grid/src/IrisGridTableModel.ts @@ -36,15 +36,17 @@ class IrisGridTableModel extends IrisGridTableModelTemplate { /** * @param table Iris data table to be used in the model + * TODO: * @param formatter The formatter to use when getting formats * @param inputTable Iris input table associated with this table */ constructor( table: Table, + tableUtils: TableUtils, formatter = new Formatter(), inputTable: InputTable | null = null ) { - super(table, formatter, inputTable); + super(table, tableUtils, formatter, inputTable); this.customColumnList = []; this.formatColumnList = []; } @@ -341,7 +343,10 @@ class IrisGridTableModel extends IrisGridTableModelTemplate { for (let c = 0; c < keyColumns.length; c += 1) { const column = keyColumns[c]; const value = row[c]; - const filterValue = TableUtils.makeFilterRawValue(column.type, value); + const filterValue = this.tableUtils.makeFilterRawValue( + column.type, + value + ); const filter = column.filter().eq(filterValue); columnFilters.push(filter); } diff --git a/packages/iris-grid/src/IrisGridTableModelTemplate.ts b/packages/iris-grid/src/IrisGridTableModelTemplate.ts index 65940255b4..19a0d3eab5 100644 --- a/packages/iris-grid/src/IrisGridTableModelTemplate.ts +++ b/packages/iris-grid/src/IrisGridTableModelTemplate.ts @@ -142,6 +142,8 @@ class IrisGridTableModelTemplate< private irisFormatter: Formatter; + tableUtils: TableUtils; + inputTable: InputTable | null; private subscription: TableViewportSubscription | null; @@ -194,6 +196,7 @@ class IrisGridTableModelTemplate< */ constructor( table: T, + tableUtils: TableUtils, formatter = new Formatter(), inputTable: InputTable | null = null ) { @@ -212,6 +215,7 @@ class IrisGridTableModelTemplate< this.inputTable = inputTable; this.subscription = null; this.table = table; + this.tableUtils = tableUtils; this.viewport = null; this.viewportData = null; this.formattedStringData = []; @@ -1411,7 +1415,7 @@ class IrisGridTableModelTemplate< } if (tableRanges.length > 0) { - const rangeSet = IrisGridUtils.rangeSetFromRanges(tableRanges); + const rangeSet = IrisGridUtils.rangeSetFromRanges(dh, tableRanges); const snapshot = await this.subscription.snapshot(rangeSet, columns); result.push( ...snapshot.rows.map(rowData => @@ -1688,7 +1692,7 @@ class IrisGridTableModelTemplate< const column = this.columns[x]; columnSet.add(column); if (formattedText[x] === undefined) { - const value = TableUtils.makeValue( + const value = this.tableUtils.makeValue( column.type, text, this.formatter.timeZone @@ -1725,7 +1729,7 @@ class IrisGridTableModelTemplate< assertNotNull(row); const { data: rowData } = row; const newRowData = new Map(rowData); - const value = TableUtils.makeValue( + const value = this.tableUtils.makeValue( column.type, text, this.formatter.timeZone @@ -1770,7 +1774,7 @@ class IrisGridTableModelTemplate< } columnSet.forEach(column => { - newRow[column.name] = TableUtils.makeValue( + newRow[column.name] = this.tableUtils.makeValue( column.type, text, this.formatter.timeZone @@ -1831,7 +1835,7 @@ class IrisGridTableModelTemplate< const x = edit.column ?? edit.x; const y = edit.row ?? edit.y; const column = this.columns[x]; - const value = TableUtils.makeValue( + const value = this.tableUtils.makeValue( column.type, text, this.formatter.timeZone @@ -1923,7 +1927,7 @@ class IrisGridTableModelTemplate< if (rowEdits != null) { rowEdits.forEach(edit => { const column = this.columns[edit.column ?? edit.x]; - newRow[column.name] = TableUtils.makeValue( + newRow[column.name] = this.tableUtils.makeValue( column.type, edit.text, this.formatter.timeZone @@ -1949,7 +1953,7 @@ class IrisGridTableModelTemplate< const x = edit.column ?? edit.x; const y = edit.row ?? edit.y; const column = this.columns[x]; - const value = TableUtils.makeValue( + const value = this.tableUtils.makeValue( column.type, text, this.formatter.timeZone @@ -2083,7 +2087,10 @@ class IrisGridTableModelTemplate< for (let c = 0; c < keyColumns.length; c += 1) { const column = keyColumns[c]; const value = row[c]; - const filterValue = TableUtils.makeFilterRawValue(column.type, value); + const filterValue = this.tableUtils.makeFilterRawValue( + column.type, + value + ); const filter = column.filter().eq(filterValue); columnFilters.push(filter); } @@ -2100,7 +2107,7 @@ class IrisGridTableModelTemplate< isValidForCell(x: ModelIndex, y: ModelIndex, value: string): boolean { try { const column = this.columns[x]; - TableUtils.makeValue(column.type, value, this.formatter.timeZone); + this.tableUtils.makeValue(column.type, value, this.formatter.timeZone); return true; } catch (e) { return false; diff --git a/packages/iris-grid/src/IrisGridTestUtils.ts b/packages/iris-grid/src/IrisGridTestUtils.ts index 3509be7c24..8550151dfb 100644 --- a/packages/iris-grid/src/IrisGridTestUtils.ts +++ b/packages/iris-grid/src/IrisGridTestUtils.ts @@ -10,10 +10,12 @@ import dh, { TableViewportSubscription, TreeTable, } from '@deephaven/jsapi-shim'; -import { Formatter } from '@deephaven/jsapi-utils'; +import { Formatter, TableUtils } from '@deephaven/jsapi-utils'; import type { LayoutHints } from '@deephaven/jsapi-shim'; import IrisGridProxyModel from './IrisGridProxyModel'; +const tableUtils = new TableUtils(dh); + class IrisGridTestUtils { static DEFAULT_TYPE = 'java.lang.String'; @@ -124,7 +126,7 @@ class IrisGridTestUtils { formatter = new Formatter(), inputTable: InputTable | null = null ): IrisGridProxyModel { - return new IrisGridProxyModel(table, formatter, inputTable); + return new IrisGridProxyModel(table, tableUtils, formatter, inputTable); } } diff --git a/packages/iris-grid/src/IrisGridUtils.test.ts b/packages/iris-grid/src/IrisGridUtils.test.ts index ee4a623925..3c47d1d77e 100644 --- a/packages/iris-grid/src/IrisGridUtils.test.ts +++ b/packages/iris-grid/src/IrisGridUtils.test.ts @@ -1,7 +1,7 @@ import { GridUtils, GridRange, MoveOperation } from '@deephaven/grid'; import dh, { Column, Table, Sort } from '@deephaven/jsapi-shim'; import { TypeValue as FilterTypeValue } from '@deephaven/filters'; -import { DateUtils } from '@deephaven/jsapi-utils'; +import { DateUtils, TableUtils } from '@deephaven/jsapi-utils'; import type { AdvancedFilter } from './CommonTypes'; import { FilterData } from './IrisGrid'; import IrisGridTestUtils from './IrisGridTestUtils'; @@ -10,6 +10,8 @@ import IrisGridUtils, { LegacyDehydratedSort, } from './IrisGridUtils'; +const tableUtils = new TableUtils(dh); + function makeFilter() { // eslint-disable-next-line @typescript-eslint/no-explicit-any return new (dh as any).FilterCondition(); @@ -51,7 +53,8 @@ describe('quickfilters tests', () => { const importedFilters = IrisGridUtils.hydrateQuickFilters( table.columns, - exportedFilters + exportedFilters, + tableUtils ); expect(importedFilters).toEqual(filters); }); @@ -70,7 +73,8 @@ describe('quickfilters tests', () => { const importedFilters = IrisGridUtils.hydrateQuickFilters( table.columns, - exportedFilters + exportedFilters, + tableUtils ); expect(importedFilters).toEqual( new Map([ @@ -99,6 +103,7 @@ describe('advanced filter tests', () => { const importedFilters = IrisGridUtils.hydrateAdvancedFilters( table.columns, exportedFilters, + tableUtils, 'America/New_York' ); expect(importedFilters).toEqual(filters); @@ -127,6 +132,7 @@ describe('advanced filter tests', () => { const importedFilters = IrisGridUtils.hydrateAdvancedFilters( table.columns, exportedFilters, + tableUtils, 'America/New_York' ); expect(importedFilters).toEqual( diff --git a/packages/iris-grid/src/IrisGridUtils.ts b/packages/iris-grid/src/IrisGridUtils.ts index 4c025e023e..7ad597de11 100644 --- a/packages/iris-grid/src/IrisGridUtils.ts +++ b/packages/iris-grid/src/IrisGridUtils.ts @@ -7,10 +7,11 @@ import { MoveOperation, VisibleIndex, } from '@deephaven/grid'; -import dh, { +import { Column, ColumnGroup, DateWrapper, + dhType, FilterCondition, LongWrapper, RangeSet, @@ -278,6 +279,7 @@ class IrisGridUtils { * @param irisGridState The current state of the IrisGrid */ static dehydrateIrisGridState( + dh: dhType, model: IrisGridModel, irisGridState: HydratedIrisGridState ): DehydratedIrisGridState { @@ -307,6 +309,7 @@ class IrisGridUtils { const { columns } = model; return { advancedFilters: IrisGridUtils.dehydrateAdvancedFilters( + dh, columns, advancedFilters ), @@ -334,6 +337,7 @@ class IrisGridUtils { selectedSearchColumns, invertSearchColumns, pendingDataMap: IrisGridUtils.dehydratePendingDataMap( + dh, columns, pendingDataMap ), @@ -352,8 +356,10 @@ class IrisGridUtils { * @param irisGridState The saved IrisGrid state */ static hydrateIrisGridState( + dh: dhType, model: IrisGridModel, - irisGridState: DehydratedIrisGridState + irisGridState: DehydratedIrisGridState, + tableUtils: TableUtils ): Omit & { userColumnWidths: ModelSizeMap; userRowHeights: ModelSizeMap; @@ -384,8 +390,10 @@ class IrisGridUtils { return { advancedFilters: IrisGridUtils.hydrateAdvancedFilters( + dh, columns, advancedFilters, + tableUtils, formatter.timeZone ), aggregationSettings, @@ -394,9 +402,10 @@ class IrisGridUtils { quickFilters: IrisGridUtils.hydrateQuickFilters( columns, quickFilters, + tableUtils, formatter.timeZone ), - sorts: IrisGridUtils.hydrateSort(columns, sorts), + sorts: IrisGridUtils.hydrateSort(dh, columns, sorts), userColumnWidths: new Map( userColumnWidths .map(([column, width]: [string | number, number]): [ @@ -427,6 +436,7 @@ class IrisGridUtils { selectedSearchColumns, invertSearchColumns, pendingDataMap: IrisGridUtils.hydratePendingDataMap( + dh, columns, pendingDataMap ) as PendingDataMap, @@ -542,6 +552,7 @@ class IrisGridUtils { static hydrateQuickFilters( columns: readonly Column[], savedQuickFilters: readonly DehydratedQuickFilter[], + tableUtils: TableUtils, timeZone?: string ): ReadonlyQuickFilterMap { const importedFilters = savedQuickFilters.map( @@ -555,7 +566,7 @@ class IrisGridUtils { try { const column = IrisGridUtils.getColumn(columns, columnIndex); if (column != null) { - filter = TableUtils.makeQuickFilter(column, text, timeZone); + filter = tableUtils.makeQuickFilter(column, text, timeZone); } } catch (error) { log.error('hydrateQuickFilters error with', text, error); @@ -575,6 +586,7 @@ class IrisGridUtils { * @returns The dehydrated advanced filters */ static dehydrateAdvancedFilters( + dh: dhType, columns: readonly Column[], advancedFilters: ReadonlyAdvancedFilterMap ): DehydratedAdvancedFilter[] { @@ -582,6 +594,7 @@ class IrisGridUtils { const column = IrisGridUtils.getColumn(columns, columnIndex); assertNotNull(column); const options = IrisGridUtils.dehydrateAdvancedFilterOptions( + dh, column, advancedFilter.options ); @@ -597,8 +610,10 @@ class IrisGridUtils { * @returns The advanced filters to apply to the columns */ static hydrateAdvancedFilters( + dh: dhType, columns: readonly Column[], savedAdvancedFilters: readonly DehydratedAdvancedFilter[], + tableUtils: TableUtils, timeZone: string ): ReadonlyAdvancedFilterMap { const importedFilters = savedAdvancedFilters.map( @@ -609,6 +624,7 @@ class IrisGridUtils { const column = IrisGridUtils.getColumn(columns, columnIndex); assertNotNull(column); const options = IrisGridUtils.hydrateAdvancedFilterOptions( + dh, column, advancedFilter.options ); @@ -617,7 +633,7 @@ class IrisGridUtils { try { const columnRetrieved = IrisGridUtils.getColumn(columns, columnIndex); if (columnRetrieved != null) { - filter = TableUtils.makeAdvancedFilter(column, options, timeZone); + filter = tableUtils.makeAdvancedFilter(column, options, timeZone); } } catch (error) { log.error('hydrateAdvancedFilters error with', options, error); @@ -631,32 +647,35 @@ class IrisGridUtils { } static dehydrateAdvancedFilterOptions( + dh: dhType, column: Column, options: AdvancedFilterOptions ): AdvancedFilterOptions { const { selectedValues, ...otherOptions } = options; return { selectedValues: selectedValues?.map((value: unknown) => - IrisGridUtils.dehydrateValue(value, column?.type) + IrisGridUtils.dehydrateValue(dh, value, column?.type) ), ...otherOptions, }; } static hydrateAdvancedFilterOptions( + dh: dhType, column: Column, options: AdvancedFilterOptions ): AdvancedFilterOptions { const { selectedValues, ...otherOptions } = options; return { selectedValues: selectedValues?.map(value => - IrisGridUtils.hydrateValue(value, column?.type) + IrisGridUtils.hydrateValue(dh, value, column?.type) ), ...otherOptions, }; } static dehydratePendingDataMap( + dh: dhType, columns: readonly Column[], pendingDataMap: ReadonlyMap< ModelIndex, @@ -670,13 +689,14 @@ class IrisGridUtils { { data: [...data].map(([c, value]) => [ columns[c].name, - IrisGridUtils.dehydrateValue(value, columns[c].type), + IrisGridUtils.dehydrateValue(dh, value, columns[c].type), ]), }, ]); } static hydratePendingDataMap( + dh: dhType, columns: readonly Column[], pendingDataMap: DehydratedPendingDataMap ): Map< @@ -708,7 +728,7 @@ class IrisGridUtils { assertNotNull(index); return [ getColumnIndex(columnName) ?? null, - IrisGridUtils.hydrateValue(value, columns[index].type), + IrisGridUtils.hydrateValue(dh, value, columns[index].type), ]; }) ), @@ -723,9 +743,14 @@ class IrisGridUtils { * @param value The value to dehydrate * @param columnType The column type */ - static dehydrateValue(value: T, columnType: string): string | T | null { + static dehydrateValue( + dh: dhType, + value: T, + columnType: string + ): string | T | null { if (TableUtils.isDateType(columnType)) { return IrisGridUtils.dehydrateDateTime( + dh, (value as unknown) as number | DateWrapper | Date ); } @@ -743,27 +768,31 @@ class IrisGridUtils { * @param columnType The type of column */ static hydrateValue( + dh: dhType, value: T, columnType: string ): DateWrapper | LongWrapper | T | null { if (TableUtils.isDateType(columnType)) { - return IrisGridUtils.hydrateDateTime((value as unknown) as string); + return IrisGridUtils.hydrateDateTime(dh, (value as unknown) as string); } if (TableUtils.isLongType(columnType)) { - return IrisGridUtils.hydrateLong((value as unknown) as string); + return IrisGridUtils.hydrateLong(dh, (value as unknown) as string); } return value; } - static dehydrateDateTime(value: number | DateWrapper | Date): string | null { + static dehydrateDateTime( + dh: dhType, + value: number | DateWrapper | Date + ): string | null { return value != null ? dh.i18n.DateTimeFormat.format(DateUtils.FULL_DATE_FORMAT, value) : null; } - static hydrateDateTime(value: string): DateWrapper | null { + static hydrateDateTime(dh: dhType, value: string): DateWrapper | null { return value != null ? dh.i18n.DateTimeFormat.parse(DateUtils.FULL_DATE_FORMAT, value) : null; @@ -773,7 +802,7 @@ class IrisGridUtils { return value != null ? `${value}` : null; } - static hydrateLong(value: string): LongWrapper | null { + static hydrateLong(dh: dhType, value: string): LongWrapper | null { return value != null ? dh.LongWrapper.ofString(value) : null; } @@ -800,6 +829,7 @@ class IrisGridUtils { * @returns The sorts to apply to the table */ static hydrateSort( + dh: dhType, columns: readonly Column[], sorts: readonly (DehydratedSort | LegacyDehydratedSort)[] ): Sort[] { @@ -884,8 +914,10 @@ class IrisGridUtils { * @param timeZone The time zone to make this value in if it is a date type. E.g. America/New_York */ static applyTableSettings( + dh: dhType, table: Table, tableSettings: TableSettings, + tableUtils: TableUtils, timeZone: string ): void { const { columns } = table; @@ -896,6 +928,7 @@ class IrisGridUtils { IrisGridUtils.hydrateQuickFilters( columns, tableSettings.quickFilters, + tableUtils, timeZone ) ); @@ -905,21 +938,24 @@ class IrisGridUtils { if (tableSettings.advancedFilters) { advancedFilters = IrisGridUtils.getFiltersFromFilterMap( IrisGridUtils.hydrateAdvancedFilters( + dh, columns, tableSettings.advancedFilters, + tableUtils, timeZone ) ); } const inputFilters = IrisGridUtils.getFiltersFromInputFilters( columns, + tableUtils, tableSettings.inputFilters, timeZone ); let sorts: Sort[] = []; if (tableSettings.sorts) { - sorts = IrisGridUtils.hydrateSort(columns, tableSettings.sorts); + sorts = IrisGridUtils.hydrateSort(dh, columns, tableSettings.sorts); } let filters = [...quickFilters, ...advancedFilters]; @@ -956,6 +992,7 @@ class IrisGridUtils { static getFiltersFromInputFilters( columns: readonly Column[], + tableUtils: TableUtils, inputFilters: readonly InputFilter[] = [], timeZone?: string ): FilterCondition[] { @@ -967,7 +1004,7 @@ class IrisGridUtils { ); if (column) { try { - return TableUtils.makeQuickFilter(column, value, timeZone); + return tableUtils.makeQuickFilter(column, value, timeZone); } catch (e) { // It may be unable to create it because user hasn't completed their input log.debug('Unable to create input filter', e); @@ -1251,7 +1288,10 @@ class IrisGridUtils { * @param ranges The ranges to get the range set for * @returns The rangeset for the provided ranges */ - static rangeSetFromRanges(ranges: readonly GridRange[]): RangeSet { + static rangeSetFromRanges( + dh: dhType, + ranges: readonly GridRange[] + ): RangeSet { const rangeSets = ranges .slice() .sort((a, b): number => { diff --git a/packages/iris-grid/src/sidebar/ChartBuilder.test.tsx b/packages/iris-grid/src/sidebar/ChartBuilder.test.tsx index a1164cdc20..dcaae29748 100644 --- a/packages/iris-grid/src/sidebar/ChartBuilder.test.tsx +++ b/packages/iris-grid/src/sidebar/ChartBuilder.test.tsx @@ -16,7 +16,12 @@ function makeChartBuilderWrapper({ ), } = {}) { return render( - + ); } diff --git a/packages/iris-grid/src/sidebar/ChartBuilder.tsx b/packages/iris-grid/src/sidebar/ChartBuilder.tsx index e9ed5d33c1..7032281d44 100644 --- a/packages/iris-grid/src/sidebar/ChartBuilder.tsx +++ b/packages/iris-grid/src/sidebar/ChartBuilder.tsx @@ -10,7 +10,7 @@ import { vsCircleLargeFilled, vsTrash, } from '@deephaven/icons'; -import dh, { Column, SeriesPlotStyle } from '@deephaven/jsapi-shim'; +import { Column, dhType, SeriesPlotStyle } from '@deephaven/jsapi-shim'; import Log from '@deephaven/log'; import shortid from 'shortid'; import { @@ -37,6 +37,7 @@ export type SeriesItem = { }; interface ChartBuilderProps { + dh: dhType; model: IrisGridModel; onSubmit: (obj: ChartBuilderSettings) => void; onChange: (obj: ChartBuilderSettings) => void; @@ -58,54 +59,7 @@ interface ChartBuilderState { * Form for configuring all the settings when creating a console. */ class ChartBuilder extends PureComponent { - static types = [ - dh.plot.SeriesPlotStyle.LINE, - dh.plot.SeriesPlotStyle.BAR, - dh.plot.SeriesPlotStyle.SCATTER, - dh.plot.SeriesPlotStyle.PIE, - // IDS-6808: Disable Histogram in Chart Builder until we pipe histogram creation through the API - // dh.plot.SeriesPlotStyle.HISTOGRAM, - ]; - - /** - * Converts the provided chart type into a readable type. - * Just replaces underscores with spaces and capitals the first letter of each word. - */ - static getTypeName(type: SeriesPlotStyle): string | SeriesPlotStyle { - switch (type) { - case dh.plot.SeriesPlotStyle.LINE: - return 'Line'; - case dh.plot.SeriesPlotStyle.BAR: - return 'Bar'; - case dh.plot.SeriesPlotStyle.SCATTER: - return 'Scatter'; - case dh.plot.SeriesPlotStyle.PIE: - return 'Pie'; - case dh.plot.SeriesPlotStyle.HISTOGRAM: - return 'Histogram'; - default: - return type; - } - } - - static getTypeIcon(type: SeriesPlotStyle): React.ReactElement | null { - switch (type) { - case dh.plot.SeriesPlotStyle.LINE: - return ; - case dh.plot.SeriesPlotStyle.BAR: - return ; - case dh.plot.SeriesPlotStyle.SCATTER: - return ; - case dh.plot.SeriesPlotStyle.PIE: - return ; - case dh.plot.SeriesPlotStyle.HISTOGRAM: - return ; - default: - return null; - } - } - - static getMaxSeriesCount(type: SeriesPlotStyle): number { + static getMaxSeriesCount(dh: dhType, type: SeriesPlotStyle): number { switch (type) { case dh.plot.SeriesPlotStyle.PIE: return 1; @@ -116,35 +70,16 @@ class ChartBuilder extends PureComponent { } } - static getXAxisLabel(type: SeriesPlotStyle): string { - switch (type) { - case dh.plot.SeriesPlotStyle.PIE: - return 'Labels'; - case dh.plot.SeriesPlotStyle.HISTOGRAM: - return 'Data'; - default: - return 'X-Axis'; - } - } - - static getSeriesLabel(type: SeriesPlotStyle): string { - switch (type) { - case dh.plot.SeriesPlotStyle.PIE: - return 'Values'; - default: - return 'Series'; - } - } - static makeSeriesItem(value: string): SeriesItem { return { id: shortid.generate(), value }; } static makeDefaultSeriesItems( + dh: dhType, type: SeriesPlotStyle, columns: readonly Column[] ): SeriesItem[] { - const maxSeriesCount = ChartBuilder.getMaxSeriesCount(type); + const maxSeriesCount = ChartBuilder.getMaxSeriesCount(dh, type); if (maxSeriesCount === 0 || columns == null || columns.length === 0) { return []; } @@ -177,12 +112,12 @@ class ChartBuilder extends PureComponent { this.handleXAxisChange = this.handleXAxisChange.bind(this); this.sendChange = this.sendChange.bind(this); - const { model } = props; + const { dh, model } = props; const { columns } = model; - const type = ChartBuilder.types[0]; + const type = this.getTypes()[0]; const xAxis = ChartBuilder.getDefaultXAxis(type, columns) as string; - const seriesItems = ChartBuilder.makeDefaultSeriesItems(type, columns); + const seriesItems = ChartBuilder.makeDefaultSeriesItems(dh, type, columns); this.state = { /** The selected chart type */ @@ -199,6 +134,80 @@ class ChartBuilder extends PureComponent { }; } + getTypes() { + const { dh } = this.props; + return [ + dh.plot.SeriesPlotStyle.LINE, + dh.plot.SeriesPlotStyle.BAR, + dh.plot.SeriesPlotStyle.SCATTER, + dh.plot.SeriesPlotStyle.PIE, + // IDS-6808: Disable Histogram in Chart Builder until we pipe histogram creation through the API + // dh.plot.SeriesPlotStyle.HISTOGRAM, + ]; + } + + /** + * Converts the provided chart type into a readable type. + * Just replaces underscores with spaces and capitals the first letter of each word. + */ + getTypeName(type: SeriesPlotStyle): string | SeriesPlotStyle { + const { dh } = this.props; + switch (type) { + case dh.plot.SeriesPlotStyle.LINE: + return 'Line'; + case dh.plot.SeriesPlotStyle.BAR: + return 'Bar'; + case dh.plot.SeriesPlotStyle.SCATTER: + return 'Scatter'; + case dh.plot.SeriesPlotStyle.PIE: + return 'Pie'; + case dh.plot.SeriesPlotStyle.HISTOGRAM: + return 'Histogram'; + default: + return type; + } + } + + getTypeIcon(type: SeriesPlotStyle): React.ReactElement | null { + const { dh } = this.props; + switch (type) { + case dh.plot.SeriesPlotStyle.LINE: + return ; + case dh.plot.SeriesPlotStyle.BAR: + return ; + case dh.plot.SeriesPlotStyle.SCATTER: + return ; + case dh.plot.SeriesPlotStyle.PIE: + return ; + case dh.plot.SeriesPlotStyle.HISTOGRAM: + return ; + default: + return null; + } + } + + getXAxisLabel(type: SeriesPlotStyle): string { + const { dh } = this.props; + switch (type) { + case dh.plot.SeriesPlotStyle.PIE: + return 'Labels'; + case dh.plot.SeriesPlotStyle.HISTOGRAM: + return 'Data'; + default: + return 'X-Axis'; + } + } + + getSeriesLabel(type: SeriesPlotStyle): string { + const { dh } = this.props; + switch (type) { + case dh.plot.SeriesPlotStyle.PIE: + return 'Values'; + default: + return 'Series'; + } + } + handleAddSeries(): void { this.setState(state => { const { seriesItems } = state; @@ -220,12 +229,12 @@ class ChartBuilder extends PureComponent { } handleReset(): void { - const { model } = this.props; + const { dh, model } = this.props; const { columns } = model; - const type = ChartBuilder.types[0]; + const type = this.getTypes()[0]; const xAxis = ChartBuilder.getDefaultXAxis(type, columns) as string; - const seriesItems = ChartBuilder.makeDefaultSeriesItems(type, columns); + const seriesItems = ChartBuilder.makeDefaultSeriesItems(dh, type, columns); const isLinked = true; this.setState({ type, seriesItems, xAxis, isLinked }, this.sendChange); @@ -280,18 +289,18 @@ class ChartBuilder extends PureComponent { const index = changeEvent.target.getAttribute('data-index') as string; const intIndex = parseInt(index, 10); - const type = ChartBuilder.types[intIndex]; + const type = this.getTypes()[intIndex]; log.debug2('handleTypeSelect', type); this.setState(state => { - const maxSeriesCount = ChartBuilder.getMaxSeriesCount(type); + const { dh, model } = this.props; + const maxSeriesCount = ChartBuilder.getMaxSeriesCount(dh, type); let { seriesItems } = state; seriesItems = seriesItems.slice(0, maxSeriesCount); if (seriesItems.length === 0 && maxSeriesCount > 0) { - const { model } = this.props; const { columns } = model; - seriesItems = ChartBuilder.makeDefaultSeriesItems(type, columns); + seriesItems = ChartBuilder.makeDefaultSeriesItems(dh, type, columns); } return { type, seriesItems }; @@ -314,12 +323,12 @@ class ChartBuilder extends PureComponent { } render(): JSX.Element { - const { model } = this.props; + const { dh, model } = this.props; const { columns } = model; const { seriesItems, type, xAxis, isLinked } = this.state; - const maxSeriesCount = ChartBuilder.getMaxSeriesCount(type); - const xAxisLabel = ChartBuilder.getXAxisLabel(type); - const seriesLabel = ChartBuilder.getSeriesLabel(type); + const maxSeriesCount = ChartBuilder.getMaxSeriesCount(dh, type); + const xAxisLabel = this.getXAxisLabel(type); + const seriesLabel = this.getSeriesLabel(type); const isSeriesVisible = seriesItems.length > 0; const isAddSeriesVisible = seriesItems.length < maxSeriesCount; @@ -329,7 +338,7 @@ class ChartBuilder extends PureComponent {
- {ChartBuilder.types.map((chartType, index) => { + {this.getTypes().map((chartType, index) => { const key = (chartType as unknown) as React.Key; return (
@@ -346,8 +355,8 @@ class ChartBuilder extends PureComponent { data-index={index} onClick={this.handleTypeClick} > - {ChartBuilder.getTypeIcon(chartType)} - {ChartBuilder.getTypeName(chartType)} + {this.getTypeIcon(chartType)} + {this.getTypeName(chartType)}
); diff --git a/packages/jsapi-utils/src/TableUtils.test.ts b/packages/jsapi-utils/src/TableUtils.test.ts index 6616cc3cce..35c0f67097 100644 --- a/packages/jsapi-utils/src/TableUtils.test.ts +++ b/packages/jsapi-utils/src/TableUtils.test.ts @@ -25,6 +25,7 @@ const DEFAULT_TIME_ZONE_ID = 'America/New_York'; const EXPECT_TIME_ZONE_PARAM = expect.objectContaining({ id: DEFAULT_TIME_ZONE_ID, }); +const tableUtils = new TableUtils(dh); /** * Sends a mock event to the last registered event handler with the given event @@ -398,7 +399,7 @@ describe('quick filter tests', () => { filter[expectedFn].mockReturnValueOnce(expectedResult); - const result = TableUtils[functionName](column, text); + const result = tableUtils[functionName](column, text); expect(filter[expectedFn]).toHaveBeenCalledWith(...args); expect(result).toBe(expectedResult); @@ -418,7 +419,7 @@ describe('quick filter tests', () => { filter[expectedFn].mockReturnValueOnce(expectedResult); - const result = TableUtils[functionName](column, text); + const result = tableUtils[functionName](column, text); expect(filter[expectedFn]).toHaveBeenCalledWith(...args); expect(result).toBe(expectedResult); @@ -454,7 +455,7 @@ describe('quick filter tests', () => { } } - const result = TableUtils[testFunction](column, text, timeZone); + const result = tableUtils[testFunction](column, text, timeZone); for (let i = 0; i < expectedFilters.length; i += 1) { const [expectedFn, expectedOperator, ...args] = expectedFilters[i]; @@ -634,7 +635,7 @@ describe('quick filter tests', () => { (notResult.and as jest.Mock).mockReturnValueOnce(expectedResult); - const result = TableUtils.makeAdvancedValueFilter( + const result = tableUtils.makeAdvancedValueFilter( column, operation, value, @@ -665,7 +666,7 @@ describe('quick filter tests', () => { filter[expectedFn].mockReturnValueOnce(expectedResult); - const result = TableUtils[functionName]( + const result = tableUtils[functionName]( column, operation, value, @@ -832,7 +833,7 @@ describe('quick filter tests', () => { nullResult.or.mockReturnValueOnce(expectedResult); - const result = TableUtils.makeAdvancedValueFilter( + const result = tableUtils.makeAdvancedValueFilter( column, FilterType.notContains, 'test', @@ -878,7 +879,7 @@ describe('quick filter tests', () => { // eslint-disable-next-line no-restricted-syntax for (const operation of invalidFilterTypes) { expect(() => - TableUtils.makeAdvancedValueFilter( + tableUtils.makeAdvancedValueFilter( column, operation, 'test', @@ -902,21 +903,21 @@ describe('quick filter tests', () => { it('handles empty cases', () => { const column = makeFilterColumn(); - expect(TableUtils.makeQuickNumberFilter(column, '')).toBe(null); + expect(tableUtils.makeQuickNumberFilter(column, '')).toBe(null); }); it('handles invalid cases', () => { const column = makeFilterColumn(); - expect(TableUtils.makeQuickNumberFilter(column, 'j*($%U#@(')).toBe(null); - expect(TableUtils.makeQuickNumberFilter(column, '<=')).toBe(null); - expect(TableUtils.makeQuickNumberFilter(column, '<=> 50')).toBe(null); - expect(TableUtils.makeQuickNumberFilter(column, '== 50')).toBe(null); - expect(TableUtils.makeQuickNumberFilter(column, '> x')).toBe(null); - expect(TableUtils.makeQuickNumberFilter(column, '50>')).toBe(null); - expect(TableUtils.makeQuickNumberFilter(column, '4,00')).toBe(null); - expect(TableUtils.makeQuickNumberFilter(column, '4,00000')).toBe(null); - expect(TableUtils.makeQuickNumberFilter(column, '40.40.40')).toBe(null); + expect(tableUtils.makeQuickNumberFilter(column, 'j*($%U#@(')).toBe(null); + expect(tableUtils.makeQuickNumberFilter(column, '<=')).toBe(null); + expect(tableUtils.makeQuickNumberFilter(column, '<=> 50')).toBe(null); + expect(tableUtils.makeQuickNumberFilter(column, '== 50')).toBe(null); + expect(tableUtils.makeQuickNumberFilter(column, '> x')).toBe(null); + expect(tableUtils.makeQuickNumberFilter(column, '50>')).toBe(null); + expect(tableUtils.makeQuickNumberFilter(column, '4,00')).toBe(null); + expect(tableUtils.makeQuickNumberFilter(column, '4,00000')).toBe(null); + expect(tableUtils.makeQuickNumberFilter(column, '40.40.40')).toBe(null); }); it('handles default operation', () => { @@ -997,38 +998,38 @@ describe('quick filter tests', () => { it('handles NaN cases', () => { const column = makeFilterColumn(); const columnFilter = column.filter(); - TableUtils.makeQuickNumberFilter(column, 'NAN'); + tableUtils.makeQuickNumberFilter(column, 'NAN'); expect(dh.FilterCondition.invoke).toBeCalledWith('isNaN', columnFilter); - TableUtils.makeQuickNumberFilter(column, 'NaN'); + tableUtils.makeQuickNumberFilter(column, 'NaN'); expect(dh.FilterCondition.invoke).toBeCalledWith('isNaN', columnFilter); - TableUtils.makeQuickNumberFilter(column, 'nan'); + tableUtils.makeQuickNumberFilter(column, 'nan'); expect(dh.FilterCondition.invoke).toBeCalledWith('isNaN', columnFilter); }); it('handles infinity cases', () => { const column = makeFilterColumn(); const columnFilter = column.filter(); - TableUtils.makeQuickNumberFilter(column, 'inf '); + tableUtils.makeQuickNumberFilter(column, 'inf '); expect(dh.FilterCondition.invoke).toBeCalledWith('isInf', columnFilter); expect(columnFilter.greaterThan).toHaveBeenCalled(); - TableUtils.makeQuickNumberFilter(column, ' infinity'); + tableUtils.makeQuickNumberFilter(column, ' infinity'); expect(dh.FilterCondition.invoke).toBeCalledWith('isInf', columnFilter); expect(columnFilter.greaterThan).toHaveBeenCalled(); - TableUtils.makeQuickNumberFilter(column, ' - infinity'); + tableUtils.makeQuickNumberFilter(column, ' - infinity'); expect(dh.FilterCondition.invoke).toBeCalledWith('isInf', columnFilter); expect(columnFilter.lessThan).toHaveBeenCalled(); - TableUtils.makeQuickNumberFilter(column, 'INFINITY'); + tableUtils.makeQuickNumberFilter(column, 'INFINITY'); expect(dh.FilterCondition.invoke).toBeCalledWith('isInf', columnFilter); expect(columnFilter.greaterThan).toHaveBeenCalled(); - TableUtils.makeQuickNumberFilter(column, '\u221E'); + tableUtils.makeQuickNumberFilter(column, '\u221E'); expect(dh.FilterCondition.invoke).toBeCalledWith('isInf', columnFilter); expect(columnFilter.greaterThan).toHaveBeenCalled(); - TableUtils.makeQuickNumberFilter(column, '- \u221E'); + tableUtils.makeQuickNumberFilter(column, '- \u221E'); expect(dh.FilterCondition.invoke).toBeCalledWith('isInf', columnFilter); expect(columnFilter.lessThan).toHaveBeenCalled(); }); @@ -1042,21 +1043,21 @@ describe('quick filter tests', () => { expectFilterCondition = mockFilterConditionReturnValue( dh.FilterCondition.invoke ); - TableUtils.makeQuickNumberFilter(column, '!NAN'); + tableUtils.makeQuickNumberFilter(column, '!NAN'); expect(dh.FilterCondition.invoke).toBeCalledWith('isNaN', columnFilter); expect(expectFilterCondition.not).toHaveBeenCalledTimes(1); expectFilterCondition = mockFilterConditionReturnValue( dh.FilterCondition.invoke ); - TableUtils.makeQuickNumberFilter(column, '!=nan'); + tableUtils.makeQuickNumberFilter(column, '!=nan'); expect(dh.FilterCondition.invoke).toBeCalledWith('isNaN', columnFilter); expect(expectFilterCondition.not).toHaveBeenCalledTimes(1); expectFilterCondition = mockFilterConditionReturnValue( dh.FilterCondition.invoke ); - TableUtils.makeQuickNumberFilter(column, '!= NaN'); + tableUtils.makeQuickNumberFilter(column, '!= NaN'); expect(dh.FilterCondition.invoke).toBeCalledWith('isNaN', columnFilter); expect(expectFilterCondition.not).toHaveBeenCalledTimes(1); @@ -1066,7 +1067,7 @@ describe('quick filter tests', () => { expectAndFilterCondition = mockFilterConditionReturnValue( expectFilterCondition.and ); - TableUtils.makeQuickNumberFilter(column, '!INFINITY'); + tableUtils.makeQuickNumberFilter(column, '!INFINITY'); expect(dh.FilterCondition.invoke).toBeCalledWith('isInf', columnFilter); expect(columnFilter.greaterThan).toHaveBeenCalled(); expect(expectFilterCondition.and).toHaveBeenCalledTimes(1); @@ -1078,7 +1079,7 @@ describe('quick filter tests', () => { expectAndFilterCondition = mockFilterConditionReturnValue( expectFilterCondition.and ); - TableUtils.makeQuickNumberFilter(column, '!= inf'); + tableUtils.makeQuickNumberFilter(column, '!= inf'); expect(dh.FilterCondition.invoke).toBeCalledWith('isInf', columnFilter); expect(columnFilter.greaterThan).toHaveBeenCalled(); expect(expectFilterCondition.and).toHaveBeenCalledTimes(1); @@ -1090,7 +1091,7 @@ describe('quick filter tests', () => { expectAndFilterCondition = mockFilterConditionReturnValue( expectFilterCondition.and ); - TableUtils.makeQuickNumberFilter(column, '!= - \u221E'); + tableUtils.makeQuickNumberFilter(column, '!= - \u221E'); expect(dh.FilterCondition.invoke).toBeCalledWith('isInf', columnFilter); expect(columnFilter.lessThan).toHaveBeenCalled(); expect(expectFilterCondition.and).toHaveBeenCalledTimes(1); @@ -1099,19 +1100,19 @@ describe('quick filter tests', () => { expectFilterCondition = mockFilterConditionReturnValue( columnFilter.isNull ); - TableUtils.makeQuickNumberFilter(column, '!null'); + tableUtils.makeQuickNumberFilter(column, '!null'); expect(expectFilterCondition.not).toHaveBeenCalledTimes(1); expectFilterCondition = mockFilterConditionReturnValue( columnFilter.isNull ); - TableUtils.makeQuickNumberFilter(column, '!= null'); + tableUtils.makeQuickNumberFilter(column, '!= null'); expect(expectFilterCondition.not).toHaveBeenCalledTimes(1); }); it('should return null if it is an abnormal value with unsupported operations', () => { const column = makeFilterColumn(); - expect(TableUtils.makeQuickNumberFilter(column, '>=NaN')).toBeNull(); + expect(tableUtils.makeQuickNumberFilter(column, '>=NaN')).toBeNull(); }); }); @@ -1133,22 +1134,22 @@ describe('quick filter tests', () => { it('handles empty cases', () => { const column = makeFilterColumn(); - expect(TableUtils.makeQuickBooleanFilter(column, '')).toBe(null); + expect(tableUtils.makeQuickBooleanFilter(column, '')).toBe(null); }); it('handles invalid cases', () => { const column = makeFilterColumn(); - expect(TableUtils.makeQuickBooleanFilter(column, 'U()$#@')).toBe(null); - expect(TableUtils.makeQuickBooleanFilter(column, 'invalid str')).toBe( + expect(tableUtils.makeQuickBooleanFilter(column, 'U()$#@')).toBe(null); + expect(tableUtils.makeQuickBooleanFilter(column, 'invalid str')).toBe( null ); - expect(TableUtils.makeQuickBooleanFilter(column, 'nu ll')).toBe(null); - expect(TableUtils.makeQuickBooleanFilter(column, 'truel')).toBe(null); - expect(TableUtils.makeQuickBooleanFilter(column, 'falsel')).toBe(null); - expect(TableUtils.makeQuickBooleanFilter(column, 'truefalse')).toBe(null); - expect(TableUtils.makeQuickBooleanFilter(column, 'falsetrue')).toBe(null); - expect(TableUtils.makeQuickBooleanFilter(column, 4)).toBe(null); + expect(tableUtils.makeQuickBooleanFilter(column, 'nu ll')).toBe(null); + expect(tableUtils.makeQuickBooleanFilter(column, 'truel')).toBe(null); + expect(tableUtils.makeQuickBooleanFilter(column, 'falsel')).toBe(null); + expect(tableUtils.makeQuickBooleanFilter(column, 'truefalse')).toBe(null); + expect(tableUtils.makeQuickBooleanFilter(column, 'falsetrue')).toBe(null); + expect(tableUtils.makeQuickBooleanFilter(column, 4)).toBe(null); }); it('handles true/false properly', () => { @@ -1239,31 +1240,31 @@ describe('quick filter tests', () => { it('handles invalid cases', () => { const column = makeFilterColumn(); - expect(() => TableUtils.makeQuickDateFilter(column, '', '')).toThrow(); + expect(() => tableUtils.makeQuickDateFilter(column, '', '')).toThrow(); - expect(() => TableUtils.makeQuickDateFilter(column, '>', '')).toThrow(); + expect(() => tableUtils.makeQuickDateFilter(column, '>', '')).toThrow(); expect(() => - TableUtils.makeQuickDateFilter(column, 'U()$#@', '') + tableUtils.makeQuickDateFilter(column, 'U()$#@', '') ).toThrow(); expect(() => - TableUtils.makeQuickDateFilter(column, 'invalid str', '') + tableUtils.makeQuickDateFilter(column, 'invalid str', '') ).toThrow(); expect(() => - TableUtils.makeQuickDateFilter(column, 'nu ll', '') + tableUtils.makeQuickDateFilter(column, 'nu ll', '') ).toThrow(); expect(() => - TableUtils.makeQuickDateFilter(column, '20193-02-02', '') + tableUtils.makeQuickDateFilter(column, '20193-02-02', '') ).toThrow(); expect(() => - TableUtils.makeQuickDateFilter(column, '302-111-303', '') + tableUtils.makeQuickDateFilter(column, '302-111-303', '') ).toThrow(); expect(() => - TableUtils.makeQuickDateFilter(column, (4 as unknown) as string, '') + tableUtils.makeQuickDateFilter(column, (4 as unknown) as string, '') ).toThrow(); // Missing time zone expect(() => - TableUtils.makeQuickDateFilter(column, '2021-10-19', '') + tableUtils.makeQuickDateFilter(column, '2021-10-19', '') ).toThrow(); }); @@ -1794,7 +1795,7 @@ describe('quick filter tests', () => { notResult.and.mockReturnValueOnce(expectedResult); - const result = TableUtils.makeQuickTextFilter(column, text); + const result = tableUtils.makeQuickTextFilter(column, text); expect(filter.isNull).toHaveBeenCalled(); expect(nullResult.not).toHaveBeenCalled(); @@ -1807,7 +1808,7 @@ describe('quick filter tests', () => { it('handles empty cases', () => { const column = makeFilterColumn(); - expect(TableUtils.makeQuickTextFilter(column, '')).toBe(null); + expect(tableUtils.makeQuickTextFilter(column, '')).toBe(null); }); it('handles null cases', () => { @@ -1835,7 +1836,7 @@ describe('quick filter tests', () => { const expectedNotFilter = makeFilterCondition(); expectedNullFilter.not.mockReturnValueOnce(expectedNotFilter); - const result = TableUtils.makeQuickTextFilter(column, '!null'); + const result = tableUtils.makeQuickTextFilter(column, '!null'); expect(filter.isNull).toHaveBeenCalled(); expect(expectedNullFilter.not).toHaveBeenCalled(); expect(result).toBe(expectedNotFilter); @@ -1922,7 +1923,7 @@ describe('quick filter tests', () => { it('throws an error if filter for andComponent is null', () => { const column = makeFilterColumn('char'); expect(() => - TableUtils.makeQuickFilter(column, '12a && 13 || 12') + tableUtils.makeQuickFilter(column, '12a && 13 || 12') ).toThrowError('Unable to parse quick filter from text 12a && 13 || 12'); }); }); @@ -1939,17 +1940,17 @@ describe('quick filter tests', () => { it('handles empty cases', () => { const column = makeFilterColumn(); - expect(TableUtils.makeQuickCharFilter(column, '')).toBe(null); + expect(tableUtils.makeQuickCharFilter(column, '')).toBe(null); }); it('handles invalid cases', () => { const column = makeFilterColumn(); - expect(TableUtils.makeQuickCharFilter(column, 'j*($%U#@(')).toBe(null); - expect(TableUtils.makeQuickCharFilter(column, '<=')).toBe(null); - expect(TableUtils.makeQuickCharFilter(column, '<=> c')).toBe(null); - expect(TableUtils.makeQuickCharFilter(column, '== c')).toBe(null); - expect(TableUtils.makeQuickCharFilter(column, 'c>')).toBe(null); + expect(tableUtils.makeQuickCharFilter(column, 'j*($%U#@(')).toBe(null); + expect(tableUtils.makeQuickCharFilter(column, '<=')).toBe(null); + expect(tableUtils.makeQuickCharFilter(column, '<=> c')).toBe(null); + expect(tableUtils.makeQuickCharFilter(column, '== c')).toBe(null); + expect(tableUtils.makeQuickCharFilter(column, 'c>')).toBe(null); }); it('handles default operation', () => { @@ -1995,7 +1996,7 @@ describe('quick filter tests', () => { filter.notEq.mockReturnValueOnce(notEqResult); eqResult.and.mockReturnValueOnce(expectedResult); - expect(TableUtils.makeSelectValueFilter(column, [], false)).toBe( + expect(tableUtils.makeSelectValueFilter(column, [], false)).toBe( expectedResult ); expect(filter.eq).toHaveBeenCalledWith(expectedValue); @@ -2006,7 +2007,7 @@ describe('quick filter tests', () => { it('should return null if there are no selected values and invertSelection is true', () => { const column = makeFilterColumn(); - expect(TableUtils.makeSelectValueFilter(column, [], true)).toBeNull(); + expect(tableUtils.makeSelectValueFilter(column, [], true)).toBeNull(); }); it('handles different column types when there are no selected values and invertSelection is false', () => { @@ -2033,7 +2034,7 @@ describe('quick filter tests', () => { isNullResult.not.mockReturnValueOnce(notResult); notResult.and.mockReturnValueOnce(expectedResult); expect( - TableUtils.makeSelectValueFilter(column, [null, 'string'], true) + tableUtils.makeSelectValueFilter(column, [null, 'string'], true) ).toBe(expectedResult); expect(filter.notIn).toHaveBeenCalledWith(['string']); expect(notResult.and).toHaveBeenCalledWith(notInResult); @@ -2051,7 +2052,7 @@ describe('quick filter tests', () => { filter.in.mockReturnValueOnce(inResult); isNullResult.or.mockReturnValueOnce(expectedResult); expect( - TableUtils.makeSelectValueFilter(column, [null, true, false], false) + tableUtils.makeSelectValueFilter(column, [null, true, false], false) ).toBe(expectedResult); expect(filter.in).toHaveBeenCalledWith([true, false]); expect(isNullResult.or).toHaveBeenCalledWith(inResult); @@ -2067,7 +2068,7 @@ describe('quick filter tests', () => { filter.isNull.mockReturnValueOnce(isNullResult); isNullResult.not.mockReturnValueOnce(expectedResult); expect( - TableUtils.makeSelectValueFilter(column, [null, null, null], true) + tableUtils.makeSelectValueFilter(column, [null, null, null], true) ).toBe(expectedResult); }); @@ -2079,7 +2080,7 @@ describe('quick filter tests', () => { filter.isNull.mockReturnValueOnce(isNullResult); expect( - TableUtils.makeSelectValueFilter(column, [null, null, null], false) + tableUtils.makeSelectValueFilter(column, [null, null, null], false) ).toBe(isNullResult); }); @@ -2090,7 +2091,7 @@ describe('quick filter tests', () => { const expectedResult = makeFilterCondition(); filter.notIn.mockReturnValueOnce(expectedResult); - expect(TableUtils.makeSelectValueFilter(column, [1, 2, 3], true)).toBe( + expect(tableUtils.makeSelectValueFilter(column, [1, 2, 3], true)).toBe( expectedResult ); expect(filter.notIn).toHaveBeenCalledWith([1, 2, 3]); @@ -2103,7 +2104,7 @@ describe('quick filter tests', () => { const expectedResult = makeFilterCondition(); filter.in.mockReturnValueOnce(expectedResult); - expect(TableUtils.makeSelectValueFilter(column, [1, 2, 3], false)).toBe( + expect(tableUtils.makeSelectValueFilter(column, [1, 2, 3], false)).toBe( expectedResult ); expect(filter.in).toHaveBeenCalledWith([1, 2, 3]); @@ -2393,7 +2394,7 @@ describe('makeValue', () => { expectedValue: unknown, timeZone = 'America/New_York' ) => { - expect(TableUtils.makeValue(columnType, text, timeZone)).toEqual( + expect(tableUtils.makeValue(columnType, text, timeZone)).toEqual( expectedValue ); }; @@ -2428,10 +2429,10 @@ describe('makeValue', () => { ), }; expect( - TableUtils.makeValue('long', 'test', 'America/New_York') + tableUtils.makeValue('long', 'test', 'America/New_York') ).toMatchObject(expectedLongWrapper); expect( - TableUtils.makeValue('java.lang.Long', 'test', 'America/New_York') + tableUtils.makeValue('java.lang.Long', 'test', 'America/New_York') ).toMatchObject(expectedLongWrapper); }); diff --git a/packages/jsapi-utils/src/TableUtils.ts b/packages/jsapi-utils/src/TableUtils.ts index 0c92356462..71e55d3c4d 100644 --- a/packages/jsapi-utils/src/TableUtils.ts +++ b/packages/jsapi-utils/src/TableUtils.ts @@ -5,7 +5,6 @@ import { OperatorValue as FilterOperatorValue, } from '@deephaven/filters'; import Log from '@deephaven/log'; -import dh from '@deephaven/jsapi-shim'; import { Column, CustomColumn, @@ -17,6 +16,7 @@ import { Table, TreeTable, } from '@deephaven/jsapi-types'; +import { dhType } from '@deephaven/jsapi-shim'; import { CancelablePromise, PromiseUtils, @@ -605,6 +605,238 @@ export class TableUtils { ); } + /** + * Adds quotes to a value if they're not already added + * @param value Value to add quotes around + */ + static quoteValue(value: string): string { + if ( + value.length >= 2 && + ((value.charAt(0) === '"' && value.charAt(value.length - 1) === '"') || + (value.charAt(0) === "'" && value.charAt(value.length - 1) === "'")) + ) { + return value; + } + return `"${value}"`; + } + + static isRangeOperation(operation: string): boolean { + switch (operation) { + case '<': + case '<=': + case '=<': + case '>': + case '>=': + case '=>': + return true; + default: + return false; + } + } + + /** + * @param filter The column filter to apply the range operation to + * @param operation The range operation to run + * @param value The value to use for the operation + * @returns The condition with the specified operation + */ + static makeRangeFilterWithOperation( + filter: FilterValue, + operation: string, + value: FilterValue + ): FilterCondition | null { + switch (operation) { + case '=': + return filter.eq(value); + case '<': + return filter.lessThan(value); + case '<=': + case '=<': + return filter.lessThanOrEqualTo(value); + case '>': + return filter.greaterThan(value); + case '>=': + case '=>': + return filter.greaterThanOrEqualTo(value); + case '!=': + case '!': + return filter.notEq(value); + default: + return null; + } + } + + /** + * Wraps a table promise in a cancelable promise that will close the table if the promise is cancelled. + * Use in a component that loads a table, and call cancel when unmounting. + * @param table The table promise to wrap + */ + static makeCancelableTablePromise( + table: Promise | Table + ): CancelablePromise
{ + return PromiseUtils.makeCancelable(table, resolved => { + resolved.close(); + }); + } + + /** + * Make a cancelable promise for a one-shot table event with a timeout. + * @param table Table to listen for events on + * @param eventName Event to listen for + * @param timeout Event timeout in milliseconds, defaults to 0 + * @param matcher Optional function to determine if the promise can be resolved or stays pending + * @returns Resolves with the event data + */ + static makeCancelableTableEventPromise( + table: Table | TreeTable, + eventName: string, + timeout = 0, + matcher: ((event: CustomEvent) => boolean) | null = null + ): CancelablePromise { + let eventCleanup: RemoverFn; + let timeoutId: ReturnType; + let isPending = true; + const wrappedPromise = new Promise((resolve, reject) => { + timeoutId = setTimeout(() => { + eventCleanup(); + isPending = false; + reject(new TimeoutError(`Event "${eventName}" timed out.`)); + }, timeout); + eventCleanup = table.addEventListener(eventName, event => { + if (matcher != null && !matcher(event)) { + log.debug2('Event triggered, but matcher returned false.'); + return; + } + log.debug2('Event triggered, resolving.'); + eventCleanup(); + clearTimeout(timeoutId); + isPending = false; + resolve(event); + }); + }) as CancelablePromise; + wrappedPromise.cancel = () => { + if (isPending) { + log.debug2('Pending promise cleanup.'); + eventCleanup(); + clearTimeout(timeoutId); + isPending = false; + return; + } + log.debug2('Ignoring non-pending promise cancel.'); + }; + return wrappedPromise; + } + + static removeCommas(value: string): string { + return value.replace(/[\s|,]/g, ''); + } + + static makeBooleanValue(text: string, allowEmpty = false): boolean | null { + if (text === '' && allowEmpty) { + return null; + } + + switch (text?.toLowerCase()) { + case 'null': + return null; + case '0': + case 'f': + case 'fa': + case 'fal': + case 'fals': + case 'false': + case 'n': + case 'no': + return false; + case '1': + case 't': + case 'tr': + case 'tru': + case 'true': + case 'y': + case 'ye': + case 'yes': + return true; + default: + throw new Error(`Invalid boolean '${text}'`); + } + } + + static makeNumberValue(text: string): number | null { + if (text === 'null' || text === '') { + return null; + } + + const cleanText = text.toLowerCase().trim(); + if (cleanText === '∞' || cleanText === 'infinity' || cleanText === 'inf') { + return Number.POSITIVE_INFINITY; + } + if ( + cleanText === '-∞' || + cleanText === '-infinity' || + cleanText === '-inf' + ) { + return Number.NEGATIVE_INFINITY; + } + + const numberText = TableUtils.removeCommas(cleanText); + if (TableUtils.NUMBER_REGEX.test(numberText)) { + return parseFloat(numberText); + } + + throw new Error(`Invalid number '${text}'`); + } + + static getFilterOperatorString(operation: FilterTypeValue): string { + switch (operation) { + case FilterType.eq: + return '='; + case FilterType.notEq: + return '!='; + case FilterType.greaterThan: + return '>'; + case FilterType.greaterThanOrEqualTo: + return '>='; + case FilterType.lessThan: + return '<'; + case FilterType.lessThanOrEqualTo: + return '<='; + case FilterType.contains: + return '~'; + case FilterType.notContains: + return '!~'; + default: + throw new Error(`Unexpected filter type ${operation}`); + } + } + + static isTreeTable(table: unknown): table is TreeTable { + return ( + table != null && + (table as TreeTable).expand !== undefined && + (table as TreeTable).collapse !== undefined + ); + } + + /** + * Copies the provided array, sorts by column name case insensitive, and returns the sorted array. + * @param columns The columns to sort + * @param isAscending Whether to sort ascending + */ + static sortColumns(columns: readonly Column[], isAscending = true): Column[] { + return [...columns].sort((a, b) => { + const aName = a.name.toUpperCase(); + const bName = b.name.toUpperCase(); + return TextUtils.sort(aName, bName, isAscending); + }); + } + + dh: dhType; + + constructor(dh: dhType) { + this.dh = dh; + } + /** * Create filter with the provided column and text. Handles multiple filters joined with && or || * @param column The column to set the filter on @@ -612,7 +844,7 @@ export class TableUtils { * @param timeZone The time zone to make this value in if it is a date type. E.g. America/New_York * @returns Returns the created filter, null if text could not be parsed */ - static makeQuickFilter( + makeQuickFilter( column: Column, text: string, timeZone?: string @@ -626,7 +858,7 @@ export class TableUtils { for (let j = 0; j < andComponents.length; j += 1) { const andComponent = andComponents[j].trim(); if (andComponent.length > 0) { - const filter = TableUtils.makeQuickFilterFromComponent( + const filter = this.makeQuickFilterFromComponent( column, andComponent, timeZone @@ -660,7 +892,7 @@ export class TableUtils { * @param timeZone The time zone to make this filter in if it is a date type. E.g. America/New_York * @returns Returns the created filter, null if text could not be parsed */ - static makeQuickFilterFromComponent( + makeQuickFilterFromComponent( column: Column, text: string, timeZone?: string @@ -681,11 +913,9 @@ export class TableUtils { return this.makeQuickTextFilter(column, text); } - static makeQuickNumberFilter( - column: Column, - text: string - ): FilterCondition | null { + makeQuickNumberFilter(column: Column, text: string): FilterCondition | null { const columnFilter = column.filter(); + const { dh } = this; let filter = null; const regex = /\s*(>=|<=|=>|=<|>|<|!=|=|!)?(\s*-\s*)?(\s*\d*(?:,\d{3})*(?:\.\d*)?\s*)?(null|nan|infinity|inf|\u221E)?(.*)/i; @@ -773,10 +1003,8 @@ export class TableUtils { return TableUtils.makeRangeFilterWithOperation(filter, operation, value); } - static makeQuickTextFilter( - column: Column, - text: string - ): FilterCondition | null { + makeQuickTextFilter(column: Column, text: string): FilterCondition | null { + const { dh } = this; const cleanText = `${text}`.trim(); const regex = /^(!~|!=|~|=|!)?(.*)/; const result = regex.exec(cleanText); @@ -914,7 +1142,8 @@ export class TableUtils { return null; } - static makeQuickBooleanFilter( + // eslint-disable-next-line class-methods-use-this + makeQuickBooleanFilter( column: Column, text: string | number ): FilterCondition | null { @@ -951,7 +1180,7 @@ export class TableUtils { * @param text The date string text to parse. * @param timeZone The time zone to make this filter in if it is a date type. E.g. America/New_York */ - static makeQuickDateFilter( + makeQuickDateFilter( column: Column, text: string, timeZone: string @@ -995,7 +1224,7 @@ export class TableUtils { break; } - return TableUtils.makeQuickDateFilterWithOperation( + return this.makeQuickDateFilterWithOperation( column, dateText, filterOperation, @@ -1010,13 +1239,14 @@ export class TableUtils { * @param operation The filter operation to use. * @param timeZone The time zone to make this filter with. E.g. America/New_York */ - static makeQuickDateFilterWithOperation( + makeQuickDateFilterWithOperation( column: Column, text: string, operation: FilterTypeValue, timeZone: string ): FilterCondition { - const [startDate, endDate] = DateUtils.parseDateRange(text, timeZone); + const { dh } = this; + const [startDate, endDate] = DateUtils.parseDateRange(dh, text, timeZone); const startValue = startDate != null ? dh.FilterValue.ofNumber(startDate) : null; @@ -1069,42 +1299,11 @@ export class TableUtils { } } - /** - * Adds quotes to a value if they're not already added - * @param value Value to add quotes around - */ - static quoteValue(value: string): string { - if ( - value.length >= 2 && - ((value.charAt(0) === '"' && value.charAt(value.length - 1) === '"') || - (value.charAt(0) === "'" && value.charAt(value.length - 1) === "'")) - ) { - return value; - } - return `"${value}"`; - } - - static isRangeOperation(operation: string): boolean { - switch (operation) { - case '<': - case '<=': - case '=<': - case '>': - case '>=': - case '=>': - return true; - default: - return false; - } - } - - static makeQuickCharFilter( - column: Column, - text: string - ): FilterCondition | null { - const cleanText = `${text}`.trim(); - const regex = /^(>=|<=|=>|=<|>|<|!=|=|!)?(null|"."|'.'|.)?(.*)/; - const result = regex.exec(cleanText); + makeQuickCharFilter(column: Column, text: string): FilterCondition | null { + const { dh } = this; + const cleanText = `${text}`.trim(); + const regex = /^(>=|<=|=>|=<|>|<|!=|=|!)?(null|"."|'.'|.)?(.*)/; + const result = regex.exec(cleanText); let operation = null; let value = null; @@ -1152,100 +1351,7 @@ export class TableUtils { ); } - /** - * @param filter The column filter to apply the range operation to - * @param operation The range operation to run - * @param value The value to use for the operation - * @returns The condition with the specified operation - */ - static makeRangeFilterWithOperation( - filter: FilterValue, - operation: string, - value: FilterValue - ): FilterCondition | null { - switch (operation) { - case '=': - return filter.eq(value); - case '<': - return filter.lessThan(value); - case '<=': - case '=<': - return filter.lessThanOrEqualTo(value); - case '>': - return filter.greaterThan(value); - case '>=': - case '=>': - return filter.greaterThanOrEqualTo(value); - case '!=': - case '!': - return filter.notEq(value); - default: - return null; - } - } - - /** - * Wraps a table promise in a cancelable promise that will close the table if the promise is cancelled. - * Use in a component that loads a table, and call cancel when unmounting. - * @param table The table promise to wrap - */ - static makeCancelableTablePromise( - table: Promise
| Table - ): CancelablePromise
{ - return PromiseUtils.makeCancelable(table, resolved => { - resolved.close(); - }); - } - - /** - * Make a cancelable promise for a one-shot table event with a timeout. - * @param table Table to listen for events on - * @param eventName Event to listen for - * @param timeout Event timeout in milliseconds, defaults to 0 - * @param matcher Optional function to determine if the promise can be resolved or stays pending - * @returns Resolves with the event data - */ - static makeCancelableTableEventPromise( - table: Table | TreeTable, - eventName: string, - timeout = 0, - matcher: ((event: CustomEvent) => boolean) | null = null - ): CancelablePromise { - let eventCleanup: RemoverFn; - let timeoutId: ReturnType; - let isPending = true; - const wrappedPromise = new Promise((resolve, reject) => { - timeoutId = setTimeout(() => { - eventCleanup(); - isPending = false; - reject(new TimeoutError(`Event "${eventName}" timed out.`)); - }, timeout); - eventCleanup = table.addEventListener(eventName, event => { - if (matcher != null && !matcher(event)) { - log.debug2('Event triggered, but matcher returned false.'); - return; - } - log.debug2('Event triggered, resolving.'); - eventCleanup(); - clearTimeout(timeoutId); - isPending = false; - resolve(event); - }); - }) as CancelablePromise; - wrappedPromise.cancel = () => { - if (isPending) { - log.debug2('Pending promise cleanup.'); - eventCleanup(); - clearTimeout(timeoutId); - isPending = false; - return; - } - log.debug2('Ignoring non-pending promise cancel.'); - }; - return wrappedPromise; - } - - static makeAdvancedFilter( + makeAdvancedFilter( column: Column, options: AdvancedFilterOptions, timeZone: string @@ -1267,7 +1373,7 @@ export class TableUtils { value.length > 0 ) { try { - const newFilter = TableUtils.makeAdvancedValueFilter( + const newFilter = this.makeAdvancedValueFilter( column, selectedType, value, @@ -1303,7 +1409,7 @@ export class TableUtils { } } - const selectValueFilter = TableUtils.makeSelectValueFilter( + const selectValueFilter = this.makeSelectValueFilter( column, selectedValues, invertSelection @@ -1319,174 +1425,14 @@ export class TableUtils { return filter; } - static removeCommas(value: string): string { - return value.replace(/[\s|,]/g, ''); - } - - /** - * @param columnType The column type to make the filter value from. - * @param value The value to make the filter value from. - * @returns The FilterValue item for this column/value combination - */ - static makeFilterValue(columnType: string, value: string): FilterValue { - const type = TableUtils.getBaseType(columnType); - if (TableUtils.isTextType(type)) { - return dh.FilterValue.ofString(value); - } - if (TableUtils.isLongType(type)) { - return dh.FilterValue.ofNumber( - dh.LongWrapper.ofString(TableUtils.removeCommas(value)) - ); - } - - return dh.FilterValue.ofNumber(TableUtils.removeCommas(value)); - } - - /** - * Takes a value and converts it to an `dh.FilterValue` - * - * @param columnType The column type to make the filter value from. - * @param value The value to actually set - * @returns The FilterValue item for this column/value combination - */ - static makeFilterRawValue( - columnType: string, - rawValue: unknown - ): FilterValue { - if (TableUtils.isTextType(columnType)) { - return dh.FilterValue.ofString(rawValue); - } - - if (TableUtils.isBooleanType(columnType)) { - return dh.FilterValue.ofBoolean(rawValue); - } - - return dh.FilterValue.ofNumber(rawValue); - } - - /** - * Converts a string value to a value appropriate for the column - * @param columnType The column type to make the value for - * @param text The string value to make a type for - * @param timeZone The time zone to make this value in if it is a date type. E.g. America/New_York - */ - static makeValue( - columnType: string, - text: string, - timeZone: string - ): string | number | boolean | LongWrapper | null { - if (text === 'null') { - return null; - } - if (TableUtils.isTextType(columnType)) { - return text; - } - if (TableUtils.isLongType(columnType)) { - return dh.LongWrapper.ofString(TableUtils.removeCommas(text)); - } - if (TableUtils.isBooleanType(columnType)) { - return TableUtils.makeBooleanValue(text, true); - } - if (TableUtils.isDateType(columnType)) { - const [date] = DateUtils.parseDateRange(text, timeZone); - return date; - } - - if (TableUtils.isNumberType(columnType)) { - return TableUtils.makeNumberValue(text); - } - - log.error('Unexpected column type', columnType); - return null; - } - - static makeBooleanValue(text: string, allowEmpty = false): boolean | null { - if (text === '' && allowEmpty) { - return null; - } - - switch (text?.toLowerCase()) { - case 'null': - return null; - case '0': - case 'f': - case 'fa': - case 'fal': - case 'fals': - case 'false': - case 'n': - case 'no': - return false; - case '1': - case 't': - case 'tr': - case 'tru': - case 'true': - case 'y': - case 'ye': - case 'yes': - return true; - default: - throw new Error(`Invalid boolean '${text}'`); - } - } - - static makeNumberValue(text: string): number | null { - if (text === 'null' || text === '') { - return null; - } - - const cleanText = text.toLowerCase().trim(); - if (cleanText === '∞' || cleanText === 'infinity' || cleanText === 'inf') { - return Number.POSITIVE_INFINITY; - } - if ( - cleanText === '-∞' || - cleanText === '-infinity' || - cleanText === '-inf' - ) { - return Number.NEGATIVE_INFINITY; - } - - const numberText = TableUtils.removeCommas(cleanText); - if (TableUtils.NUMBER_REGEX.test(numberText)) { - return parseFloat(numberText); - } - - throw new Error(`Invalid number '${text}'`); - } - - static getFilterOperatorString(operation: FilterTypeValue): string { - switch (operation) { - case FilterType.eq: - return '='; - case FilterType.notEq: - return '!='; - case FilterType.greaterThan: - return '>'; - case FilterType.greaterThanOrEqualTo: - return '>='; - case FilterType.lessThan: - return '<'; - case FilterType.lessThanOrEqualTo: - return '<='; - case FilterType.contains: - return '~'; - case FilterType.notContains: - return '!~'; - default: - throw new Error(`Unexpected filter type ${operation}`); - } - } - - static makeAdvancedValueFilter( + makeAdvancedValueFilter( column: Column, operation: FilterTypeValue, value: string, timeZone: string ): FilterCondition | null { if (TableUtils.isDateType(column.type)) { - return TableUtils.makeQuickDateFilterWithOperation( + return this.makeQuickDateFilterWithOperation( column, value, operation, @@ -1498,13 +1444,13 @@ export class TableUtils { TableUtils.isNumberType(column.type) || TableUtils.isCharType(column.type) ) { - return TableUtils.makeQuickFilter( + return this.makeQuickFilter( column, `${TableUtils.getFilterOperatorString(operation)}${value}` ); } - const filterValue = TableUtils.makeFilterValue(column.type, value); + const filterValue = this.makeFilterValue(column.type, value); const filter = column.filter(); switch (operation) { case FilterType.eq: @@ -1604,6 +1550,83 @@ export class TableUtils { return eqFilter.and(notEqFilter); } + /** + * @param columnType The column type to make the filter value from. + * @param value The value to make the filter value from. + * @returns The FilterValue item for this column/value combination + */ + makeFilterValue(columnType: string, value: string): FilterValue { + const { dh } = this; + const type = TableUtils.getBaseType(columnType); + if (TableUtils.isTextType(type)) { + return dh.FilterValue.ofString(value); + } + if (TableUtils.isLongType(type)) { + return dh.FilterValue.ofNumber( + dh.LongWrapper.ofString(TableUtils.removeCommas(value)) + ); + } + + return dh.FilterValue.ofNumber(TableUtils.removeCommas(value)); + } + + /** + * Takes a value and converts it to an `dh.FilterValue` + * + * @param columnType The column type to make the filter value from. + * @param value The value to actually set + * @returns The FilterValue item for this column/value combination + */ + makeFilterRawValue(columnType: string, rawValue: unknown): FilterValue { + const { dh } = this; + if (TableUtils.isTextType(columnType)) { + return dh.FilterValue.ofString(rawValue); + } + + if (TableUtils.isBooleanType(columnType)) { + return dh.FilterValue.ofBoolean(rawValue); + } + + return dh.FilterValue.ofNumber(rawValue); + } + + /** + * Converts a string value to a value appropriate for the column + * @param columnType The column type to make the value for + * @param text The string value to make a type for + * @param timeZone The time zone to make this value in if it is a date type. E.g. America/New_York + */ + makeValue( + columnType: string, + text: string, + timeZone: string + ): string | number | boolean | LongWrapper | null { + const { dh } = this; + if (text === 'null') { + return null; + } + if (TableUtils.isTextType(columnType)) { + return text; + } + if (TableUtils.isLongType(columnType)) { + return dh.LongWrapper.ofString(TableUtils.removeCommas(text)); + } + if (TableUtils.isBooleanType(columnType)) { + return TableUtils.makeBooleanValue(text, true); + } + if (TableUtils.isDateType(columnType)) { + const [date] = DateUtils.parseDateRange(dh, text, timeZone); + return date; + } + + if (TableUtils.isNumberType(columnType)) { + return TableUtils.makeNumberValue(text); + } + + log.error('Unexpected column type', columnType); + return null; + } + /** * Create a filter using the selected items * Has a flag for invertSelection as we start from a "Select All" state and a user just deselects items. @@ -1613,11 +1636,12 @@ export class TableUtils { * @param invertSelection Invert the selection (eg. All items are selected, then you deselect items) * @returns Returns a `in` or `notIn` FilterCondition as necessary, or null if no filtering should be applied (everything selected) */ - static makeSelectValueFilter( + makeSelectValueFilter( column: Column, selectedValues: unknown[], invertSelection: TInvert ): TInvert extends true ? FilterCondition | null : FilterCondition { + const { dh } = this; if (selectedValues.length === 0) { if (invertSelection) { // No filter means select everything @@ -1675,27 +1699,6 @@ export class TableUtils { return column.filter().in(values); } - - static isTreeTable(table: unknown): table is TreeTable { - return ( - table != null && - (table as TreeTable).expand !== undefined && - (table as TreeTable).collapse !== undefined - ); - } - - /** - * Copies the provided array, sorts by column name case insensitive, and returns the sorted array. - * @param columns The columns to sort - * @param isAscending Whether to sort ascending - */ - static sortColumns(columns: readonly Column[], isAscending = true): Column[] { - return [...columns].sort((a, b) => { - const aName = a.name.toUpperCase(); - const bName = b.name.toUpperCase(); - return TextUtils.sort(aName, bName, isAscending); - }); - } } export default TableUtils; From ae1cf73bc96d1c33d41404df2ddd21086197e8a5 Mon Sep 17 00:00:00 2001 From: Vlad Babich Date: Wed, 3 May 2023 13:03:52 -0600 Subject: [PATCH 02/16] WIP --- packages/code-studio/src/main/WidgetUtils.ts | 13 ++++++++++--- .../dashboard-core-plugins/src/PandasPlugin.tsx | 6 +++++- packages/jsapi-utils/src/DateUtils.ts | 15 ++++++++++++--- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/code-studio/src/main/WidgetUtils.ts b/packages/code-studio/src/main/WidgetUtils.ts index 6d803734c1..3bf4868891 100644 --- a/packages/code-studio/src/main/WidgetUtils.ts +++ b/packages/code-studio/src/main/WidgetUtils.ts @@ -1,8 +1,9 @@ import { ChartModel, ChartModelFactory } from '@deephaven/chart'; -import dh, { +import { Table, VariableTypeUnion, IdeConnection, + dhType, } from '@deephaven/jsapi-shim'; import { IrisGridModel, @@ -15,12 +16,14 @@ import { GLChartPanelState, isChartPanelTableMetadata, } from '@deephaven/dashboard-core-plugins'; +import { TableUtils } from '@deephaven/jsapi-utils'; export type GridPanelMetadata = { table: string; }; export const createChartModel = async ( + dh: dhType, connection: IdeConnection, metadata: ChartPanelMetadata, panelState?: GLChartPanelState @@ -76,10 +79,12 @@ export const createChartModel = async ( type: dh.VariableType.TABLE, }; const table = await connection.getObject(definition); - + const tableUtils = new TableUtils(dh); IrisGridUtils.applyTableSettings( + dh, table, tableSettings, + tableUtils, getTimeZone(store.getState()) ); @@ -88,6 +93,7 @@ export const createChartModel = async ( }; export const createGridModel = async ( + dh: dhType, connection: IdeConnection, metadata: GridPanelMetadata, type: VariableTypeUnion = dh.VariableType.TABLE @@ -95,7 +101,8 @@ export const createGridModel = async ( const { table: tableName } = metadata; const definition = { title: tableName, name: tableName, type }; const table = (await connection.getObject(definition)) as Table; - return IrisGridModelFactory.makeModel(table); + const tableUtils = new TableUtils(dh); + return IrisGridModelFactory.makeModel(table, tableUtils); }; export default { createChartModel, createGridModel }; diff --git a/packages/dashboard-core-plugins/src/PandasPlugin.tsx b/packages/dashboard-core-plugins/src/PandasPlugin.tsx index 00e5178aee..c2628d5056 100644 --- a/packages/dashboard-core-plugins/src/PandasPlugin.tsx +++ b/packages/dashboard-core-plugins/src/PandasPlugin.tsx @@ -11,6 +11,7 @@ import { IrisGridModelFactory } from '@deephaven/iris-grid'; import { Table } from '@deephaven/jsapi-shim'; import shortid from 'shortid'; import { PandasPanel, PandasPanelProps } from './panels'; +import { TableUtils } from '@deephaven/jsapi-utils'; export type PandasPluginProps = Partial & { hydrate: PanelHydrateFunction; @@ -28,8 +29,11 @@ export function PandasPlugin(props: PandasPluginProps): JSX.Element | null { } const metadata = { name, table: name }; + const tableUtils = new TableUtils(dh); const makeModel = () => - fetch().then((table: Table) => IrisGridModelFactory.makeModel(table)); + fetch().then((table: Table) => + IrisGridModelFactory.makeModel(table, tableUtils) + ); const config = { type: 'react-component' as const, component: PandasPanel.COMPONENT, diff --git a/packages/jsapi-utils/src/DateUtils.ts b/packages/jsapi-utils/src/DateUtils.ts index b4f9c31c9d..eb6fea003e 100644 --- a/packages/jsapi-utils/src/DateUtils.ts +++ b/packages/jsapi-utils/src/DateUtils.ts @@ -1,5 +1,5 @@ -import dh from '@deephaven/jsapi-shim'; -import type { DateWrapper } from '@deephaven/jsapi-types'; +import { dhType } from '@deephaven/jsapi-shim'; +import type { DateWrapper } from '@deephaven/jsapi-shim'; interface DateParts { year: T; @@ -41,6 +41,7 @@ export class DateUtils { * @param ns The nanoseconds */ static makeDateWrapper( + dh: dhType, timeZone: string, year: number, month = 0, @@ -89,6 +90,7 @@ export class DateUtils { * @returns Returns the DateWrapper for the next date, or null if a full date was passed in */ static getNextDate( + dh: dhType, components: DateParts, values: DateParts, timeZone: string @@ -125,6 +127,7 @@ export class DateUtils { // Still need to add nanos after, and the overflow from that is already added to seconds above const jsDate = new Date(year, month, date, hours, minutes, seconds); return DateUtils.makeDateWrapper( + dh, timeZone, jsDate.getFullYear(), jsDate.getMonth(), @@ -254,6 +257,7 @@ export class DateUtils { * @returns A tuple with the start and end value/null for that date range, or both null */ static parseDateRange( + dh: dhType, text: string, timeZone: string ): [DateWrapper, DateWrapper | null] | [null, null] { @@ -269,12 +273,14 @@ export class DateUtils { if (cleanText === 'today') { const now = new Date(Date.now()); const startDate = DateUtils.makeDateWrapper( + dh, timeZone, now.getFullYear(), now.getMonth(), now.getDate() ); const endDate = DateUtils.makeDateWrapper( + dh, timeZone, now.getFullYear(), now.getMonth(), @@ -286,12 +292,14 @@ export class DateUtils { if (cleanText === 'yesterday') { const now = new Date(Date.now()); const startDate = DateUtils.makeDateWrapper( + dh, timeZone, now.getFullYear(), now.getMonth(), now.getDate() - 1 ); const endDate = DateUtils.makeDateWrapper( + dh, timeZone, now.getFullYear(), now.getMonth(), @@ -330,6 +338,7 @@ export class DateUtils { } const startDate = DateUtils.makeDateWrapper( + dh, timeZone, values.year, values.month, @@ -340,7 +349,7 @@ export class DateUtils { values.nanos ); - const endDate = DateUtils.getNextDate(components, values, timeZone); + const endDate = DateUtils.getNextDate(dh, components, values, timeZone); return [startDate, endDate]; } From d8d90749e38b9c9f7dd7a0f2b9047e89117b3ad6 Mon Sep 17 00:00:00 2001 From: Vlad Babich Date: Wed, 3 May 2023 14:40:45 -0600 Subject: [PATCH 03/16] Unit tests --- package-lock.json | 4 +- .../dashboard-core-plugins/src/GridPlugin.tsx | 2 +- packages/iris-grid/package.json | 3 +- .../src/AdvancedFilterCreator.test.tsx | 12 +- .../src/AdvancedFilterCreatorFilterItem.tsx | 2 +- .../src/AdvancedFilterCreatorSelectValue.tsx | 2 +- packages/iris-grid/src/IrisGrid.test.tsx | 4 +- .../src/IrisGridCopyHandler.test.tsx | 3 +- packages/iris-grid/src/IrisGridModel.test.ts | 12 +- packages/iris-grid/src/IrisGridProxyModel.ts | 2 +- packages/iris-grid/src/IrisGridTableModel.ts | 2 +- packages/iris-grid/src/IrisGridTestUtils.ts | 17 +- packages/iris-grid/src/IrisGridUtils.test.ts | 39 +- packages/iris-grid/src/IrisGridUtils.ts | 2356 ++++++++--------- .../src/sidebar/ChartBuilder.test.tsx | 2 + .../src/sidebar/CustomColumnBuilder.test.tsx | 9 +- .../src/sidebar/TableCsvExporter.test.tsx | 5 +- .../VisibilityOrderingBuilder.test.tsx | 7 +- .../VisibilityOrderingBuilderUtils.test.ts | 4 +- packages/jsapi-utils/src/DateUtils.test.ts | 19 +- packages/jsapi-utils/src/TableUtils.test.ts | 8 +- 21 files changed, 1254 insertions(+), 1260 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2cf9a79130..c617a16f0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26654,7 +26654,7 @@ "@deephaven/filters": "file:../filters", "@deephaven/grid": "file:../grid", "@deephaven/icons": "file:../icons", - "@deephaven/jsapi-shim": "file:../jsapi-shim", + "@deephaven/jsapi-types": "file:../jsapi-types", "@deephaven/jsapi-utils": "file:../jsapi-utils", "@deephaven/log": "file:../log", "@deephaven/react-hooks": "file:../react-hooks", @@ -26678,6 +26678,7 @@ "shortid": "^2.2.16" }, "devDependencies": { + "@deephaven/jsapi-shim": "file:../jsapi-shim", "@deephaven/mocks": "file:../mocks", "@deephaven/tsconfig": "file:../tsconfig" }, @@ -28701,6 +28702,7 @@ "@deephaven/grid": "file:../grid", "@deephaven/icons": "file:../icons", "@deephaven/jsapi-shim": "file:../jsapi-shim", + "@deephaven/jsapi-types": "file:../jsapi-types", "@deephaven/jsapi-utils": "file:../jsapi-utils", "@deephaven/log": "file:../log", "@deephaven/mocks": "file:../mocks", diff --git a/packages/dashboard-core-plugins/src/GridPlugin.tsx b/packages/dashboard-core-plugins/src/GridPlugin.tsx index 687df940ae..77a40c5948 100644 --- a/packages/dashboard-core-plugins/src/GridPlugin.tsx +++ b/packages/dashboard-core-plugins/src/GridPlugin.tsx @@ -9,9 +9,9 @@ import { } from '@deephaven/dashboard'; import { IrisGridModelFactory, IrisGridThemeType } from '@deephaven/iris-grid'; import { Table, VariableDefinition } from '@deephaven/jsapi-shim'; +import { TableUtils } from '@deephaven/jsapi-utils'; import shortid from 'shortid'; import { IrisGridPanel, IrisGridPanelProps } from './panels'; -import { TableUtils } from '@deephaven/jsapi-utils'; const SUPPORTED_TYPES: string[] = [ dh.VariableType.TABLE, diff --git a/packages/iris-grid/package.json b/packages/iris-grid/package.json index a58bb87e7b..2cfa5ab3bd 100644 --- a/packages/iris-grid/package.json +++ b/packages/iris-grid/package.json @@ -28,7 +28,7 @@ "@deephaven/filters": "file:../filters", "@deephaven/grid": "file:../grid", "@deephaven/icons": "file:../icons", - "@deephaven/jsapi-shim": "file:../jsapi-shim", + "@deephaven/jsapi-types": "file:../jsapi-types", "@deephaven/jsapi-utils": "file:../jsapi-utils", "@deephaven/log": "file:../log", "@deephaven/react-hooks": "file:../react-hooks", @@ -56,6 +56,7 @@ "react-dom": "^17.x" }, "devDependencies": { + "@deephaven/jsapi-shim": "file:../jsapi-shim", "@deephaven/mocks": "file:../mocks", "@deephaven/tsconfig": "file:../tsconfig" }, diff --git a/packages/iris-grid/src/AdvancedFilterCreator.test.tsx b/packages/iris-grid/src/AdvancedFilterCreator.test.tsx index 4d17519d66..2c62d37503 100644 --- a/packages/iris-grid/src/AdvancedFilterCreator.test.tsx +++ b/packages/iris-grid/src/AdvancedFilterCreator.test.tsx @@ -1,9 +1,9 @@ import React from 'react'; -import { Formatter } from '@deephaven/jsapi-utils'; +import { Formatter, TableUtils } from '@deephaven/jsapi-utils'; import { render } from '@testing-library/react'; -import { Column } from '@deephaven/jsapi-shim'; +import { Column } from '@deephaven/jsapi-types'; // import userEvent from '@testing-library/user-event'; -// import dh from '@deephaven/jsapi-shim'; +import dh from '@deephaven/jsapi-shim'; // import { // Type as FilterType, // Operator as FilterOperator, @@ -32,23 +32,26 @@ import IrisGridModel from './IrisGridModel'; function makeAdvancedFilterCreatorWrapper( { + tableUtils, options, model, column, formatter, }: { + tableUtils: TableUtils; options: AdvancedFilterOptions; model: IrisGridModel; column: Column; formatter: Formatter; } = { + tableUtils: new TableUtils(dh), options: { filterItems: [], filterOperators: [], invertSelection: false, selectedValues: [], }, - model: IrisGridTestUtils.makeModel(), + model: IrisGridTestUtils.makeModel(dh), column: IrisGridTestUtils.makeColumn(), formatter: new Formatter(), } @@ -62,6 +65,7 @@ function makeAdvancedFilterCreatorWrapper( onSortChange={() => null} onDone={() => null} options={options} + tableUtils={tableUtils} /> ); diff --git a/packages/iris-grid/src/AdvancedFilterCreatorFilterItem.tsx b/packages/iris-grid/src/AdvancedFilterCreatorFilterItem.tsx index f60070f6b1..d8bef808ed 100644 --- a/packages/iris-grid/src/AdvancedFilterCreatorFilterItem.tsx +++ b/packages/iris-grid/src/AdvancedFilterCreatorFilterItem.tsx @@ -9,7 +9,7 @@ import { TypeValue as FilterTypeValue, } from '@deephaven/filters'; import { vsTrash } from '@deephaven/icons'; -import { Column } from '@deephaven/jsapi-shim'; +import { Column } from '@deephaven/jsapi-types'; import { AdvancedFilterItemType, Formatter, diff --git a/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx b/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx index fc6a63cd73..5d4e9ddeda 100644 --- a/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx +++ b/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx @@ -3,7 +3,7 @@ import React, { PureComponent } from 'react'; import { CSSTransition } from 'react-transition-group'; import classNames from 'classnames'; import { Formatter, TableUtils } from '@deephaven/jsapi-utils'; -import { FilterCondition, Table } from '@deephaven/jsapi-shim'; +import { FilterCondition, Table } from '@deephaven/jsapi-types'; import { Button } from '@deephaven/components'; import AdvancedFilterCreatorSelectValueList from './AdvancedFilterCreatorSelectValueList'; import './AdvancedFilterCreatorSelectValue.scss'; diff --git a/packages/iris-grid/src/IrisGrid.test.tsx b/packages/iris-grid/src/IrisGrid.test.tsx index fbb5ed45aa..e87168e7f2 100644 --- a/packages/iris-grid/src/IrisGrid.test.tsx +++ b/packages/iris-grid/src/IrisGrid.test.tsx @@ -1,5 +1,6 @@ import React from 'react'; import TestRenderer from 'react-test-renderer'; +import dh from '@deephaven/jsapi-shim'; import { DateUtils, Settings } from '@deephaven/jsapi-utils'; import { TestUtils } from '@deephaven/utils'; import { IrisGrid } from './IrisGrid'; @@ -52,7 +53,7 @@ function createNodeMock(element) { } function makeComponent( - model = IrisGridTestUtils.makeModel(), + model = IrisGridTestUtils.makeModel(dh), settings = DEFAULT_SETTINGS ) { const testRenderer = TestRenderer.create( @@ -198,6 +199,7 @@ it('handles undefined operator, should default to eq', () => { it('should set gotoValueSelectedColumnName to empty string if no columns are given', () => { const component = makeComponent( IrisGridTestUtils.makeModel( + dh, IrisGridTestUtils.makeTable({ columns: [], }) diff --git a/packages/iris-grid/src/IrisGridCopyHandler.test.tsx b/packages/iris-grid/src/IrisGridCopyHandler.test.tsx index 789901b496..9235857d74 100644 --- a/packages/iris-grid/src/IrisGridCopyHandler.test.tsx +++ b/packages/iris-grid/src/IrisGridCopyHandler.test.tsx @@ -3,6 +3,7 @@ import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { GridTestUtils } from '@deephaven/grid'; import { copyToClipboard } from '@deephaven/utils'; +import dh from '@deephaven/jsapi-shim'; import IrisGridTestUtils from './IrisGridTestUtils'; import IrisGridCopyHandler, { CopyOperation } from './IrisGridCopyHandler'; @@ -41,7 +42,7 @@ function makeCopyOperation( } function makeModel() { - const model = IrisGridTestUtils.makeModel(); + const model = IrisGridTestUtils.makeModel(dh); model.textSnapshot = makeSnapshotFn(); return model; } diff --git a/packages/iris-grid/src/IrisGridModel.test.ts b/packages/iris-grid/src/IrisGridModel.test.ts index fd85f39404..2a51bdfcf7 100644 --- a/packages/iris-grid/src/IrisGridModel.test.ts +++ b/packages/iris-grid/src/IrisGridModel.test.ts @@ -5,7 +5,7 @@ import { TableViewportSubscription, TotalsTable, TreeTable, -} from '@deephaven/jsapi-shim'; +} from '@deephaven/jsapi-types'; import { Formatter } from '@deephaven/jsapi-utils'; import IrisGridModel from './IrisGridModel'; import IrisGridTestUtils from './IrisGridTestUtils'; @@ -27,7 +27,7 @@ describe('viewport and subscription tests', () => { table.applyCustomColumns = jest.fn(val => val as string[]); subscription.setViewport = jest.fn(); subscription.close = jest.fn(); - model = IrisGridTestUtils.makeModel(table); + model = IrisGridTestUtils.makeModel(dh, table); }); it('applies viewport to existing subscription', () => { @@ -133,7 +133,7 @@ it('updates the model correctly when adding and removing a rollup config', async ); table.rollup = mock; - const model = IrisGridTestUtils.makeModel(table); + const model = IrisGridTestUtils.makeModel(dh, table); expect(mock).not.toHaveBeenCalled(); @@ -151,7 +151,7 @@ it('updates the model correctly when adding and removing a rollup config', async it('closes the table correctly when the model is closed', () => { const table = IrisGridTestUtils.makeTable(); table.close = jest.fn(); - const model = IrisGridTestUtils.makeModel(table); + const model = IrisGridTestUtils.makeModel(dh, table); model.close(); @@ -177,7 +177,7 @@ describe('totals table tests', () => { table.getTotalsTable = jest.fn(() => Promise.resolve(totalsTable)); totalsTable.close = jest.fn(); - model = IrisGridTestUtils.makeModel(table); + model = IrisGridTestUtils.makeModel(dh, table); }); it('opens a totals table correctly and closes it when done', async () => { @@ -228,7 +228,7 @@ describe('pending new rows tests', () => { inputTable = IrisGridTestUtils.makeInputTable(table.columns.slice(0, 3)); - model = IrisGridTestUtils.makeModel(table, new Formatter(), inputTable); + model = IrisGridTestUtils.makeModel(dh, table, new Formatter(), inputTable); model.pendingRowCount = PENDING_ROW_COUNT; }); diff --git a/packages/iris-grid/src/IrisGridProxyModel.ts b/packages/iris-grid/src/IrisGridProxyModel.ts index e25b753f61..d8331108e5 100644 --- a/packages/iris-grid/src/IrisGridProxyModel.ts +++ b/packages/iris-grid/src/IrisGridProxyModel.ts @@ -19,7 +19,7 @@ import { Table, TreeTable, ValueTypeUnion, -} from '@deephaven/jsapi-shim'; +} from '@deephaven/jsapi-types'; import { EditableGridModel, isEditableGridModel, diff --git a/packages/iris-grid/src/IrisGridTableModel.ts b/packages/iris-grid/src/IrisGridTableModel.ts index 906c4b9706..afcc466182 100644 --- a/packages/iris-grid/src/IrisGridTableModel.ts +++ b/packages/iris-grid/src/IrisGridTableModel.ts @@ -9,7 +9,7 @@ import { LayoutHints, Table, ValueTypeUnion, -} from '@deephaven/jsapi-shim'; +} from '@deephaven/jsapi-types'; import Log from '@deephaven/log'; import { Formatter, TableUtils } from '@deephaven/jsapi-utils'; import { diff --git a/packages/iris-grid/src/IrisGridTestUtils.ts b/packages/iris-grid/src/IrisGridTestUtils.ts index 8550151dfb..bf247c1beb 100644 --- a/packages/iris-grid/src/IrisGridTestUtils.ts +++ b/packages/iris-grid/src/IrisGridTestUtils.ts @@ -1,21 +1,20 @@ import { GridRangeIndex, ModelSizeMap } from '@deephaven/grid'; -import dh, { +import { Column, + dh as DhType, FilterCondition, InputTable, + LayoutHints, RollupConfig, Row, Sort, Table, TableViewportSubscription, TreeTable, -} from '@deephaven/jsapi-shim'; +} from '@deephaven/jsapi-types'; import { Formatter, TableUtils } from '@deephaven/jsapi-utils'; -import type { LayoutHints } from '@deephaven/jsapi-shim'; import IrisGridProxyModel from './IrisGridProxyModel'; -const tableUtils = new TableUtils(dh); - class IrisGridTestUtils { static DEFAULT_TYPE = 'java.lang.String'; @@ -122,11 +121,17 @@ class IrisGridTestUtils { } static makeModel( + dh: DhType, table = IrisGridTestUtils.makeTable(), formatter = new Formatter(), inputTable: InputTable | null = null ): IrisGridProxyModel { - return new IrisGridProxyModel(table, tableUtils, formatter, inputTable); + return new IrisGridProxyModel( + table, + new TableUtils(dh), + formatter, + inputTable + ); } } diff --git a/packages/iris-grid/src/IrisGridUtils.test.ts b/packages/iris-grid/src/IrisGridUtils.test.ts index 3c47d1d77e..533b463586 100644 --- a/packages/iris-grid/src/IrisGridUtils.test.ts +++ b/packages/iris-grid/src/IrisGridUtils.test.ts @@ -1,7 +1,8 @@ import { GridUtils, GridRange, MoveOperation } from '@deephaven/grid'; -import dh, { Column, Table, Sort } from '@deephaven/jsapi-shim'; +import dh from '@deephaven/jsapi-shim'; +import { Column, Table, Sort } from '@deephaven/jsapi-types'; import { TypeValue as FilterTypeValue } from '@deephaven/filters'; -import { DateUtils, TableUtils } from '@deephaven/jsapi-utils'; +import { DateUtils } from '@deephaven/jsapi-utils'; import type { AdvancedFilter } from './CommonTypes'; import { FilterData } from './IrisGrid'; import IrisGridTestUtils from './IrisGridTestUtils'; @@ -10,7 +11,7 @@ import IrisGridUtils, { LegacyDehydratedSort, } from './IrisGridUtils'; -const tableUtils = new TableUtils(dh); +const irisGridUtils = new IrisGridUtils(dh); function makeFilter() { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -51,10 +52,9 @@ describe('quickfilters tests', () => { const exportedFilters = IrisGridUtils.dehydrateQuickFilters(filters); expect(exportedFilters).toEqual([]); - const importedFilters = IrisGridUtils.hydrateQuickFilters( + const importedFilters = irisGridUtils.hydrateQuickFilters( table.columns, - exportedFilters, - tableUtils + exportedFilters ); expect(importedFilters).toEqual(filters); }); @@ -71,10 +71,9 @@ describe('quickfilters tests', () => { [column, expect.objectContaining({ text })], ]); - const importedFilters = IrisGridUtils.hydrateQuickFilters( + const importedFilters = irisGridUtils.hydrateQuickFilters( table.columns, - exportedFilters, - tableUtils + exportedFilters ); expect(importedFilters).toEqual( new Map([ @@ -94,16 +93,15 @@ describe('advanced filter tests', () => { it('exports/imports empty list', () => { const table = makeTable(); const filters = new Map(); - const exportedFilters = IrisGridUtils.dehydrateAdvancedFilters( + const exportedFilters = irisGridUtils.dehydrateAdvancedFilters( table.columns, filters ); expect(exportedFilters).toEqual([]); - const importedFilters = IrisGridUtils.hydrateAdvancedFilters( + const importedFilters = irisGridUtils.hydrateAdvancedFilters( table.columns, exportedFilters, - tableUtils, 'America/New_York' ); expect(importedFilters).toEqual(filters); @@ -121,7 +119,7 @@ describe('advanced filter tests', () => { }; const filters = new Map([[column, { filter, options }]]); - const exportedFilters = IrisGridUtils.dehydrateAdvancedFilters( + const exportedFilters = irisGridUtils.dehydrateAdvancedFilters( table.columns, filters as Map ); @@ -129,10 +127,9 @@ describe('advanced filter tests', () => { [column, expect.objectContaining({ options })], ]); - const importedFilters = IrisGridUtils.hydrateAdvancedFilters( + const importedFilters = irisGridUtils.hydrateAdvancedFilters( table.columns, exportedFilters, - tableUtils, 'America/New_York' ); expect(importedFilters).toEqual( @@ -156,7 +153,7 @@ describe('sort exporting/importing', () => { const exportedSort = IrisGridUtils.dehydrateSort(sort); expect(exportedSort).toEqual([]); - const importedSort = IrisGridUtils.hydrateSort(table.columns, exportedSort); + const importedSort = irisGridUtils.hydrateSort(table.columns, exportedSort); expect(importedSort).toEqual(sort); }); @@ -190,7 +187,7 @@ describe('sort exporting/importing', () => { ['current', dehydratedSorts], ['legacy', legacyDehydratedSorts], ])('%s', (_label, sorts) => { - const importedSort = IrisGridUtils.hydrateSort(table.columns, sorts); + const importedSort = irisGridUtils.hydrateSort(table.columns, sorts); expect(importedSort).toEqual([ expect.objectContaining({ @@ -212,13 +209,13 @@ describe('pendingDataMap hydration/dehydration', () => { it('dehydrates/hydrates empty map', () => { const pendingDataMap = new Map(); const columns = makeColumns(); - const dehydratedMap = IrisGridUtils.dehydratePendingDataMap( + const dehydratedMap = irisGridUtils.dehydratePendingDataMap( columns, pendingDataMap ); expect(dehydratedMap).toEqual([]); - const hydratedMap = IrisGridUtils.hydratePendingDataMap( + const hydratedMap = irisGridUtils.hydratePendingDataMap( columns, dehydratedMap ); @@ -244,7 +241,7 @@ describe('pendingDataMap hydration/dehydration', () => { ], ]); const columns = makeColumns(); - const dehydratedMap = IrisGridUtils.dehydratePendingDataMap( + const dehydratedMap = irisGridUtils.dehydratePendingDataMap( columns, pendingDataMap ); @@ -266,7 +263,7 @@ describe('pendingDataMap hydration/dehydration', () => { ], ]); - const hydratedMap = IrisGridUtils.hydratePendingDataMap( + const hydratedMap = irisGridUtils.hydratePendingDataMap( columns, dehydratedMap ); diff --git a/packages/iris-grid/src/IrisGridUtils.ts b/packages/iris-grid/src/IrisGridUtils.ts index 7ad597de11..3c5975e764 100644 --- a/packages/iris-grid/src/IrisGridUtils.ts +++ b/packages/iris-grid/src/IrisGridUtils.ts @@ -11,7 +11,7 @@ import { Column, ColumnGroup, DateWrapper, - dhType, + dh as DhType, FilterCondition, LongWrapper, RangeSet, @@ -19,7 +19,7 @@ import { Sort, Table, TableData, -} from '@deephaven/jsapi-shim'; +} from '@deephaven/jsapi-types'; import { DateUtils, TableUtils, @@ -273,181 +273,6 @@ class IrisGridUtils { }; } - /** - * Exports the state from IrisGrid to a JSON stringifiable object - * @param model The table model to export the state for - * @param irisGridState The current state of the IrisGrid - */ - static dehydrateIrisGridState( - dh: dhType, - model: IrisGridModel, - irisGridState: HydratedIrisGridState - ): DehydratedIrisGridState { - const { - aggregationSettings = { aggregations: EMPTY_ARRAY, showOnTop: false }, - advancedFilters, - customColumnFormatMap, - isFilterBarShown, - metrics, - quickFilters, - customColumns, - conditionalFormats = EMPTY_ARRAY, - reverseType, - rollupConfig = undefined, - showSearchBar, - searchValue, - selectDistinctColumns = EMPTY_ARRAY, - selectedSearchColumns, - sorts, - invertSearchColumns, - pendingDataMap = EMPTY_MAP, - frozenColumns, - columnHeaderGroups, - } = irisGridState; - assertNotNull(metrics); - const { userColumnWidths, userRowHeights } = metrics; - const { columns } = model; - return { - advancedFilters: IrisGridUtils.dehydrateAdvancedFilters( - dh, - columns, - advancedFilters - ), - aggregationSettings, - customColumnFormatMap: [...customColumnFormatMap], - isFilterBarShown, - quickFilters: IrisGridUtils.dehydrateQuickFilters(quickFilters), - sorts: IrisGridUtils.dehydrateSort(sorts), - userColumnWidths: [...userColumnWidths] - .filter( - ([columnIndex]) => - columnIndex != null && - columnIndex >= 0 && - columnIndex < columns.length - ) - .map(([columnIndex, width]) => [columns[columnIndex].name, width]), - userRowHeights: [...userRowHeights], - customColumns: [...customColumns], - conditionalFormats: [...conditionalFormats], - reverseType, - rollupConfig, - showSearchBar, - searchValue, - selectDistinctColumns: [...selectDistinctColumns], - selectedSearchColumns, - invertSearchColumns, - pendingDataMap: IrisGridUtils.dehydratePendingDataMap( - dh, - columns, - pendingDataMap - ), - frozenColumns, - columnHeaderGroups: columnHeaderGroups?.map(item => ({ - name: item.name, - children: item.children, - color: item.color, - })), - }; - } - - /** - * Import a state for IrisGrid that was exported with {{@link dehydrateIrisGridState}} - * @param model The table model to import the state with - * @param irisGridState The saved IrisGrid state - */ - static hydrateIrisGridState( - dh: dhType, - model: IrisGridModel, - irisGridState: DehydratedIrisGridState, - tableUtils: TableUtils - ): Omit & { - userColumnWidths: ModelSizeMap; - userRowHeights: ModelSizeMap; - } { - const { - advancedFilters, - aggregationSettings = { aggregations: [], showOnTop: false }, - customColumnFormatMap, - isFilterBarShown, - quickFilters, - sorts, - customColumns, - conditionalFormats, - userColumnWidths, - userRowHeights, - reverseType, - rollupConfig = undefined, - showSearchBar, - searchValue, - selectDistinctColumns, - selectedSearchColumns, - invertSearchColumns = true, - pendingDataMap = [], - frozenColumns, - columnHeaderGroups, - } = irisGridState; - const { columns, formatter } = model; - - return { - advancedFilters: IrisGridUtils.hydrateAdvancedFilters( - dh, - columns, - advancedFilters, - tableUtils, - formatter.timeZone - ), - aggregationSettings, - customColumnFormatMap: new Map(customColumnFormatMap), - isFilterBarShown, - quickFilters: IrisGridUtils.hydrateQuickFilters( - columns, - quickFilters, - tableUtils, - formatter.timeZone - ), - sorts: IrisGridUtils.hydrateSort(dh, columns, sorts), - userColumnWidths: new Map( - userColumnWidths - .map(([column, width]: [string | number, number]): [ - number, - number - ] => { - if ( - typeof column === 'string' || - (column as unknown) instanceof String - ) { - return [columns.findIndex(({ name }) => name === column), width]; - } - return [column, width]; - }) - .filter( - ([column]) => - column != null && column >= 0 && column < columns.length - ) - ), - customColumns, - conditionalFormats, - userRowHeights: new Map(userRowHeights), - reverseType, - rollupConfig, - showSearchBar, - searchValue, - selectDistinctColumns, - selectedSearchColumns, - invertSearchColumns, - pendingDataMap: IrisGridUtils.hydratePendingDataMap( - dh, - columns, - pendingDataMap - ) as PendingDataMap, - frozenColumns, - columnHeaderGroups: IrisGridUtils.parseColumnHeaderGroups( - model, - columnHeaderGroups ?? model.layoutHints?.columnGroups ?? [] - ).groups, - }; - } - /** * Export the IrisGridPanel state. * @param model The table model the state is being dehydrated with @@ -542,1209 +367,1350 @@ class IrisGridUtils { }); } - /** - * Import the saved quick filters to apply to the columns. Does not actually apply the filters. - * @param columns The columns the filters will be applied to - * @param savedQuickFilters Exported quick filters definitions - * @param timeZone The time zone to make this value in if it is a date type. E.g. America/New_York - * @returns The quick filters to apply to the columns - */ - static hydrateQuickFilters( - columns: readonly Column[], - savedQuickFilters: readonly DehydratedQuickFilter[], - tableUtils: TableUtils, - timeZone?: string - ): ReadonlyQuickFilterMap { - const importedFilters = savedQuickFilters.map( - ([columnIndex, quickFilter]: DehydratedQuickFilter): [ - number, - { text: string; filter: FilterCondition | null } - ] => { - const { text } = quickFilter; - - let filter = null; - try { - const column = IrisGridUtils.getColumn(columns, columnIndex); - if (column != null) { - filter = tableUtils.makeQuickFilter(column, text, timeZone); - } - } catch (error) { - log.error('hydrateQuickFilters error with', text, error); - } - - return [columnIndex, { text, filter }]; - } - ); - - return new Map(importedFilters); + static dehydrateLong(value: T): string | null { + return value != null ? `${value}` : null; } /** - * Export the advanced filters from the provided columns to JSON striginfiable object - * @param columns The columns for the filters - * @param advancedFilters The advanced filters to dehydrate - * @returns The dehydrated advanced filters + * Export the sorts from the provided table sorts to JSON stringifiable object + * @param sorts The table sorts + * @returns The dehydrated sorts */ - static dehydrateAdvancedFilters( - dh: dhType, - columns: readonly Column[], - advancedFilters: ReadonlyAdvancedFilterMap - ): DehydratedAdvancedFilter[] { - return [...advancedFilters].map(([columnIndex, advancedFilter]) => { - const column = IrisGridUtils.getColumn(columns, columnIndex); - assertNotNull(column); - const options = IrisGridUtils.dehydrateAdvancedFilterOptions( - dh, - column, - advancedFilter.options - ); - return [columnIndex, { options }]; + static dehydrateSort(sorts: readonly Sort[]): DehydratedSort[] { + return sorts.map(sort => { + const { column, isAbs, direction } = sort; + return { + column: column.name, + isAbs, + direction, + }; }); } /** - * Import the saved advanced filters to apply to the columns. Does not actually apply the filters. - * @param columns The columns the filters will be applied to - * @param savedAdvancedFilters Exported advanced filters definitions - * @param timeZone The time zone to make this filter in if it is a date type. E.g. America/New_York - * @returns The advanced filters to apply to the columns + * Pulls just the table settings from the panel state, eg. filters/sorts + * @param panelState The dehydrated panel state + * @returns A dehydrated table settings object, { partition, partitionColumn, advancedFilters, quickFilters, sorts } */ - static hydrateAdvancedFilters( - dh: dhType, + static extractTableSettings( + panelState: { + irisGridState: { advancedFilters: AF; quickFilters: QF; sorts: S }; + irisGridPanelState: { + partitionColumn?: ColumnName; + partition?: unknown; + }; + }, + inputFilters: InputFilter[] = [] + ): { + partitionColumn: ColumnName | undefined; + partition: unknown; + advancedFilters: AF; + inputFilters: InputFilter[]; + quickFilters: QF; + sorts: S; + } { + const { irisGridPanelState, irisGridState } = panelState; + const { partitionColumn, partition } = irisGridPanelState; + const { advancedFilters, quickFilters, sorts } = irisGridState; + + return { + advancedFilters, + inputFilters, + partition, + partitionColumn, + quickFilters, + sorts, + }; + } + + static getInputFiltersForColumns( columns: readonly Column[], - savedAdvancedFilters: readonly DehydratedAdvancedFilter[], - tableUtils: TableUtils, - timeZone: string - ): ReadonlyAdvancedFilterMap { - const importedFilters = savedAdvancedFilters.map( - ([columnIndex, advancedFilter]: DehydratedAdvancedFilter): [ - number, - { options: AdvancedFilterOptions; filter: FilterCondition | null } - ] => { - const column = IrisGridUtils.getColumn(columns, columnIndex); - assertNotNull(column); - const options = IrisGridUtils.hydrateAdvancedFilterOptions( - dh, - column, - advancedFilter.options - ); - let filter = null; + inputFilters: readonly InputFilter[] = [] + ): InputFilter[] { + return inputFilters.filter(({ name, type }) => + columns.find( + ({ name: columnName, type: columnType }) => + columnName === name && columnType === type + ) + ); + } - try { - const columnRetrieved = IrisGridUtils.getColumn(columns, columnIndex); - if (columnRetrieved != null) { - filter = tableUtils.makeAdvancedFilter(column, options, timeZone); - } - } catch (error) { - log.error('hydrateAdvancedFilters error with', options, error); - } + static getFiltersFromFilterMap( + filterMap: ReadonlyAdvancedFilterMap | ReadonlyQuickFilterMap + ): FilterCondition[] { + const filters = []; - return [columnIndex, { options, filter }]; + const keys = Array.from(filterMap.keys()); + for (let i = 0; i < keys.length; i += 1) { + const key = keys[i]; + const item = filterMap.get(key); + if (item?.filter != null) { + filters.push(item.filter); } - ); + } - return new Map(importedFilters); + return filters; } - static dehydrateAdvancedFilterOptions( - dh: dhType, - column: Column, - options: AdvancedFilterOptions - ): AdvancedFilterOptions { - const { selectedValues, ...otherOptions } = options; - return { - selectedValues: selectedValues?.map((value: unknown) => - IrisGridUtils.dehydrateValue(dh, value, column?.type) - ), - ...otherOptions, - }; + /** + * Get array of hidden column indexes + * @param userColumnWidths Map of user column widths + * @returns Array of hidden column indexes + */ + static getHiddenColumns(userColumnWidths: ModelSizeMap): ModelIndex[] { + return [...userColumnWidths.entries()] + .filter(([, value]) => value === 0) + .map(([key]) => key); } - static hydrateAdvancedFilterOptions( - dh: dhType, - column: Column, - options: AdvancedFilterOptions - ): AdvancedFilterOptions { - const { selectedValues, ...otherOptions } = options; - return { - selectedValues: selectedValues?.map(value => - IrisGridUtils.hydrateValue(dh, value, column?.type) - ), - ...otherOptions, - }; + static parseCustomColumnNames( + customColumns: readonly ColumnName[] + ): ColumnName[] { + return customColumns.map(customColumn => customColumn.split('=')[0]); } - static dehydratePendingDataMap( - dh: dhType, - columns: readonly Column[], - pendingDataMap: ReadonlyMap< - ModelIndex, - { - data: Map; - } - > - ): DehydratedPendingDataMap { - return [...pendingDataMap].map(([rowIndex, { data }]) => [ - rowIndex, - { - data: [...data].map(([c, value]) => [ - columns[c].name, - IrisGridUtils.dehydrateValue(dh, value, columns[c].type), - ]), - }, - ]); + static getRemovedCustomColumnNames( + oldCustomColumns: readonly ColumnName[], + customColumns: readonly ColumnName[] + ): ColumnName[] { + const oldCustomColumnsNames = IrisGridUtils.parseCustomColumnNames( + oldCustomColumns + ); + const customColumnNames = IrisGridUtils.parseCustomColumnNames( + customColumns + ); + return oldCustomColumnsNames.filter( + oldCustomColumnName => !customColumnNames.includes(oldCustomColumnName) + ); } - static hydratePendingDataMap( - dh: dhType, - columns: readonly Column[], - pendingDataMap: DehydratedPendingDataMap - ): Map< - number, - { data: Map } - > { - const columnMap = new Map(); - const getColumnIndex = (columnName: ColumnName) => { - if (!columnMap.has(columnName)) { - columnMap.set( - columnName, - columns.findIndex(({ name }) => name === columnName) - ); - } - return columnMap.get(columnName); - }; + static removeSortsInColumns( + sorts: readonly Sort[], + columnNames: readonly string[] + ): Sort[] { + return sorts.filter(sort => !columnNames.includes(sort.column.name)); + } - return new Map( - pendingDataMap.map( - ([rowIndex, { data }]: [ - number, - { data: [string, CellData | string | null][] } - ]) => [ - rowIndex, - { - data: new Map( - data.map(([columnName, value]) => { - const index = getColumnIndex(columnName); - assertNotNull(index); - return [ - getColumnIndex(columnName) ?? null, - IrisGridUtils.hydrateValue(dh, value, columns[index].type), - ]; - }) - ), - }, - ] - ) + static removeFiltersInColumns( + columns: readonly Column[], + filters: ReadonlyMap, + removedColumnNames: readonly ColumnName[] + ): Map { + const columnNames = columns.map(({ name }) => name); + const newFilter = new Map(filters); + removedColumnNames.forEach(columnName => + newFilter.delete(columnNames.indexOf(columnName)) ); + return newFilter; } - /** - * Dehydrates/serializes a value for storage. - * @param value The value to dehydrate - * @param columnType The column type - */ - static dehydrateValue( - dh: dhType, - value: T, - columnType: string - ): string | T | null { - if (TableUtils.isDateType(columnType)) { - return IrisGridUtils.dehydrateDateTime( - dh, - (value as unknown) as number | DateWrapper | Date + static removeColumnFromMovedColumns( + columns: readonly Column[], + movedColumns: readonly MoveOperation[], + removedColumnNames: readonly ColumnName[] + ): MoveOperation[] { + const columnNames = columns.map(({ name }) => name); + let newMoves = [...movedColumns]; + for (let i = 0; i < removedColumnNames.length; i += 1) { + const removedColumnName = removedColumnNames[i]; + let removedColumnIndex = columnNames.findIndex( + name => name === removedColumnName ); - } - - if (TableUtils.isLongType(columnType)) { - return IrisGridUtils.dehydrateLong(value); - } - - return value; - } + const moves: MoveOperation[] = []; + for (let j = 0; j < newMoves.length; j += 1) { + const move = newMoves[j]; + const newMove = { ...move }; + let [fromStart, fromEnd] = Array.isArray(move.from) + ? move.from + : [move.from, move.from]; - /** - * Hydrate a value from it's serialized state - * @param value The dehydrated value that needs to be hydrated - * @param columnType The type of column - */ - static hydrateValue( - dh: dhType, - value: T, - columnType: string - ): DateWrapper | LongWrapper | T | null { - if (TableUtils.isDateType(columnType)) { - return IrisGridUtils.hydrateDateTime(dh, (value as unknown) as string); - } + if (removedColumnIndex <= move.to) { + newMove.to -= 1; + } - if (TableUtils.isLongType(columnType)) { - return IrisGridUtils.hydrateLong(dh, (value as unknown) as string); - } + // If equal to fromStart, the new fromStart would stay the same + // It's just the next element in the range which will have the same index after deletion + if (removedColumnIndex < fromStart) { + fromStart -= 1; + } - return value; - } + if (removedColumnIndex <= fromEnd) { + fromEnd -= 1; + } - static dehydrateDateTime( - dh: dhType, - value: number | DateWrapper | Date - ): string | null { - return value != null - ? dh.i18n.DateTimeFormat.format(DateUtils.FULL_DATE_FORMAT, value) - : null; - } + if (fromStart <= fromEnd && fromStart !== newMove.to) { + if (fromStart === fromEnd) { + moves.push({ ...newMove, from: fromStart }); + } else { + moves.push({ ...newMove, from: [fromStart, fromEnd] }); + } + } - static hydrateDateTime(dh: dhType, value: string): DateWrapper | null { - return value != null - ? dh.i18n.DateTimeFormat.parse(DateUtils.FULL_DATE_FORMAT, value) - : null; - } + // get the next index of the removed column after the move is applied + // eslint-disable-next-line prefer-destructuring + removedColumnIndex = GridUtils.applyItemMoves( + removedColumnIndex, + removedColumnIndex, + [move] + )[0][0]; + } - static dehydrateLong(value: T): string | null { - return value != null ? `${value}` : null; + newMoves = moves; + columnNames.splice( + columnNames.findIndex(name => name === removedColumnName), + 1 + ); + } + return newMoves; } - static hydrateLong(dh: dhType, value: string): LongWrapper | null { - return value != null ? dh.LongWrapper.ofString(value) : null; + static removeColumnsFromSelectDistinctColumns( + selectDistinctColumns: readonly ColumnName[], + removedColumnNames: readonly ColumnName[] + ): ColumnName[] { + return selectDistinctColumns.filter( + columnName => !removedColumnNames.includes(columnName) + ); } - /** - * Export the sorts from the provided table sorts to JSON stringifiable object - * @param sorts The table sorts - * @returns The dehydrated sorts - */ - static dehydrateSort(sorts: readonly Sort[]): DehydratedSort[] { - return sorts.map(sort => { - const { column, isAbs, direction } = sort; - return { - column: column.name, - isAbs, - direction, - }; + static getVisibleColumnsInRange( + tableColumns: readonly Column[], + left: number, + right: number, + movedColumns: readonly MoveOperation[], + hiddenColumns: readonly number[] + ): Column[] { + const columns: Column[] = []; + for (let i = left; i <= right; i += 1) { + const modelIndex = GridUtils.getModelIndex(i, movedColumns); + if ( + modelIndex >= 0 && + modelIndex < tableColumns.length && + !hiddenColumns.includes(modelIndex) + ) { + columns.push(tableColumns[modelIndex]); + } + } + return columns; + } + + static getPrevVisibleColumns( + tableColumns: readonly Column[], + startIndex: VisibleIndex, + count: number, + movedColumns: readonly MoveOperation[], + hiddenColumns: readonly VisibleIndex[] + ): Column[] { + const columns = []; + let i = startIndex; + while (i >= 0 && columns.length < count) { + const modelIndex = GridUtils.getModelIndex(i, movedColumns); + if ( + modelIndex >= 0 && + modelIndex < tableColumns.length && + !hiddenColumns.includes(modelIndex) + ) { + columns.unshift(tableColumns[modelIndex]); + } + i -= 1; + } + return columns; + } + + static getNextVisibleColumns( + tableColumns: readonly Column[], + startIndex: VisibleIndex, + count: number, + movedColumns: readonly MoveOperation[], + hiddenColumns: readonly VisibleIndex[] + ): Column[] { + const columns = []; + let i = startIndex; + while (i < tableColumns.length && columns.length < count) { + const modelIndex = GridUtils.getModelIndex(i, movedColumns); + if ( + modelIndex >= 0 && + modelIndex < tableColumns.length && + !hiddenColumns.includes(modelIndex) + ) { + columns.push(tableColumns[modelIndex]); + } + i += 1; + } + return columns; + } + + static getColumnsToFetch( + tableColumns: readonly Column[], + viewportColumns: readonly Column[], + alwaysFetchColumnNames: readonly ColumnName[] + ): Column[] { + const columnsToFetch = [...viewportColumns]; + alwaysFetchColumnNames.forEach(columnName => { + const column = tableColumns.find(({ name }) => name === columnName); + if (column != null && !viewportColumns.includes(column)) { + columnsToFetch.push(column); + } }); + return columnsToFetch; } - /** - * Import the saved sorts to apply to the table. Does not actually apply the sort. - * @param columns The columns the sorts will be applied to - * @param sorts Exported sort definitions - * @returns The sorts to apply to the table - */ - static hydrateSort( - dh: dhType, + static getModelViewportColumns( columns: readonly Column[], - sorts: readonly (DehydratedSort | LegacyDehydratedSort)[] - ): Sort[] { - return ( - sorts - .map(sort => { - const { column: columnIndexOrName, isAbs, direction } = sort; - if (direction === TableUtils.sortDirection.reverse) { - return dh.Table.reverse(); - } + left: number | null, + right: number | null, + movedColumns: readonly MoveOperation[], + hiddenColumns: readonly VisibleIndex[] = [], + alwaysFetchColumnNames: readonly ColumnName[] = [], + bufferPages = 0 + ): Column[] | null { + if (left == null || right == null) { + return null; + } - const column = - typeof columnIndexOrName === 'string' - ? IrisGridUtils.getColumnByName(columns, columnIndexOrName) - : IrisGridUtils.getColumn(columns, columnIndexOrName); + const columnsCenter = IrisGridUtils.getVisibleColumnsInRange( + columns, + left, + right, + movedColumns, + hiddenColumns + ); + const bufferWidth = columnsCenter.length * bufferPages; + const columnsLeft = IrisGridUtils.getPrevVisibleColumns( + columns, + left - 1, + bufferWidth, + movedColumns, + hiddenColumns + ); + const columnsRight = IrisGridUtils.getNextVisibleColumns( + columns, + right + 1, + bufferWidth, + movedColumns, + hiddenColumns + ); - if (column != null) { - let columnSort = column.sort(); - if (isAbs) { - columnSort = columnSort.abs(); - } - if (direction === TableUtils.sortDirection.descending) { - columnSort = columnSort.desc(); - } else { - columnSort = columnSort.asc(); - } - return columnSort; - } + const bufferedColumns = [...columnsLeft, ...columnsCenter, ...columnsRight]; - return null; - }) - // If we can't find the column any more, it's null, filter it out - // If the item is a reverse sort item, filter it out - it will get applied with the `reverseType` property - // This should only happen when loading a legacy dashboard - .filter( - item => - item != null && item.direction !== TableUtils.sortDirection.reverse - ) as Sort[] + return IrisGridUtils.getColumnsToFetch( + columns, + bufferedColumns, + alwaysFetchColumnNames ); } /** - * Pulls just the table settings from the panel state, eg. filters/sorts - * @param panelState The dehydrated panel state - * @returns A dehydrated table settings object, { partition, partitionColumn, advancedFilters, quickFilters, sorts } + * Validate whether the ranges passed in are valid to take a snapshot from. + * Multiple selections are valid if all of the selected rows have the same columns selected. + * + * @param ranges The ranges to validate + * @returns True if the ranges are valid, false otherwise */ - static extractTableSettings( - panelState: { - irisGridState: { advancedFilters: AF; quickFilters: QF; sorts: S }; - irisGridPanelState: { - partitionColumn?: ColumnName; - partition?: unknown; - }; - }, - inputFilters: InputFilter[] = [] - ): { - partitionColumn: ColumnName | undefined; - partition: unknown; - advancedFilters: AF; - inputFilters: InputFilter[]; - quickFilters: QF; - sorts: S; - } { - const { irisGridPanelState, irisGridState } = panelState; - const { partitionColumn, partition } = irisGridPanelState; - const { advancedFilters, quickFilters, sorts } = irisGridState; + static isValidSnapshotRanges(ranges: readonly GridRange[]): boolean { + if (ranges == null || ranges.length === 0) { + return false; + } - return { - advancedFilters, - inputFilters, - partition, - partitionColumn, - quickFilters, - sorts, - }; + // To verify all the rows selected have the same set of columns selected, build a map with string representations + // of each range. + const rangeMap = new Map(); + for (let i = 0; i < ranges.length; i += 1) { + const range = ranges[i]; + const rowMapIndex = `${range.startRow}:${range.endRow}`; + const columnMapIndex = `${range.startColumn}:${range.endColumn}`; + if (!rangeMap.has(rowMapIndex)) { + rangeMap.set(rowMapIndex, []); + } + rangeMap.get(rowMapIndex).push(columnMapIndex); + } + + const keys = [...rangeMap.keys()]; + const matchColumnRanges = rangeMap.get(keys[0]).sort().join(','); + for (let i = 1; i < keys.length; i += 1) { + if (rangeMap.get(keys[i]).sort().join(',') !== matchColumnRanges) { + return false; + } + } + + return true; } /** - * Applies the passed in table settings directly to the provided table - * @param table The table to apply the settings to - * @param tableSettings Dehydrated table settings extracted with `extractTableSettings` - * @param timeZone The time zone to make this value in if it is a date type. E.g. America/New_York + * Check if the provided value is a valid table index + * @param value A value to check if it's a valid table index */ - static applyTableSettings( - dh: dhType, - table: Table, - tableSettings: TableSettings, - tableUtils: TableUtils, - timeZone: string - ): void { - const { columns } = table; - - let quickFilters: FilterCondition[] = []; - if (tableSettings.quickFilters) { - quickFilters = IrisGridUtils.getFiltersFromFilterMap( - IrisGridUtils.hydrateQuickFilters( - columns, - tableSettings.quickFilters, - tableUtils, - timeZone - ) - ); + static isValidIndex(value: unknown): boolean { + if (!Number.isInteger(value)) { + return false; + } + if (!(typeof value === 'number')) { + return false; } + return value >= 0; + } - let advancedFilters: FilterCondition[] = []; - if (tableSettings.advancedFilters) { - advancedFilters = IrisGridUtils.getFiltersFromFilterMap( - IrisGridUtils.hydrateAdvancedFilters( - dh, - columns, - tableSettings.advancedFilters, - tableUtils, - timeZone - ) - ); + /** + * Returns all columns used in any of the ranges provided + * @param ranges The model ranges to get columns for + * @param allColumns All the columns to pull from + * @returns The columns selected in the range + */ + static columnsFromRanges( + ranges: readonly GridRange[], + allColumns: readonly Column[] + ): Column[] { + if (ranges == null || ranges.length === 0) { + return []; } - const inputFilters = IrisGridUtils.getFiltersFromInputFilters( - columns, - tableUtils, - tableSettings.inputFilters, - timeZone - ); - - let sorts: Sort[] = []; - if (tableSettings.sorts) { - sorts = IrisGridUtils.hydrateSort(dh, columns, tableSettings.sorts); + if (ranges[0].startColumn === null && ranges[0].endColumn === null) { + // Snapshot of all the columns + return [...allColumns]; } - let filters = [...quickFilters, ...advancedFilters]; - const { partition, partitionColumn: partitionColumnName } = tableSettings; - if (partition != null && partitionColumnName != null) { - const partitionColumn = IrisGridUtils.getColumnByName( - columns, - partitionColumnName - ); - if (partitionColumn) { - const partitionFilter = partitionColumn - .filter() - .eq(dh.FilterValue.ofString(partition)); - filters = [partitionFilter, ...filters]; + const columnSet = new Set(); + for (let i = 0; i < ranges.length; i += 1) { + const range = ranges[i]; + assertNotNull(range.startColumn); + assertNotNull(range.endColumn); + for ( + let c = range.startColumn ?? 0; + c <= (range.endColumn ?? allColumns.length - 1); + c += 1 + ) { + columnSet.add(c); } } - filters = [...inputFilters, ...filters]; - - table.applyFilter(filters); - table.applySort(sorts); + return [...columnSet].map(c => allColumns[c]); } - static getInputFiltersForColumns( - columns: readonly Column[], - inputFilters: readonly InputFilter[] = [] - ): InputFilter[] { - return inputFilters.filter(({ name, type }) => - columns.find( - ({ name: columnName, type: columnType }) => - columnName === name && columnType === type - ) - ); + /** + * Transforms an iris data snapshot into a simple data matrix + * @param data The Iris formatted table data + * @returns A matrix of the values of the data + */ + static snapshotDataToMatrix(data: TableData): unknown[][] { + const { columns, rows } = data; + const result = []; + for (let r = 0; r < rows.length; r += 1) { + const row = rows[r]; + const rowData = []; + for (let c = 0; c < columns.length; c += 1) { + const column = columns[c]; + const value = row.get(column); + rowData.push(value); + } + result.push(rowData); + } + return result; } - static getFiltersFromInputFilters( - columns: readonly Column[], - tableUtils: TableUtils, - inputFilters: readonly InputFilter[] = [], - timeZone?: string - ): FilterCondition[] { - return inputFilters - .map(({ name, type, value }) => { - const column = columns.find( - ({ name: columnName, type: columnType }) => - columnName === name && columnType === type - ); - if (column) { - try { - return tableUtils.makeQuickFilter(column, value, timeZone); - } catch (e) { - // It may be unable to create it because user hasn't completed their input - log.debug('Unable to create input filter', e); - } - } + /** + * Hydrate model rollup config + * @param originalColumns Original model columns + * @param config Dehydrated rollup config + * @param aggregationSettings Aggregation settings + * @returns Rollup config for the model + */ + static getModelRollupConfig( + originalColumns: readonly Column[], + config: UIRollupConfig | undefined, + aggregationSettings: AggregationSettings + ): RollupConfig | null { + if ((config?.columns?.length ?? 0) === 0) { + return null; + } - return null; - }) - .filter(filter => filter != null) as FilterCondition[]; - } + const { + columns: groupingColumns = [], + showConstituents: includeConstituents = true, + showNonAggregatedColumns = true, + includeDescriptions = true, + } = config ?? {}; + const { aggregations = [] } = aggregationSettings ?? {}; + const aggregationColumns = aggregations.map( + ({ operation, selected, invert }) => + AggregationUtils.isRollupOperation(operation) + ? [] + : AggregationUtils.getOperationColumnNames( + originalColumns, + operation, + selected, + invert + ) + ); - static getFiltersFromFilterMap( - filterMap: ReadonlyAdvancedFilterMap | ReadonlyQuickFilterMap - ): FilterCondition[] { - const filters = []; + const aggregationMap = {} as Record; + // Aggregation columns should show first, add them first + for (let i = 0; i < aggregations.length; i += 1) { + aggregationMap[aggregations[i].operation] = aggregationColumns[i]; + } - const keys = Array.from(filterMap.keys()); - for (let i = 0; i < keys.length; i += 1) { - const key = keys[i]; - const item = filterMap.get(key); - if (item?.filter != null) { - filters.push(item.filter); + if (showNonAggregatedColumns) { + // Filter out any column that already has an aggregation or grouping + const nonAggregatedColumnSet = new Set( + originalColumns + .map(c => c.name) + .filter(name => !groupingColumns.includes(name)) + ); + aggregationColumns.forEach(columns => { + columns.forEach(name => nonAggregatedColumnSet.delete(name)); + }); + + if (nonAggregatedColumnSet.size > 0) { + const existingColumns = + aggregationMap[AggregationOperation.FIRST] ?? []; + aggregationMap[AggregationOperation.FIRST] = [ + ...existingColumns, + ...nonAggregatedColumnSet, + ]; } } - return filters; + return { + groupingColumns, + includeConstituents, + includeDescriptions, + aggregations: aggregationMap, + }; } /** - * Get array of hidden column indexes - * @param userColumnWidths Map of user column widths - * @returns Array of hidden column indexes + * @param pendingDataMap Map of pending data + * @returns A map with the errors in the pending data */ - static getHiddenColumns(userColumnWidths: ModelSizeMap): ModelIndex[] { - return [...userColumnWidths.entries()] - .filter(([, value]) => value === 0) - .map(([key]) => key); - } + static getPendingErrors(pendingDataMap: Map): void { + pendingDataMap.forEach((row, rowIndex) => { + if (!IrisGridUtils.isValidIndex(rowIndex)) { + throw new Error(`Invalid rowIndex ${rowIndex}`); + } - static parseCustomColumnNames( - customColumns: readonly ColumnName[] - ): ColumnName[] { - return customColumns.map(customColumn => customColumn.split('=')[0]); + const { data } = row; + data.forEach((value, columnIndex) => { + if (!IrisGridUtils.isValidIndex(columnIndex)) { + throw new Error(`Invalid columnIndex ${columnIndex}`); + } + }); + }); } - static getRemovedCustomColumnNames( - oldCustomColumns: readonly ColumnName[], - customColumns: readonly ColumnName[] - ): ColumnName[] { - const oldCustomColumnsNames = IrisGridUtils.parseCustomColumnNames( - oldCustomColumns - ); - const customColumnNames = IrisGridUtils.parseCustomColumnNames( - customColumns - ); - return oldCustomColumnsNames.filter( - oldCustomColumnName => !customColumnNames.includes(oldCustomColumnName) - ); - } + /** + * Retrieves a column from the provided array at the index, or `null` and logs an error if it's invalid + * + * @param columns The columns to get the column from + * @param columnIndex The column index to get + */ + static getColumn( + columns: readonly Column[], + columnIndex: ModelIndex + ): Column | null { + if (columnIndex < columns.length) { + return columns[columnIndex]; + } - static removeSortsInColumns( - sorts: readonly Sort[], - columnNames: readonly string[] - ): Sort[] { - return sorts.filter(sort => !columnNames.includes(sort.column.name)); - } + log.error('Unable to retrieve column', columnIndex, '>=', columns.length); - static removeFiltersInColumns( - columns: readonly Column[], - filters: ReadonlyMap, - removedColumnNames: readonly ColumnName[] - ): Map { - const columnNames = columns.map(({ name }) => name); - const newFilter = new Map(filters); - removedColumnNames.forEach(columnName => - newFilter.delete(columnNames.indexOf(columnName)) - ); - return newFilter; + return null; } - static removeColumnFromMovedColumns( - columns: readonly Column[], - movedColumns: readonly MoveOperation[], - removedColumnNames: readonly ColumnName[] - ): MoveOperation[] { - const columnNames = columns.map(({ name }) => name); - let newMoves = [...movedColumns]; - for (let i = 0; i < removedColumnNames.length; i += 1) { - const removedColumnName = removedColumnNames[i]; - let removedColumnIndex = columnNames.findIndex( - name => name === removedColumnName + /** + * Retrieves a column from the provided array matching the name, or `null` and logs an error if not found + * @param columns The columns to get the column from + * @param columnName The column name to retrieve + */ + static getColumnByName( + columns: readonly Column[], + columnName: ColumnName + ): Column | undefined { + const column = columns.find(({ name }) => name === columnName); + if (column == null) { + log.error( + 'Unable to retrieve column by name', + columnName, + columns.map(({ name }) => name) ); - const moves: MoveOperation[] = []; - for (let j = 0; j < newMoves.length; j += 1) { - const move = newMoves[j]; - const newMove = { ...move }; - let [fromStart, fromEnd] = Array.isArray(move.from) - ? move.from - : [move.from, move.from]; + } - if (removedColumnIndex <= move.to) { - newMove.to -= 1; - } + return column; + } - // If equal to fromStart, the new fromStart would stay the same - // It's just the next element in the range which will have the same index after deletion - if (removedColumnIndex < fromStart) { - fromStart -= 1; - } + /** + * Get filter configs with column names changed to indexes, exclude missing columns + * @param columns The columns to get column indexes from + * @param filters Filter configs + * @returns Updated filter configs with column names changed to indexes + */ + static changeFilterColumnNamesToIndexes( + columns: readonly Column[], + filters: { name: ColumnName; filter: T }[] + ): [number, T][] { + return filters + .map(({ name, filter }): null | [number, T] => { + const index = columns.findIndex(column => column.name === name); + return index < 0 ? null : [index, filter]; + }) + .filter(filterConfig => filterConfig != null) as [number, T][]; + } - if (removedColumnIndex <= fromEnd) { - fromEnd -= 1; - } + /** + * @param columnType The column type that the filters will be applied to. + * @param filterList The list of filters to be combined. + * @returns The combination of the filters in filterList as text. + */ + static combineFiltersFromList( + columnType: string, + filterList: FilterData[] + ): string { + filterList.sort((a, b) => { + // move all 'equals' comparisons to end of list + if (a.operator === 'eq' && b.operator !== 'eq') { + return 1; + } + if (a.operator !== 'eq' && b.operator === 'eq') { + return -1; + } + return a.startColumnIndex - b.startColumnIndex; + }); - if (fromStart <= fromEnd && fromStart !== newMove.to) { - if (fromStart === fromEnd) { - moves.push({ ...newMove, from: fromStart }); - } else { - moves.push({ ...newMove, from: [fromStart, fromEnd] }); + let combinedText = ''; + for (let i = 0; i < filterList.length; i += 1) { + const { text, value, operator } = filterList[i]; + if (value !== undefined) { + let symbol = ''; + if (operator !== undefined) { + if (value == null && operator === 'eq') { + symbol = '='; + } else if (operator !== 'eq') { + if (operator === 'startsWith' || operator === 'endsWith') { + symbol = '*'; + } else { + symbol = TableUtils.getFilterOperatorString(operator); + } } } - // get the next index of the removed column after the move is applied - // eslint-disable-next-line prefer-destructuring - removedColumnIndex = GridUtils.applyItemMoves( - removedColumnIndex, - removedColumnIndex, - [move] - )[0][0]; + let filterText = `${symbol}${text}`; + if (operator === 'startsWith') { + filterText = `${text}${symbol}`; + } + if ( + columnType != null && + value !== null && + TableUtils.isCharType(columnType) + ) { + filterText = `${symbol}${String.fromCharCode(parseInt(text, 10))}`; + } + if (i !== 0) { + combinedText += operator === 'eq' ? ' || ' : ' && '; + } + combinedText += filterText; } - - newMoves = moves; - columnNames.splice( - columnNames.findIndex(name => name === removedColumnName), - 1 - ); } - return newMoves; + return combinedText; } - static removeColumnsFromSelectDistinctColumns( - selectDistinctColumns: readonly ColumnName[], - removedColumnNames: readonly ColumnName[] - ): ColumnName[] { - return selectDistinctColumns.filter( - columnName => !removedColumnNames.includes(columnName) - ); - } + /** + * Parses the column header groups provided. + * If undefined, should provide default groups such as from layoutHints + * + * @returns Object containing groups array, max depth, map of name to parent group, and map of name to group + */ + static parseColumnHeaderGroups( + model: IrisGridModel, + groupsParam: readonly ColumnGroup[] + ): { + groups: ColumnHeaderGroup[]; + maxDepth: number; + parentMap: Map; + groupMap: Map; + } { + let maxDepth = 1; + const parentMap: Map = new Map(); + const groupMap: Map = new Map(); - static getVisibleColumnsInRange( - tableColumns: readonly Column[], - left: number, - right: number, - movedColumns: readonly MoveOperation[], - hiddenColumns: readonly number[] - ): Column[] { - const columns: Column[] = []; - for (let i = left; i <= right; i += 1) { - const modelIndex = GridUtils.getModelIndex(i, movedColumns); - if ( - modelIndex >= 0 && - modelIndex < tableColumns.length && - !hiddenColumns.includes(modelIndex) - ) { - columns.push(tableColumns[modelIndex]); - } - } - return columns; - } + // Remove any empty groups before parsing + const groups = groupsParam?.filter(({ children }) => children.length > 0); - static getPrevVisibleColumns( - tableColumns: readonly Column[], - startIndex: VisibleIndex, - count: number, - movedColumns: readonly MoveOperation[], - hiddenColumns: readonly VisibleIndex[] - ): Column[] { - const columns = []; - let i = startIndex; - while (i >= 0 && columns.length < count) { - const modelIndex = GridUtils.getModelIndex(i, movedColumns); - if ( - modelIndex >= 0 && - modelIndex < tableColumns.length && - !hiddenColumns.includes(modelIndex) - ) { - columns.unshift(tableColumns[modelIndex]); - } - i -= 1; + if (groups.length === 0) { + return { groups: [], maxDepth, parentMap, groupMap }; } - return columns; - } - static getNextVisibleColumns( - tableColumns: readonly Column[], - startIndex: VisibleIndex, - count: number, - movedColumns: readonly MoveOperation[], - hiddenColumns: readonly VisibleIndex[] - ): Column[] { - const columns = []; - let i = startIndex; - while (i < tableColumns.length && columns.length < count) { - const modelIndex = GridUtils.getModelIndex(i, movedColumns); - if ( - modelIndex >= 0 && - modelIndex < tableColumns.length && - !hiddenColumns.includes(modelIndex) - ) { - columns.push(tableColumns[modelIndex]); - } - i += 1; - } - return columns; - } + const originalGroupMap = new Map(groups.map(group => [group.name, group])); + const seenChildren = new Set(); - static getColumnsToFetch( - tableColumns: readonly Column[], - viewportColumns: readonly Column[], - alwaysFetchColumnNames: readonly ColumnName[] - ): Column[] { - const columnsToFetch = [...viewportColumns]; - alwaysFetchColumnNames.forEach(columnName => { - const column = tableColumns.find(({ name }) => name === columnName); - if (column != null && !viewportColumns.includes(column)) { - columnsToFetch.push(column); + const addGroup = (group: ColumnGroup): ColumnHeaderGroup => { + const { name } = group; + + if (model.getColumnIndexByName(name) != null) { + throw new Error(`Column header group has same name as column: ${name}`); } - }); - return columnsToFetch; - } - static getModelViewportColumns( - columns: readonly Column[], - left: number | null, - right: number | null, - movedColumns: readonly MoveOperation[], - hiddenColumns: readonly VisibleIndex[] = [], - alwaysFetchColumnNames: readonly ColumnName[] = [], - bufferPages = 0 - ): Column[] | null { - if (left == null || right == null) { - return null; - } + const existingGroup = groupMap.get(name); - const columnsCenter = IrisGridUtils.getVisibleColumnsInRange( - columns, - left, - right, - movedColumns, - hiddenColumns - ); - const bufferWidth = columnsCenter.length * bufferPages; - const columnsLeft = IrisGridUtils.getPrevVisibleColumns( - columns, - left - 1, - bufferWidth, - movedColumns, - hiddenColumns - ); - const columnsRight = IrisGridUtils.getNextVisibleColumns( - columns, - right + 1, - bufferWidth, - movedColumns, - hiddenColumns - ); + if (existingGroup) { + return existingGroup; + } - const bufferedColumns = [...columnsLeft, ...columnsCenter, ...columnsRight]; + const childIndexes: ColumnHeaderGroup['childIndexes'] = []; + let depth = 1; - return IrisGridUtils.getColumnsToFetch( - columns, - bufferedColumns, - alwaysFetchColumnNames - ); - } + group.children.forEach(childName => { + if (seenChildren.has(childName)) { + throw new Error( + `Column group child ${childName} specified in multiple groups` + ); + } + seenChildren.add(childName); - /** - * Get the dh.RangeSet representation of the provided ranges. - * Ranges are sorted prior to creating the RangeSet. Only the rows are taken into account, - * RangeSet does not have an option for columns. - * @param ranges The ranges to get the range set for - * @returns The rangeset for the provided ranges - */ - static rangeSetFromRanges( - dh: dhType, - ranges: readonly GridRange[] - ): RangeSet { - const rangeSets = ranges - .slice() - .sort((a, b): number => { - assertNotNull(a.startRow); - assertNotNull(b.startRow); - return a.startRow - b.startRow; - }) - .map(range => { - const { startRow, endRow } = range; - assertNotNull(startRow); - assertNotNull(endRow); - return dh.RangeSet.ofRange(startRow, endRow); + const childGroup = originalGroupMap.get(childName); + const childIndex = model.getColumnIndexByName(childName); + if (childGroup) { + // Adding another column header group + const addedGroup = addGroup(childGroup); + childIndexes.push(...addedGroup.childIndexes); + depth = Math.max(depth, addedGroup.depth + 1); + } else if (childIndex != null) { + // Adding a base column + childIndexes.push(childIndex); + depth = Math.max(depth, 1); + } else { + throw new Error(`Unknown child ${childName} in group ${name}`); + } }); - return dh.RangeSet.ofRanges(rangeSets); - } - /** - * Validate whether the ranges passed in are valid to take a snapshot from. - * Multiple selections are valid if all of the selected rows have the same columns selected. - * - * @param ranges The ranges to validate - * @returns True if the ranges are valid, false otherwise - */ - static isValidSnapshotRanges(ranges: readonly GridRange[]): boolean { - if (ranges == null || ranges.length === 0) { - return false; - } + const columnHeaderGroup = new ColumnHeaderGroup({ + ...group, + depth, + childIndexes: childIndexes.flat(), + }); - // To verify all the rows selected have the same set of columns selected, build a map with string representations - // of each range. - const rangeMap = new Map(); - for (let i = 0; i < ranges.length; i += 1) { - const range = ranges[i]; - const rowMapIndex = `${range.startRow}:${range.endRow}`; - const columnMapIndex = `${range.startColumn}:${range.endColumn}`; - if (!rangeMap.has(rowMapIndex)) { - rangeMap.set(rowMapIndex, []); - } - rangeMap.get(rowMapIndex).push(columnMapIndex); - } + groupMap.set(name, columnHeaderGroup); + group.children.forEach(childName => + parentMap.set(childName, columnHeaderGroup) + ); - const keys = [...rangeMap.keys()]; - const matchColumnRanges = rangeMap.get(keys[0]).sort().join(','); - for (let i = 1; i < keys.length; i += 1) { - if (rangeMap.get(keys[i]).sort().join(',') !== matchColumnRanges) { - return false; + maxDepth = Math.max(maxDepth, columnHeaderGroup.depth + 1); + return columnHeaderGroup; + }; + + const groupNames = new Set(); + + groups.forEach(group => { + const { name } = group; + if (groupNames.has(name)) { + throw new Error(`Duplicate column group name: ${name}`); } - } + groupNames.add(name); + addGroup(group); + }); - return true; + groupMap.forEach(group => { + group.setParent(parentMap.get(group.name)?.name); + }); + + return { groups: [...groupMap.values()], maxDepth, groupMap, parentMap }; } /** - * Check if the provided value is a valid table index - * @param value A value to check if it's a valid table index + * @param value The value of the cell in a column + * @param columnType The type of the column + * @returns The value of the cell converted to text */ - static isValidIndex(value: unknown): boolean { - if (!Number.isInteger(value)) { - return false; + static convertValueToText(value: unknown, columnType: string): string { + if ( + columnType != null && + TableUtils.isCharType(columnType) && + value != null && + typeof value === 'number' + ) { + return String.fromCharCode(value); } - if (!(typeof value === 'number')) { - return false; + if (TableUtils.isDateType(columnType) && isDateWrapper(value)) { + const date = new Date(value.asDate()); + const offset = date.getTimezoneOffset(); + const offsetDate = new Date(date.getTime() - offset * 60 * 1000); + const dateText = offsetDate.toISOString(); + const formattedText = dateText.replace('T', ' ').substring(0, 23); + return formattedText; } - return value >= 0; + if (value == null) { + return ''; + } + return `${value}`; } - /** - * Returns all columns used in any of the ranges provided - * @param ranges The model ranges to get columns for - * @param allColumns All the columns to pull from - * @returns The columns selected in the range - */ - static columnsFromRanges( - ranges: readonly GridRange[], - allColumns: readonly Column[] - ): Column[] { - if (ranges == null || ranges.length === 0) { - return []; - } - if (ranges[0].startColumn === null && ranges[0].endColumn === null) { - // Snapshot of all the columns - return [...allColumns]; - } + private dh: DhType; - const columnSet = new Set(); - for (let i = 0; i < ranges.length; i += 1) { - const range = ranges[i]; - assertNotNull(range.startColumn); - assertNotNull(range.endColumn); - for ( - let c = range.startColumn ?? 0; - c <= (range.endColumn ?? allColumns.length - 1); - c += 1 - ) { - columnSet.add(c); - } - } - return [...columnSet].map(c => allColumns[c]); + private tableUtils: TableUtils; + + constructor(dh: DhType) { + this.dh = dh; + this.tableUtils = new TableUtils(dh); } /** - * Transforms an iris data snapshot into a simple data matrix - * @param data The Iris formatted table data - * @returns A matrix of the values of the data + * Exports the state from IrisGrid to a JSON stringifiable object + * @param model The table model to export the state for + * @param irisGridState The current state of the IrisGrid */ - static snapshotDataToMatrix(data: TableData): unknown[][] { - const { columns, rows } = data; - const result = []; - for (let r = 0; r < rows.length; r += 1) { - const row = rows[r]; - const rowData = []; - for (let c = 0; c < columns.length; c += 1) { - const column = columns[c]; - const value = row.get(column); - rowData.push(value); - } - result.push(rowData); - } - return result; + dehydrateIrisGridState( + model: IrisGridModel, + irisGridState: HydratedIrisGridState + ): DehydratedIrisGridState { + const { + aggregationSettings = { aggregations: EMPTY_ARRAY, showOnTop: false }, + advancedFilters, + customColumnFormatMap, + isFilterBarShown, + metrics, + quickFilters, + customColumns, + conditionalFormats = EMPTY_ARRAY, + reverseType, + rollupConfig = undefined, + showSearchBar, + searchValue, + selectDistinctColumns = EMPTY_ARRAY, + selectedSearchColumns, + sorts, + invertSearchColumns, + pendingDataMap = EMPTY_MAP, + frozenColumns, + columnHeaderGroups, + } = irisGridState; + assertNotNull(metrics); + const { userColumnWidths, userRowHeights } = metrics; + const { columns } = model; + return { + advancedFilters: this.dehydrateAdvancedFilters(columns, advancedFilters), + aggregationSettings, + customColumnFormatMap: [...customColumnFormatMap], + isFilterBarShown, + quickFilters: IrisGridUtils.dehydrateQuickFilters(quickFilters), + sorts: IrisGridUtils.dehydrateSort(sorts), + userColumnWidths: [...userColumnWidths] + .filter( + ([columnIndex]) => + columnIndex != null && + columnIndex >= 0 && + columnIndex < columns.length + ) + .map(([columnIndex, width]) => [columns[columnIndex].name, width]), + userRowHeights: [...userRowHeights], + customColumns: [...customColumns], + conditionalFormats: [...conditionalFormats], + reverseType, + rollupConfig, + showSearchBar, + searchValue, + selectDistinctColumns: [...selectDistinctColumns], + selectedSearchColumns, + invertSearchColumns, + pendingDataMap: this.dehydratePendingDataMap(columns, pendingDataMap), + frozenColumns, + columnHeaderGroups: columnHeaderGroups?.map(item => ({ + name: item.name, + children: item.children, + color: item.color, + })), + }; } /** - * Hydrate model rollup config - * @param originalColumns Original model columns - * @param config Dehydrated rollup config - * @param aggregationSettings Aggregation settings - * @returns Rollup config for the model + * Import a state for IrisGrid that was exported with {{@link dehydrateIrisGridState}} + * @param model The table model to import the state with + * @param irisGridState The saved IrisGrid state */ - static getModelRollupConfig( - originalColumns: readonly Column[], - config: UIRollupConfig | undefined, - aggregationSettings: AggregationSettings - ): RollupConfig | null { - if ((config?.columns?.length ?? 0) === 0) { - return null; - } - + hydrateIrisGridState( + model: IrisGridModel, + irisGridState: DehydratedIrisGridState + ): Omit & { + userColumnWidths: ModelSizeMap; + userRowHeights: ModelSizeMap; + } { const { - columns: groupingColumns = [], - showConstituents: includeConstituents = true, - showNonAggregatedColumns = true, - includeDescriptions = true, - } = config ?? {}; - const { aggregations = [] } = aggregationSettings ?? {}; - const aggregationColumns = aggregations.map( - ({ operation, selected, invert }) => - AggregationUtils.isRollupOperation(operation) - ? [] - : AggregationUtils.getOperationColumnNames( - originalColumns, - operation, - selected, - invert - ) - ); + advancedFilters, + aggregationSettings = { aggregations: [], showOnTop: false }, + customColumnFormatMap, + isFilterBarShown, + quickFilters, + sorts, + customColumns, + conditionalFormats, + userColumnWidths, + userRowHeights, + reverseType, + rollupConfig = undefined, + showSearchBar, + searchValue, + selectDistinctColumns, + selectedSearchColumns, + invertSearchColumns = true, + pendingDataMap = [], + frozenColumns, + columnHeaderGroups, + } = irisGridState; + const { columns, formatter } = model; - const aggregationMap = {} as Record; - // Aggregation columns should show first, add them first - for (let i = 0; i < aggregations.length; i += 1) { - aggregationMap[aggregations[i].operation] = aggregationColumns[i]; - } + return { + advancedFilters: this.hydrateAdvancedFilters( + columns, + advancedFilters, + formatter.timeZone + ), + aggregationSettings, + customColumnFormatMap: new Map(customColumnFormatMap), + isFilterBarShown, + quickFilters: this.hydrateQuickFilters( + columns, + quickFilters, + formatter.timeZone + ), + sorts: this.hydrateSort(columns, sorts), + userColumnWidths: new Map( + userColumnWidths + .map(([column, width]: [string | number, number]): [ + number, + number + ] => { + if ( + typeof column === 'string' || + (column as unknown) instanceof String + ) { + return [columns.findIndex(({ name }) => name === column), width]; + } + return [column, width]; + }) + .filter( + ([column]) => + column != null && column >= 0 && column < columns.length + ) + ), + customColumns, + conditionalFormats, + userRowHeights: new Map(userRowHeights), + reverseType, + rollupConfig, + showSearchBar, + searchValue, + selectDistinctColumns, + selectedSearchColumns, + invertSearchColumns, + pendingDataMap: this.hydratePendingDataMap( + columns, + pendingDataMap + ) as PendingDataMap, + frozenColumns, + columnHeaderGroups: IrisGridUtils.parseColumnHeaderGroups( + model, + columnHeaderGroups ?? model.layoutHints?.columnGroups ?? [] + ).groups, + }; + } - if (showNonAggregatedColumns) { - // Filter out any column that already has an aggregation or grouping - const nonAggregatedColumnSet = new Set( - originalColumns - .map(c => c.name) - .filter(name => !groupingColumns.includes(name)) - ); - aggregationColumns.forEach(columns => { - columns.forEach(name => nonAggregatedColumnSet.delete(name)); - }); + /** + * Import the saved quick filters to apply to the columns. Does not actually apply the filters. + * @param columns The columns the filters will be applied to + * @param savedQuickFilters Exported quick filters definitions + * @param timeZone The time zone to make this value in if it is a date type. E.g. America/New_York + * @returns The quick filters to apply to the columns + */ + hydrateQuickFilters( + columns: readonly Column[], + savedQuickFilters: readonly DehydratedQuickFilter[], + timeZone?: string + ): ReadonlyQuickFilterMap { + const importedFilters = savedQuickFilters.map( + ([columnIndex, quickFilter]: DehydratedQuickFilter): [ + number, + { text: string; filter: FilterCondition | null } + ] => { + const { text } = quickFilter; + + let filter = null; + try { + const column = IrisGridUtils.getColumn(columns, columnIndex); + if (column != null) { + filter = this.tableUtils.makeQuickFilter(column, text, timeZone); + } + } catch (error) { + log.error('hydrateQuickFilters error with', text, error); + } - if (nonAggregatedColumnSet.size > 0) { - const existingColumns = - aggregationMap[AggregationOperation.FIRST] ?? []; - aggregationMap[AggregationOperation.FIRST] = [ - ...existingColumns, - ...nonAggregatedColumnSet, - ]; + return [columnIndex, { text, filter }]; } - } + ); - return { - groupingColumns, - includeConstituents, - includeDescriptions, - aggregations: aggregationMap, - }; + return new Map(importedFilters); } /** - * @param pendingDataMap Map of pending data - * @returns A map with the errors in the pending data + * Export the advanced filters from the provided columns to JSON striginfiable object + * @param columns The columns for the filters + * @param advancedFilters The advanced filters to dehydrate + * @returns The dehydrated advanced filters */ - static getPendingErrors(pendingDataMap: Map): void { - pendingDataMap.forEach((row, rowIndex) => { - if (!IrisGridUtils.isValidIndex(rowIndex)) { - throw new Error(`Invalid rowIndex ${rowIndex}`); - } - - const { data } = row; - data.forEach((value, columnIndex) => { - if (!IrisGridUtils.isValidIndex(columnIndex)) { - throw new Error(`Invalid columnIndex ${columnIndex}`); - } - }); + dehydrateAdvancedFilters( + columns: readonly Column[], + advancedFilters: ReadonlyAdvancedFilterMap + ): DehydratedAdvancedFilter[] { + return [...advancedFilters].map(([columnIndex, advancedFilter]) => { + const column = IrisGridUtils.getColumn(columns, columnIndex); + assertNotNull(column); + const options = this.dehydrateAdvancedFilterOptions( + column, + advancedFilter.options + ); + return [columnIndex, { options }]; }); } /** - * Retrieves a column from the provided array at the index, or `null` and logs an error if it's invalid - * - * @param columns The columns to get the column from - * @param columnIndex The column index to get + * Import the saved advanced filters to apply to the columns. Does not actually apply the filters. + * @param columns The columns the filters will be applied to + * @param savedAdvancedFilters Exported advanced filters definitions + * @param timeZone The time zone to make this filter in if it is a date type. E.g. America/New_York + * @returns The advanced filters to apply to the columns */ - static getColumn( + hydrateAdvancedFilters( columns: readonly Column[], - columnIndex: ModelIndex - ): Column | null { - if (columnIndex < columns.length) { - return columns[columnIndex]; - } + savedAdvancedFilters: readonly DehydratedAdvancedFilter[], + timeZone: string + ): ReadonlyAdvancedFilterMap { + const importedFilters = savedAdvancedFilters.map( + ([columnIndex, advancedFilter]: DehydratedAdvancedFilter): [ + number, + { options: AdvancedFilterOptions; filter: FilterCondition | null } + ] => { + const column = IrisGridUtils.getColumn(columns, columnIndex); + assertNotNull(column); + const options = this.hydrateAdvancedFilterOptions( + column, + advancedFilter.options + ); + let filter = null; - log.error('Unable to retrieve column', columnIndex, '>=', columns.length); + try { + const columnRetrieved = IrisGridUtils.getColumn(columns, columnIndex); + if (columnRetrieved != null) { + filter = this.tableUtils.makeAdvancedFilter( + column, + options, + timeZone + ); + } + } catch (error) { + log.error('hydrateAdvancedFilters error with', options, error); + } - return null; + return [columnIndex, { options, filter }]; + } + ); + + return new Map(importedFilters); } - /** - * Retrieves a column from the provided array matching the name, or `null` and logs an error if not found - * @param columns The columns to get the column from - * @param columnName The column name to retrieve - */ - static getColumnByName( - columns: readonly Column[], - columnName: ColumnName - ): Column | undefined { - const column = columns.find(({ name }) => name === columnName); - if (column == null) { - log.error( - 'Unable to retrieve column by name', - columnName, - columns.map(({ name }) => name) - ); - } + dehydrateAdvancedFilterOptions( + column: Column, + options: AdvancedFilterOptions + ): AdvancedFilterOptions { + const { selectedValues, ...otherOptions } = options; + return { + selectedValues: selectedValues?.map((value: unknown) => + this.dehydrateValue(value, column?.type) + ), + ...otherOptions, + }; + } - return column; + hydrateAdvancedFilterOptions( + column: Column, + options: AdvancedFilterOptions + ): AdvancedFilterOptions { + const { selectedValues, ...otherOptions } = options; + return { + selectedValues: selectedValues?.map(value => + this.hydrateValue(value, column?.type) + ), + ...otherOptions, + }; } - /** - * Get filter configs with column names changed to indexes, exclude missing columns - * @param columns The columns to get column indexes from - * @param filters Filter configs - * @returns Updated filter configs with column names changed to indexes - */ - static changeFilterColumnNamesToIndexes( + dehydratePendingDataMap( columns: readonly Column[], - filters: { name: ColumnName; filter: T }[] - ): [number, T][] { - return filters - .map(({ name, filter }): null | [number, T] => { - const index = columns.findIndex(column => column.name === name); - return index < 0 ? null : [index, filter]; - }) - .filter(filterConfig => filterConfig != null) as [number, T][]; + pendingDataMap: ReadonlyMap< + ModelIndex, + { + data: Map; + } + > + ): DehydratedPendingDataMap { + return [...pendingDataMap].map(([rowIndex, { data }]) => [ + rowIndex, + { + data: [...data].map(([c, value]) => [ + columns[c].name, + this.dehydrateValue(value, columns[c].type), + ]), + }, + ]); } - /** - * @param columnType The column type that the filters will be applied to. - * @param filterList The list of filters to be combined. - * @returns The combination of the filters in filterList as text. - */ - static combineFiltersFromList( - columnType: string, - filterList: FilterData[] - ): string { - filterList.sort((a, b) => { - // move all 'equals' comparisons to end of list - if (a.operator === 'eq' && b.operator !== 'eq') { - return 1; - } - if (a.operator !== 'eq' && b.operator === 'eq') { - return -1; + hydratePendingDataMap( + columns: readonly Column[], + pendingDataMap: DehydratedPendingDataMap + ): Map< + number, + { data: Map } + > { + const columnMap = new Map(); + const getColumnIndex = (columnName: ColumnName) => { + if (!columnMap.has(columnName)) { + columnMap.set( + columnName, + columns.findIndex(({ name }) => name === columnName) + ); } - return a.startColumnIndex - b.startColumnIndex; - }); + return columnMap.get(columnName); + }; - let combinedText = ''; - for (let i = 0; i < filterList.length; i += 1) { - const { text, value, operator } = filterList[i]; - if (value !== undefined) { - let symbol = ''; - if (operator !== undefined) { - if (value == null && operator === 'eq') { - symbol = '='; - } else if (operator !== 'eq') { - if (operator === 'startsWith' || operator === 'endsWith') { - symbol = '*'; - } else { - symbol = TableUtils.getFilterOperatorString(operator); - } - } - } + return new Map( + pendingDataMap.map( + ([rowIndex, { data }]: [ + number, + { data: [string, CellData | string | null][] } + ]) => [ + rowIndex, + { + data: new Map( + data.map(([columnName, value]) => { + const index = getColumnIndex(columnName); + assertNotNull(index); + return [ + getColumnIndex(columnName) ?? null, + this.hydrateValue(value, columns[index].type), + ]; + }) + ), + }, + ] + ) + ); + } - let filterText = `${symbol}${text}`; - if (operator === 'startsWith') { - filterText = `${text}${symbol}`; - } - if ( - columnType != null && - value !== null && - TableUtils.isCharType(columnType) - ) { - filterText = `${symbol}${String.fromCharCode(parseInt(text, 10))}`; - } - if (i !== 0) { - combinedText += operator === 'eq' ? ' || ' : ' && '; - } - combinedText += filterText; - } + /** + * Dehydrates/serializes a value for storage. + * @param value The value to dehydrate + * @param columnType The column type + */ + dehydrateValue(value: T, columnType: string): string | T | null { + if (TableUtils.isDateType(columnType)) { + return this.dehydrateDateTime( + (value as unknown) as number | DateWrapper | Date + ); + } + + if (TableUtils.isLongType(columnType)) { + return IrisGridUtils.dehydrateLong(value); } - return combinedText; + + return value; } /** - * Parses the column header groups provided. - * If undefined, should provide default groups such as from layoutHints - * - * @returns Object containing groups array, max depth, map of name to parent group, and map of name to group + * Hydrate a value from it's serialized state + * @param value The dehydrated value that needs to be hydrated + * @param columnType The type of column */ - static parseColumnHeaderGroups( - model: IrisGridModel, - groupsParam: readonly ColumnGroup[] - ): { - groups: ColumnHeaderGroup[]; - maxDepth: number; - parentMap: Map; - groupMap: Map; - } { - let maxDepth = 1; - const parentMap: Map = new Map(); - const groupMap: Map = new Map(); - - // Remove any empty groups before parsing - const groups = groupsParam?.filter(({ children }) => children.length > 0); + hydrateValue( + value: T, + columnType: string + ): DateWrapper | LongWrapper | T | null { + if (TableUtils.isDateType(columnType)) { + return this.hydrateDateTime((value as unknown) as string); + } - if (groups.length === 0) { - return { groups: [], maxDepth, parentMap, groupMap }; + if (TableUtils.isLongType(columnType)) { + return this.hydrateLong((value as unknown) as string); } - const originalGroupMap = new Map(groups.map(group => [group.name, group])); - const seenChildren = new Set(); + return value; + } - const addGroup = (group: ColumnGroup): ColumnHeaderGroup => { - const { name } = group; + dehydrateDateTime(value: number | DateWrapper | Date): string | null { + return value != null + ? this.dh.i18n.DateTimeFormat.format(DateUtils.FULL_DATE_FORMAT, value) + : null; + } - if (model.getColumnIndexByName(name) != null) { - throw new Error(`Column header group has same name as column: ${name}`); - } + hydrateDateTime(value: string): DateWrapper | null { + return value != null + ? this.dh.i18n.DateTimeFormat.parse(DateUtils.FULL_DATE_FORMAT, value) + : null; + } - const existingGroup = groupMap.get(name); + hydrateLong(value: string): LongWrapper | null { + return value != null ? this.dh.LongWrapper.ofString(value) : null; + } - if (existingGroup) { - return existingGroup; - } + /** + * Import the saved sorts to apply to the table. Does not actually apply the sort. + * @param columns The columns the sorts will be applied to + * @param sorts Exported sort definitions + * @returns The sorts to apply to the table + */ + hydrateSort( + columns: readonly Column[], + sorts: readonly (DehydratedSort | LegacyDehydratedSort)[] + ): Sort[] { + const { dh } = this; + return ( + sorts + .map(sort => { + const { column: columnIndexOrName, isAbs, direction } = sort; + if (direction === TableUtils.sortDirection.reverse) { + return dh.Table.reverse(); + } - const childIndexes: ColumnHeaderGroup['childIndexes'] = []; - let depth = 1; + const column = + typeof columnIndexOrName === 'string' + ? IrisGridUtils.getColumnByName(columns, columnIndexOrName) + : IrisGridUtils.getColumn(columns, columnIndexOrName); - group.children.forEach(childName => { - if (seenChildren.has(childName)) { - throw new Error( - `Column group child ${childName} specified in multiple groups` - ); - } - seenChildren.add(childName); + if (column != null) { + let columnSort = column.sort(); + if (isAbs) { + columnSort = columnSort.abs(); + } + if (direction === TableUtils.sortDirection.descending) { + columnSort = columnSort.desc(); + } else { + columnSort = columnSort.asc(); + } + return columnSort; + } - const childGroup = originalGroupMap.get(childName); - const childIndex = model.getColumnIndexByName(childName); - if (childGroup) { - // Adding another column header group - const addedGroup = addGroup(childGroup); - childIndexes.push(...addedGroup.childIndexes); - depth = Math.max(depth, addedGroup.depth + 1); - } else if (childIndex != null) { - // Adding a base column - childIndexes.push(childIndex); - depth = Math.max(depth, 1); - } else { - throw new Error(`Unknown child ${childName} in group ${name}`); - } - }); + return null; + }) + // If we can't find the column any more, it's null, filter it out + // If the item is a reverse sort item, filter it out - it will get applied with the `reverseType` property + // This should only happen when loading a legacy dashboard + .filter( + item => + item != null && item.direction !== TableUtils.sortDirection.reverse + ) as Sort[] + ); + } - const columnHeaderGroup = new ColumnHeaderGroup({ - ...group, - depth, - childIndexes: childIndexes.flat(), - }); + /** + * Applies the passed in table settings directly to the provided table + * @param table The table to apply the settings to + * @param tableSettings Dehydrated table settings extracted with `extractTableSettings` + * @param timeZone The time zone to make this value in if it is a date type. E.g. America/New_York + */ + applyTableSettings( + table: Table, + tableSettings: TableSettings, + timeZone: string + ): void { + const { columns } = table; - groupMap.set(name, columnHeaderGroup); - group.children.forEach(childName => - parentMap.set(childName, columnHeaderGroup) + let quickFilters: FilterCondition[] = []; + if (tableSettings.quickFilters) { + quickFilters = IrisGridUtils.getFiltersFromFilterMap( + this.hydrateQuickFilters(columns, tableSettings.quickFilters, timeZone) ); + } - maxDepth = Math.max(maxDepth, columnHeaderGroup.depth + 1); - return columnHeaderGroup; - }; + let advancedFilters: FilterCondition[] = []; + if (tableSettings.advancedFilters) { + advancedFilters = IrisGridUtils.getFiltersFromFilterMap( + this.hydrateAdvancedFilters( + columns, + tableSettings.advancedFilters, + timeZone + ) + ); + } + const inputFilters = this.getFiltersFromInputFilters( + columns, + tableSettings.inputFilters, + timeZone + ); - const groupNames = new Set(); + let sorts: Sort[] = []; + if (tableSettings.sorts) { + sorts = this.hydrateSort(columns, tableSettings.sorts); + } - groups.forEach(group => { - const { name } = group; - if (groupNames.has(name)) { - throw new Error(`Duplicate column group name: ${name}`); + let filters = [...quickFilters, ...advancedFilters]; + const { partition, partitionColumn: partitionColumnName } = tableSettings; + if (partition != null && partitionColumnName != null) { + const partitionColumn = IrisGridUtils.getColumnByName( + columns, + partitionColumnName + ); + if (partitionColumn) { + const partitionFilter = partitionColumn + .filter() + .eq(dh.FilterValue.ofString(partition)); + filters = [partitionFilter, ...filters]; } - groupNames.add(name); - addGroup(group); - }); + } + filters = [...inputFilters, ...filters]; - groupMap.forEach(group => { - group.setParent(parentMap.get(group.name)?.name); - }); + table.applyFilter(filters); + table.applySort(sorts); + } - return { groups: [...groupMap.values()], maxDepth, groupMap, parentMap }; + getFiltersFromInputFilters( + columns: readonly Column[], + inputFilters: readonly InputFilter[] = [], + timeZone?: string + ): FilterCondition[] { + return inputFilters + .map(({ name, type, value }) => { + const column = columns.find( + ({ name: columnName, type: columnType }) => + columnName === name && columnType === type + ); + if (column) { + try { + return this.tableUtils.makeQuickFilter(column, value, timeZone); + } catch (e) { + // It may be unable to create it because user hasn't completed their input + log.debug('Unable to create input filter', e); + } + } + + return null; + }) + .filter(filter => filter != null) as FilterCondition[]; } /** - * @param value The value of the cell in a column - * @param columnType The type of the column - * @returns The value of the cell converted to text + * Get the dh.RangeSet representation of the provided ranges. + * Ranges are sorted prior to creating the RangeSet. Only the rows are taken into account, + * RangeSet does not have an option for columns. + * @param ranges The ranges to get the range set for + * @returns The rangeset for the provided ranges */ - static convertValueToText(value: unknown, columnType: string): string { - if ( - columnType != null && - TableUtils.isCharType(columnType) && - value != null && - typeof value === 'number' - ) { - return String.fromCharCode(value); - } - if (TableUtils.isDateType(columnType) && isDateWrapper(value)) { - const date = new Date(value.asDate()); - const offset = date.getTimezoneOffset(); - const offsetDate = new Date(date.getTime() - offset * 60 * 1000); - const dateText = offsetDate.toISOString(); - const formattedText = dateText.replace('T', ' ').substring(0, 23); - return formattedText; - } - if (value == null) { - return ''; - } - return `${value}`; + rangeSetFromRanges(ranges: readonly GridRange[]): RangeSet { + const { dh } = this; + const rangeSets = ranges + .slice() + .sort((a, b): number => { + assertNotNull(a.startRow); + assertNotNull(b.startRow); + return a.startRow - b.startRow; + }) + .map(range => { + const { startRow, endRow } = range; + assertNotNull(startRow); + assertNotNull(endRow); + return dh.RangeSet.ofRange(startRow, endRow); + }); + return dh.RangeSet.ofRanges(rangeSets); } } diff --git a/packages/iris-grid/src/sidebar/ChartBuilder.test.tsx b/packages/iris-grid/src/sidebar/ChartBuilder.test.tsx index dcaae29748..9505ee1633 100644 --- a/packages/iris-grid/src/sidebar/ChartBuilder.test.tsx +++ b/packages/iris-grid/src/sidebar/ChartBuilder.test.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { render } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import dh from '@deephaven/jsapi-shim'; import ChartBuilder from './ChartBuilder'; import IrisGridTestUtils from '../IrisGridTestUtils'; @@ -10,6 +11,7 @@ function makeChartBuilderWrapper({ onChange = () => null, onSubmit = () => null, model = IrisGridTestUtils.makeModel( + dh, IrisGridTestUtils.makeTable({ columns: COLUMN_NAMES.map(name => IrisGridTestUtils.makeColumn(name)), }) diff --git a/packages/iris-grid/src/sidebar/CustomColumnBuilder.test.tsx b/packages/iris-grid/src/sidebar/CustomColumnBuilder.test.tsx index 1e366f038f..dc48be1b94 100644 --- a/packages/iris-grid/src/sidebar/CustomColumnBuilder.test.tsx +++ b/packages/iris-grid/src/sidebar/CustomColumnBuilder.test.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { EventShimCustomEvent } from '@deephaven/utils'; +import dh from '@deephaven/jsapi-shim'; import CustomColumnBuilder, { CustomColumnBuilderProps, } from './CustomColumnBuilder'; @@ -9,7 +10,7 @@ import IrisGridTestUtils from '../IrisGridTestUtils'; import IrisGridModel from '../IrisGridModel'; function Builder({ - model = IrisGridTestUtils.makeModel(), + model = IrisGridTestUtils.makeModel(dh), customColumns = [], onSave = jest.fn(), onCancel = jest.fn(), @@ -44,7 +45,7 @@ test('Calls on save', async () => { test('Switches to loader button while saving', async () => { jest.useFakeTimers(); const user = userEvent.setup({ delay: null }); - const model = IrisGridTestUtils.makeModel(); + const model = IrisGridTestUtils.makeModel(dh); const mockSave = jest.fn(() => setTimeout(() => { model.dispatchEvent( @@ -99,7 +100,7 @@ test('Ignores deleted formulas on save', async () => { // This test instead creates the new text, saves, then removes it to test the same behavior jest.useFakeTimers(); const user = userEvent.setup({ delay: null }); - const model = IrisGridTestUtils.makeModel(); + const model = IrisGridTestUtils.makeModel(dh); const mockSave = jest.fn(() => setTimeout(() => { model.dispatchEvent( @@ -150,7 +151,7 @@ test('Deletes columns', async () => { test('Displays request failure message', async () => { const user = userEvent.setup(); - const model = IrisGridTestUtils.makeModel(); + const model = IrisGridTestUtils.makeModel(dh); render(); // Should ignore this since not in saving state diff --git a/packages/iris-grid/src/sidebar/TableCsvExporter.test.tsx b/packages/iris-grid/src/sidebar/TableCsvExporter.test.tsx index 446bd05512..68b623e103 100644 --- a/packages/iris-grid/src/sidebar/TableCsvExporter.test.tsx +++ b/packages/iris-grid/src/sidebar/TableCsvExporter.test.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import dh from '@deephaven/jsapi-shim'; import TableCsvExporter from './TableCsvExporter'; import IrisGridTestUtils from '../IrisGridTestUtils'; @@ -30,7 +31,7 @@ function makeTableCsvExporterWrapper({ selectedRanges = [], userColumnWidths = IrisGridTestUtils.makeUserColumnWidths(), movedColumns = [], - model = IrisGridTestUtils.makeModel(TABLE), + model = IrisGridTestUtils.makeModel(dh, TABLE), } = {}) { return render( { const onDownloadStart = jest.fn(); const onDownload = jest.fn(); const onCancel = jest.fn(); - const model = IrisGridTestUtils.makeModel(BAD_TABLE); + const model = IrisGridTestUtils.makeModel(dh, BAD_TABLE); makeTableCsvExporterWrapper({ onDownloadStart, onDownload, diff --git a/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilder.test.tsx b/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilder.test.tsx index b6c00c4b07..487d465a07 100644 --- a/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilder.test.tsx +++ b/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilder.test.tsx @@ -4,7 +4,8 @@ import userEvent from '@testing-library/user-event'; import { GridUtils } from '@deephaven/grid'; import type { MoveOperation } from '@deephaven/grid'; import { assertNotNull, TestUtils } from '@deephaven/utils'; -import type { ColumnGroup } from '@deephaven/jsapi-shim'; +import dh from '@deephaven/jsapi-shim'; +import { ColumnGroup } from '@deephaven/jsapi-types'; import VisibilityOrderingBuilder from './VisibilityOrderingBuilder'; import IrisGridTestUtils from '../../IrisGridTestUtils'; import ColumnHeaderGroup from '../../ColumnHeaderGroup'; @@ -89,12 +90,14 @@ function BuilderWithNestedGroups({ function makeModel() { return IrisGridTestUtils.makeModel( + dh, IrisGridTestUtils.makeTable({ columns: COLUMNS }) ); } function makeModelWithGroups(groups = COLUMN_HEADER_GROUPS) { return IrisGridTestUtils.makeModel( + dh, IrisGridTestUtils.makeTable({ columns: COLUMNS, layoutHints: { columnGroups: groups }, @@ -155,6 +158,7 @@ test('Renders all children', () => { test('Renders immovable items', () => { const model = IrisGridTestUtils.makeModel( + dh, IrisGridTestUtils.makeTable({ columns: COLUMNS, layoutHints: { @@ -178,6 +182,7 @@ test('Renders immovable items', () => { test('Renders a grid of only immovable items', () => { const model = IrisGridTestUtils.makeModel( + dh, IrisGridTestUtils.makeTable({ columns: IrisGridTestUtils.makeColumns(1, COLUMN_PREFIX), layoutHints: { diff --git a/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilderUtils.test.ts b/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilderUtils.test.ts index c2af59368f..b78b6350ee 100644 --- a/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilderUtils.test.ts +++ b/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilderUtils.test.ts @@ -1,4 +1,5 @@ -import type { ColumnGroup } from '@deephaven/jsapi-shim'; +import dh from '@deephaven/jsapi-shim'; +import { ColumnGroup } from '@deephaven/jsapi-types'; import IrisGridTestUtils from '../../IrisGridTestUtils'; import { moveItemsFromDrop, @@ -47,6 +48,7 @@ const NESTED_COLUMN_HEADER_GROUPS: ColumnGroup[] = [ function makeTreeItems(groups: ColumnGroup[] = []) { const model = IrisGridTestUtils.makeModel( + dh, IrisGridTestUtils.makeTable({ columns: COLUMNS, layoutHints: { columnGroups: groups }, diff --git a/packages/jsapi-utils/src/DateUtils.test.ts b/packages/jsapi-utils/src/DateUtils.test.ts index ca96948119..514a88b22d 100644 --- a/packages/jsapi-utils/src/DateUtils.test.ts +++ b/packages/jsapi-utils/src/DateUtils.test.ts @@ -1,3 +1,4 @@ +import dh from '@deephaven/jsapi-shim'; import DateUtils from './DateUtils'; describe('month parsing tests', () => { @@ -179,7 +180,7 @@ describe('makeDateWrapper', () => { const expectedDate = new Date(2022, 0, 1, 0, 0, 0, 0); expect( - DateUtils.makeDateWrapper('Asia/Dubai', 2022).valueOf() + DateUtils.makeDateWrapper(dh, 'Asia/Dubai', 2022).valueOf() ).toStrictEqual(expectedDate.valueOf().toString()); }); }); @@ -212,20 +213,20 @@ describe('parseDateRange', () => { } it('should throw an error if the text is empty', () => { - expect(() => DateUtils.parseDateRange('', 'America/New_York')).toThrowError( - 'Cannot parse date range from empty string' - ); + expect(() => + DateUtils.parseDateRange(dh, '', 'America/New_York') + ).toThrowError('Cannot parse date range from empty string'); }); it('should return a range of null values if text is "null"', () => { - expect(DateUtils.parseDateRange('null', 'America/New_York')).toEqual([ + expect(DateUtils.parseDateRange(dh, 'null', 'America/New_York')).toEqual([ null, null, ]); }); it('should return a range from today to tomorrow if text is "today"', () => { - const range = DateUtils.parseDateRange('today', 'America/New_York'); + const range = DateUtils.parseDateRange(dh, 'today', 'America/New_York'); const start = range[0]; const end = range[1]; if (start && end) { @@ -236,13 +237,13 @@ describe('parseDateRange', () => { }); it('should return null as the end range if text is "now"', () => { - const range = DateUtils.parseDateRange('now', 'America/New_York'); + const range = DateUtils.parseDateRange(dh, 'now', 'America/New_York'); expect(range[1]).toBeNull(); }); it('should throw an error if a value in text is invalid', () => { expect(() => - DateUtils.parseDateRange('9999-99-99', 'America/New_York') + DateUtils.parseDateRange(dh, '9999-99-99', 'America/New_York') ).toThrowError(/Unable to extract date values from/i); }); }); @@ -254,7 +255,7 @@ describe('getJsDate', () => { }); it('returns a date object given a DateWrapper', () => { - const dateWrapper = DateUtils.makeDateWrapper('America/New_York', 2022); + const dateWrapper = DateUtils.makeDateWrapper(dh, 'America/New_York', 2022); expect(DateUtils.getJsDate(dateWrapper)).toEqual(dateWrapper.asDate()); }); }); diff --git a/packages/jsapi-utils/src/TableUtils.test.ts b/packages/jsapi-utils/src/TableUtils.test.ts index 35c0f67097..42fd943be0 100644 --- a/packages/jsapi-utils/src/TableUtils.test.ts +++ b/packages/jsapi-utils/src/TableUtils.test.ts @@ -1,4 +1,5 @@ -import dh, { +import dh from '@deephaven/jsapi-shim'; +import { Column, CustomColumn, DateWrapper, @@ -8,7 +9,7 @@ import dh, { Sort, Table, TreeTable, -} from '@deephaven/jsapi-shim'; +} from '@deephaven/jsapi-types'; import { Operator as FilterOperator, Type as FilterType, @@ -698,6 +699,7 @@ describe('quick filter tests', () => { it('should return a date filter if column type is date', () => { const [startValue] = DateUtils.parseDateRange( + dh, '2022-11-12', DEFAULT_TIME_ZONE_ID ); @@ -2446,12 +2448,14 @@ describe('makeValue', () => { it('should return a DateWrapper object if columnType is date', () => { const now = new Date(Date.now()); const currentDate = DateUtils.makeDateWrapper( + dh, 'America/New_York', now.getFullYear(), now.getMonth(), now.getDate() ); const yesterdayDate = DateUtils.makeDateWrapper( + dh, 'America/New_York', now.getFullYear(), now.getMonth(), From e74b67146386ed1fc084c52364580f46ebb5dc35 Mon Sep 17 00:00:00 2001 From: Vlad Babich Date: Wed, 3 May 2023 16:14:04 -0600 Subject: [PATCH 04/16] Unit tests, update StyleGuide --- packages/code-studio/src/styleguide/Grids.tsx | 73 ++++++++----------- .../src/PandasPlugin.tsx | 4 +- .../src/panels/IrisGridPanel.tsx | 49 +++++-------- packages/iris-grid/src/IrisGrid.tsx | 25 +++---- 4 files changed, 61 insertions(+), 90 deletions(-) diff --git a/packages/code-studio/src/styleguide/Grids.tsx b/packages/code-studio/src/styleguide/Grids.tsx index 51b81c9a27..27d07bf7bf 100644 --- a/packages/code-studio/src/styleguide/Grids.tsx +++ b/packages/code-studio/src/styleguide/Grids.tsx @@ -1,12 +1,13 @@ -import React, { PureComponent } from 'react'; +import React, { ReactElement, useState } from 'react'; import { Grid, + GridThemeType, MockGridModel, MockTreeGridModel, ThemeContext, - GridThemeType, } from '@deephaven/grid'; import { IrisGrid } from '@deephaven/iris-grid'; +import { useApi } from '@deephaven/jsapi-bootstrap'; import MockIrisGridTreeModel from './MockIrisGridTreeModel'; import StaticExample from './grid-examples/StaticExample'; import QuadrillionExample from './grid-examples/QuadrillionExample'; @@ -14,40 +15,30 @@ import TreeExample from './grid-examples/TreeExample'; import AsyncExample from './grid-examples/AsyncExample'; import DataBarExample from './grid-examples/DataBarExample'; -type GridsState = { - irisGridModel: MockIrisGridTreeModel; - model: MockGridModel; - theme: Partial; - contextTheme: Partial; -}; - -class Grids extends PureComponent, GridsState> { - constructor(props: Record) { - super(props); - - this.state = { - irisGridModel: new MockIrisGridTreeModel(new MockTreeGridModel()), - model: new MockGridModel(), - theme: { autoSelectRow: true }, - contextTheme: { rowHeight: 40 }, - }; - } +function Grids(): ReactElement { + const [irisGridModel] = useState( + new MockIrisGridTreeModel(new MockTreeGridModel()) + ); + const [model] = useState(new MockGridModel()); + const [theme] = useState>({ autoSelectRow: true }); + const [contextTheme] = useState>({ rowHeight: 40 }); + const dh = useApi(); - render(): React.ReactElement { - const { contextTheme, irisGridModel, model, theme } = this.state; - - return ( -
- -

Grid

-
- -
-

Static Data

-
- -
-
+ return ( +
+ +

Grid

+
+ +
+

Static Data

+
+ +
+

Data Bar

+
+ +

Quadrillion rows and columns

@@ -62,15 +53,11 @@ class Grids extends PureComponent, GridsState> {

Iris Grid

- -
-

Data Bar

-
- +
-
- ); - } + +
+ ); } export default Grids; diff --git a/packages/dashboard-core-plugins/src/PandasPlugin.tsx b/packages/dashboard-core-plugins/src/PandasPlugin.tsx index c2628d5056..4f16cb7ef1 100644 --- a/packages/dashboard-core-plugins/src/PandasPlugin.tsx +++ b/packages/dashboard-core-plugins/src/PandasPlugin.tsx @@ -8,10 +8,10 @@ import { useListener, } from '@deephaven/dashboard'; import { IrisGridModelFactory } from '@deephaven/iris-grid'; -import { Table } from '@deephaven/jsapi-shim'; +import { Table } from '@deephaven/jsapi-types'; +import { TableUtils } from '@deephaven/jsapi-utils'; import shortid from 'shortid'; import { PandasPanel, PandasPanelProps } from './panels'; -import { TableUtils } from '@deephaven/jsapi-utils'; export type PandasPluginProps = Partial & { hydrate: PanelHydrateFunction; diff --git a/packages/dashboard-core-plugins/src/panels/IrisGridPanel.tsx b/packages/dashboard-core-plugins/src/panels/IrisGridPanel.tsx index 61e22e8d74..3fcd588443 100644 --- a/packages/dashboard-core-plugins/src/panels/IrisGridPanel.tsx +++ b/packages/dashboard-core-plugins/src/panels/IrisGridPanel.tsx @@ -61,13 +61,13 @@ import { PromiseUtils, } from '@deephaven/utils'; import { ContextAction, ContextMenuRoot } from '@deephaven/components'; -import defaultDh, { +import { Column, - dhType, + dh as DhType, FilterCondition, Sort, VariableTypeUnion, -} from '@deephaven/jsapi-shim'; +} from '@deephaven/jsapi-types'; import { GridRangeIndex, GridState, @@ -126,7 +126,7 @@ export interface PanelState { export interface IrisGridPanelProps { children?: ReactNode; - dh?: dhType; + dh: DhType; glContainer: Container; glEventHub: EventEmitter; metadata: Metadata; @@ -209,7 +209,6 @@ export class IrisGridPanel extends PureComponent< static defaultProps = { onStateChange: (): void => undefined, onPanelStateUpdate: (): void => undefined, - dh: defaultDh, }; static displayName = 'IrisGridPanel'; @@ -247,10 +246,11 @@ export class IrisGridPanel extends PureComponent< this.irisGrid = React.createRef(); this.pluginRef = React.createRef(); - const { panelState, dh = defaultDh } = props; + const { panelState, dh } = props; this.pluginState = null; - this.tableUtils = new TableUtils(dh); + this.dh = dh; + this.irisGridUtils = new IrisGridUtils(dh); this.state = { error: null, @@ -345,7 +345,9 @@ export class IrisGridPanel extends PureComponent< pluginState: unknown; - tableUtils: TableUtils; + private dh: DhType; + + private irisGridUtils: IrisGridUtils; getTableName(): string { const { metadata } = this.props; @@ -445,7 +447,6 @@ export class IrisGridPanel extends PureComponent< getDehydratedIrisGridState = memoize( ( - dh: dhType, model: IrisGridModel, sorts: readonly Sort[], advancedFilters: ReadonlyAdvancedFilterMap, @@ -468,7 +469,7 @@ export class IrisGridPanel extends PureComponent< conditionalFormats: readonly SidebarFormattingRule[], columnHeaderGroups: readonly ColumnHeaderGroup[] ) => - IrisGridUtils.dehydrateIrisGridState(dh, model, { + this.irisGridUtils.dehydrateIrisGridState(model, { advancedFilters, aggregationSettings, customColumnFormatMap, @@ -614,9 +615,8 @@ export class IrisGridPanel extends PureComponent< const { model } = this.state; assertNotNull(model); const { columns, formatter } = model; - const pluginFilters = IrisGridUtils.getFiltersFromInputFilters( + const pluginFilters = this.irisGridUtils.getFiltersFromInputFilters( columns, - this.tableUtils, filters, formatter.timeZone ); @@ -949,17 +949,14 @@ export class IrisGridPanel extends PureComponent< irisGrid.clearAllFilters(); irisGrid.setFilters({ - quickFilters: IrisGridUtils.hydrateQuickFilters( + quickFilters: this.irisGridUtils.hydrateQuickFilters( columns, indexedQuickFilters, - this.tableUtils, formatter.timeZone ), - advancedFilters: IrisGridUtils.hydrateAdvancedFilters( - dh, + advancedFilters: this.irisGridUtils.hydrateAdvancedFilters( columns, indexedAdvancedFilters, - this.tableUtils, formatter.timeZone ), }); @@ -1048,15 +1045,10 @@ export class IrisGridPanel extends PureComponent< frozenColumns, conditionalFormats, columnHeaderGroups, - } = IrisGridUtils.hydrateIrisGridState( - dh, - model, - { - ...irisGridState, - ...irisGridStateOverrides, - }, - this.tableUtils - ); + } = this.irisGridUtils.hydrateIrisGridState(model, { + ...irisGridState, + ...irisGridStateOverrides, + }); const { isStuckToBottom, isStuckToRight, @@ -1105,7 +1097,7 @@ export class IrisGridPanel extends PureComponent< savePanelState = debounce(() => { const { irisGridState, gridState, pluginState } = this; assertNotNull(irisGridState); - const { onPanelStateUpdate, dh } = this.props; + const { onPanelStateUpdate } = this.props; const { model, panelState: oldPanelState, @@ -1155,9 +1147,6 @@ export class IrisGridPanel extends PureComponent< advancedSettings ), this.getDehydratedIrisGridState( - // dh is set to defaultDh in defaultProps - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - dh!, model, sorts, advancedFilters, diff --git a/packages/iris-grid/src/IrisGrid.tsx b/packages/iris-grid/src/IrisGrid.tsx index 15bfb4ba86..09a1847062 100644 --- a/packages/iris-grid/src/IrisGrid.tsx +++ b/packages/iris-grid/src/IrisGrid.tsx @@ -62,17 +62,16 @@ import { vsTools, } from '@deephaven/icons'; import { - dh as defaultDh, - dhType, Column, ColumnGroup, CustomColumn, DateWrapper, + dh as DhType, FilterCondition, Sort, Table, TableViewportSubscription, -} from '@deephaven/jsapi-shim'; +} from '@deephaven/jsapi-types'; import { DateUtils, Formatter, @@ -253,7 +252,7 @@ export type FilterMap = Map< >; export interface IrisGridProps { children: React.ReactNode; - dh: dhType; + dh: DhType; advancedFilters: ReadonlyAdvancedFilterMap; advancedSettings: Map; alwaysFetchColumns: readonly ColumnName[]; @@ -441,7 +440,6 @@ export class IrisGrid extends Component { static defaultProps = { children: null, - dh: defaultDh, advancedFilters: EMPTY_MAP, advancedSettings: EMPTY_MAP, alwaysFetchColumns: EMPTY_ARRAY, @@ -1032,8 +1030,7 @@ export class IrisGrid extends Component { column: Column, advancedFilterOptions: AdvancedFilterOptions | undefined, sortDirection: SortDirection | undefined, - formatter: Formatter, - tableUtils: TableUtils + formatter: Formatter ) => ( { onDone={this.handleAdvancedFilterDone} options={advancedFilterOptions} sortDirection={sortDirection} - // TODO: use formatter and tableUtils from the model? formatter={formatter} - tableUtils={tableUtils} + tableUtils={this.tableUtils} /> ), { max: 50 } @@ -1362,7 +1358,7 @@ export class IrisGrid extends Component { rowIndex: GridRangeIndex, rawValue = false ): string | unknown { - const { model } = this.props; + const { dh, model } = this.props; const modelColumn = this.getModelColumn(columnIndex); const modelRow = this.getModelRow(rowIndex); if (rawValue && modelColumn != null && modelRow != null) { @@ -1928,6 +1924,7 @@ export class IrisGrid extends Component { } updatePartition(partition: string, partitionColumn: Column): void { + const { dh } = this.props; const partitionFilter = partitionColumn .filter() .eq(dh.FilterValue.ofString(partition)); @@ -2473,7 +2470,7 @@ export class IrisGrid extends Component { } isTableSearchAvailable(): boolean { - const { model, canToggleSearch } = this.props; + const { dh, model, canToggleSearch } = this.props; const searchDisplayMode = model?.layoutHints?.searchDisplayMode; if (searchDisplayMode === dh.SearchDisplayMode?.SEARCH_DISPLAY_HIDE) { @@ -3304,7 +3301,7 @@ export class IrisGrid extends Component { gotoValueSelectedColumnName: selectedColumnName, gotoValueSelectedFilter, } = this.state; - const { model, dh } = this.props; + const { dh, model } = this.props; if (!model.isSeekRowAvailable) { return; } @@ -4227,9 +4224,7 @@ export class IrisGrid extends Component { column, advancedFilterOptions, sortDirection, - formatter, - // TODO: - this.tableUtils + formatter )} From 7a9e0425e36102c6f0da738692b0b1c73815e320 Mon Sep 17 00:00:00 2001 From: Vlad Babich Date: Wed, 3 May 2023 16:24:07 -0600 Subject: [PATCH 05/16] Fix unit tests --- packages/iris-grid/README.md | 4 +++- packages/iris-grid/src/IrisGrid.test.tsx | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/iris-grid/README.md b/packages/iris-grid/README.md index 6da5b7a60d..b577184383 100644 --- a/packages/iris-grid/README.md +++ b/packages/iris-grid/README.md @@ -11,11 +11,13 @@ npm install --save @deephaven/iris-grid Then, import and use the component from the package: ``` +import { useApi } from '@deephaven/jsapi-bootstrap'; import { IrisGrid, IrisGridModelFactory } from '@deephaven/iris-grid'; // In your initialization, create the model async const model = await IrisGridModelFactory.makeModel(table); +const dh = useApi(); // In your render function - + ``` \ No newline at end of file diff --git a/packages/iris-grid/src/IrisGrid.test.tsx b/packages/iris-grid/src/IrisGrid.test.tsx index e87168e7f2..ed34a65eed 100644 --- a/packages/iris-grid/src/IrisGrid.test.tsx +++ b/packages/iris-grid/src/IrisGrid.test.tsx @@ -57,7 +57,7 @@ function makeComponent( settings = DEFAULT_SETTINGS ) { const testRenderer = TestRenderer.create( - , + , { createNodeMock, } From ac956f61bced01a53197273776107616886bff79 Mon Sep 17 00:00:00 2001 From: Vlad Babich Date: Wed, 3 May 2023 17:07:30 -0600 Subject: [PATCH 06/16] Fix jsapi-types --- .../iris-grid/src/AdvancedFilterCreator.tsx | 2 +- .../src/AdvancedFilterCreatorSelectValue.tsx | 1 + .../AdvancedFilterCreatorSelectValueList.tsx | 17 +++- packages/iris-grid/src/ColumnStatistics.tsx | 2 +- packages/iris-grid/src/CommonTypes.tsx | 2 +- packages/iris-grid/src/CrossColumnSearch.tsx | 2 +- packages/iris-grid/src/GotoRow.tsx | 2 +- packages/iris-grid/src/IrisGrid.tsx | 5 +- .../iris-grid/src/IrisGridCopyHandler.tsx | 2 +- .../iris-grid/src/IrisGridMetricCalculator.ts | 2 +- packages/iris-grid/src/IrisGridModel.ts | 4 +- .../iris-grid/src/IrisGridModelFactory.ts | 2 +- .../iris-grid/src/IrisGridModelUpdater.tsx | 9 +- .../src/IrisGridPartitionSelector.tsx | 2 +- packages/iris-grid/src/IrisGridRenderer.ts | 2 +- packages/iris-grid/src/IrisGridTableModel.ts | 6 +- .../src/IrisGridTableModelTemplate.ts | 23 +++-- .../iris-grid/src/IrisGridTreeTableModel.ts | 2 +- .../src/PartitionSelectorSearch.test.tsx | 3 +- .../iris-grid/src/PartitionSelectorSearch.tsx | 2 +- .../iris-grid/src/TableViewportUpdater.tsx | 2 +- .../src/TreeTableViewportUpdater.tsx | 8 +- .../IrisGridColumnSelectMouseHandler.ts | 2 +- .../IrisGridContextMenuHandler.tsx | 97 +++++++++---------- .../iris-grid/src/sidebar/ChartBuilder.tsx | 8 +- packages/iris-grid/src/sidebar/RollupRows.tsx | 2 +- .../src/sidebar/SelectDistinctBuilder.tsx | 2 +- .../src/sidebar/TableCsvExporter.test.tsx | 1 + .../src/sidebar/TableCsvExporter.tsx | 13 ++- packages/iris-grid/src/sidebar/TableSaver.tsx | 9 +- .../sidebar/aggregations/AggregationEdit.tsx | 2 +- .../sidebar/aggregations/AggregationUtils.ts | 2 +- .../ConditionalFormattingAPIUtils.ts | 2 +- .../ConditionalFormattingUtils.test.ts | 2 +- .../ConditionalFormattingUtils.ts | 2 +- .../VisibilityOrderingBuilder.tsx | 2 +- .../sortable-tree/utilities.ts | 2 +- packages/jsapi-utils/src/DateUtils.ts | 9 +- packages/jsapi-utils/src/TableUtils.ts | 6 +- 39 files changed, 150 insertions(+), 115 deletions(-) diff --git a/packages/iris-grid/src/AdvancedFilterCreator.tsx b/packages/iris-grid/src/AdvancedFilterCreator.tsx index 76f663df48..2a972ce411 100644 --- a/packages/iris-grid/src/AdvancedFilterCreator.tsx +++ b/packages/iris-grid/src/AdvancedFilterCreator.tsx @@ -21,7 +21,7 @@ import { import { Button, ContextActionUtils } from '@deephaven/components'; import Log from '@deephaven/log'; import { CancelablePromise, PromiseUtils } from '@deephaven/utils'; -import { Column, FilterCondition, Table } from '@deephaven/jsapi-shim'; +import { Column, FilterCondition, Table } from '@deephaven/jsapi-types'; import shortid from 'shortid'; import AdvancedFilterCreatorFilterItem from './AdvancedFilterCreatorFilterItem'; import AdvancedFilterCreatorSelectValue from './AdvancedFilterCreatorSelectValue'; diff --git a/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx b/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx index 5d4e9ddeda..09a3ae2748 100644 --- a/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx +++ b/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx @@ -292,6 +292,7 @@ class AdvancedFilterCreatorSelectValue extends PureComponent< )} { + dh: DhType; selectedValues: T[]; table?: Table; filters: FilterCondition[]; @@ -73,9 +74,10 @@ class AdvancedFilterCreatorSelectValueList extends PureComponent< this.handleSelectionUpdate = this.handleSelectionUpdate.bind(this); this.handleTableUpdate = this.handleTableUpdate.bind(this); - this.list = null; + const { selectedValues, dh } = this.props; - const { selectedValues } = this.props; + this.dh = dh; + this.list = null; this.state = { itemCount: 0, @@ -120,6 +122,8 @@ class AdvancedFilterCreatorSelectValueList extends PureComponent< if (table) this.stopListening(table); } + dh: DhType; + list: SelectValueList | null; handleSelect(itemIndex: number, value: T): void { @@ -207,11 +211,14 @@ class AdvancedFilterCreatorSelectValueList extends PureComponent< } startListening(table: Table): void { - table.addEventListener(dh.Table.EVENT_UPDATED, this.handleTableUpdate); + table.addEventListener(this.dh.Table.EVENT_UPDATED, this.handleTableUpdate); } stopListening(table: Table): void { - table.removeEventListener(dh.Table.EVENT_UPDATED, this.handleTableUpdate); + table.removeEventListener( + this.dh.Table.EVENT_UPDATED, + this.handleTableUpdate + ); } resetViewport(): void { diff --git a/packages/iris-grid/src/ColumnStatistics.tsx b/packages/iris-grid/src/ColumnStatistics.tsx index b360a26b74..61e96c0624 100644 --- a/packages/iris-grid/src/ColumnStatistics.tsx +++ b/packages/iris-grid/src/ColumnStatistics.tsx @@ -6,7 +6,7 @@ import { dhFreeze, dhRefresh, vsLock } from '@deephaven/icons'; import { Column, ColumnStatistics as APIColumnStatistics, -} from '@deephaven/jsapi-shim'; +} from '@deephaven/jsapi-types'; import Log from '@deephaven/log'; import { CancelablePromise, PromiseUtils } from '@deephaven/utils'; import { isExpandableGridModel } from '@deephaven/grid'; diff --git a/packages/iris-grid/src/CommonTypes.tsx b/packages/iris-grid/src/CommonTypes.tsx index a9dbe9132f..444a082caa 100644 --- a/packages/iris-grid/src/CommonTypes.tsx +++ b/packages/iris-grid/src/CommonTypes.tsx @@ -4,7 +4,7 @@ import { TotalsTableConfig, FilterCondition, Format, -} from '@deephaven/jsapi-shim'; +} from '@deephaven/jsapi-types'; import { Shortcut } from '@deephaven/components'; import { IconDefinition } from '@deephaven/icons'; import AggregationOperation from './sidebar/aggregations/AggregationOperation'; diff --git a/packages/iris-grid/src/CrossColumnSearch.tsx b/packages/iris-grid/src/CrossColumnSearch.tsx index 0386794f95..47be5c8ab0 100644 --- a/packages/iris-grid/src/CrossColumnSearch.tsx +++ b/packages/iris-grid/src/CrossColumnSearch.tsx @@ -9,7 +9,7 @@ import { dhWarningCircleFilled, vsCircleLargeFilled, } from '@deephaven/icons'; -import dh, { Column, FilterCondition } from '@deephaven/jsapi-shim'; +import { Column, dh as DhType, FilterCondition } from '@deephaven/jsapi-types'; import { TableUtils } from '@deephaven/jsapi-utils'; import './CrossColumnSearch.scss'; import { ColumnName } from './CommonTypes'; diff --git a/packages/iris-grid/src/GotoRow.tsx b/packages/iris-grid/src/GotoRow.tsx index f96dc9379e..5de62d200d 100644 --- a/packages/iris-grid/src/GotoRow.tsx +++ b/packages/iris-grid/src/GotoRow.tsx @@ -8,7 +8,7 @@ import React, { useRef, useState, } from 'react'; -import { Column } from '@deephaven/jsapi-shim'; +import { Column } from '@deephaven/jsapi-types'; import { Type as FilterType, TypeValue as FilterTypeValue, diff --git a/packages/iris-grid/src/IrisGrid.tsx b/packages/iris-grid/src/IrisGrid.tsx index 09a1847062..9fde313e91 100644 --- a/packages/iris-grid/src/IrisGrid.tsx +++ b/packages/iris-grid/src/IrisGrid.tsx @@ -719,7 +719,7 @@ export class IrisGrid extends Component { new IrisGridColumnTooltipMouseHandler(this), new IrisGridSortMouseHandler(this), new IrisGridFilterMouseHandler(this), - new IrisGridContextMenuHandler(this), + new IrisGridContextMenuHandler(this, dh), new IrisGridDataSelectMouseHandler(this), new PendingMouseHandler(this), ]; @@ -4347,6 +4347,7 @@ export class IrisGrid extends Component { case OptionType.TABLE_EXPORTER: return ( { /> {isVisible && ( { onExited={this.handleAnimationEnd} /> { this.tableSaver = tableSaver; }} diff --git a/packages/iris-grid/src/IrisGridCopyHandler.tsx b/packages/iris-grid/src/IrisGridCopyHandler.tsx index 4b92f850c7..5f009d8fa4 100644 --- a/packages/iris-grid/src/IrisGridCopyHandler.tsx +++ b/packages/iris-grid/src/IrisGridCopyHandler.tsx @@ -15,7 +15,7 @@ import { PromiseUtils, } from '@deephaven/utils'; import Log from '@deephaven/log'; -import { Column } from '@deephaven/jsapi-shim'; +import { Column } from '@deephaven/jsapi-types'; import IrisGridUtils from './IrisGridUtils'; import IrisGridBottomBar from './IrisGridBottomBar'; import './IrisGridCopyHandler.scss'; diff --git a/packages/iris-grid/src/IrisGridMetricCalculator.ts b/packages/iris-grid/src/IrisGridMetricCalculator.ts index bab984f191..bed3440b7d 100644 --- a/packages/iris-grid/src/IrisGridMetricCalculator.ts +++ b/packages/iris-grid/src/IrisGridMetricCalculator.ts @@ -1,6 +1,6 @@ import { GridMetricCalculator, ModelSizeMap } from '@deephaven/grid'; import type { GridMetricState } from '@deephaven/grid'; -import type { FilterCondition, Sort } from '@deephaven/jsapi-shim'; +import type { FilterCondition, Sort } from '@deephaven/jsapi-types'; import { TableUtils } from '@deephaven/jsapi-utils'; import type IrisGridModel from './IrisGridModel'; import { IrisGridThemeType } from './IrisGridTheme'; diff --git a/packages/iris-grid/src/IrisGridModel.ts b/packages/iris-grid/src/IrisGridModel.ts index 901731e54e..832bbf13fd 100644 --- a/packages/iris-grid/src/IrisGridModel.ts +++ b/packages/iris-grid/src/IrisGridModel.ts @@ -7,7 +7,7 @@ import { MoveOperation, VisibleIndex, } from '@deephaven/grid'; -import type { +import { Column, ColumnStatistics, CustomColumn, @@ -19,7 +19,7 @@ import type { Sort, Table, ValueTypeUnion, -} from '@deephaven/jsapi-shim'; +} from '@deephaven/jsapi-types'; import { Formatter } from '@deephaven/jsapi-utils'; import { ColumnName, diff --git a/packages/iris-grid/src/IrisGridModelFactory.ts b/packages/iris-grid/src/IrisGridModelFactory.ts index 27ec4e01bb..d23386de7f 100644 --- a/packages/iris-grid/src/IrisGridModelFactory.ts +++ b/packages/iris-grid/src/IrisGridModelFactory.ts @@ -1,4 +1,4 @@ -import { Table, TreeTable } from '@deephaven/jsapi-shim'; +import { Table, TreeTable } from '@deephaven/jsapi-types'; import { Formatter, TableUtils } from '@deephaven/jsapi-utils'; import IrisGridModel from './IrisGridModel'; import IrisGridProxyModel from './IrisGridProxyModel'; diff --git a/packages/iris-grid/src/IrisGridModelUpdater.tsx b/packages/iris-grid/src/IrisGridModelUpdater.tsx index 375d77c2df..809347d256 100644 --- a/packages/iris-grid/src/IrisGridModelUpdater.tsx +++ b/packages/iris-grid/src/IrisGridModelUpdater.tsx @@ -1,13 +1,14 @@ /* eslint-disable react/require-default-props */ /* eslint-disable no-param-reassign */ import React, { useEffect, useMemo } from 'react'; -import dh, { +import { Column, CustomColumn, + dh as DhType, FilterCondition, RollupConfig, Sort, -} from '@deephaven/jsapi-shim'; +} from '@deephaven/jsapi-types'; import { ModelIndex, MoveOperation } from '@deephaven/grid'; import { Formatter, ReverseType, TableUtils } from '@deephaven/jsapi-utils'; import { EMPTY_ARRAY, EMPTY_MAP } from '@deephaven/utils'; @@ -19,6 +20,7 @@ import type ColumnHeaderGroup from './ColumnHeaderGroup'; const COLUMN_BUFFER_PAGES = 1; interface IrisGridModelUpdaterProps { + dh: DhType; model: IrisGridModel; modelColumns: readonly Column[]; top: number; @@ -48,6 +50,7 @@ interface IrisGridModelUpdaterProps { */ const IrisGridModelUpdater = React.memo( ({ + dh, model, modelColumns, top, @@ -106,7 +109,7 @@ const IrisGridModelUpdater = React.memo( } model.sort = sortsForModel; }, - [model, sorts, reverseType] + [dh, model, sorts, reverseType] ); useEffect( function updateFormatter() { diff --git a/packages/iris-grid/src/IrisGridPartitionSelector.tsx b/packages/iris-grid/src/IrisGridPartitionSelector.tsx index ab3fa90e05..5e9155ca6e 100644 --- a/packages/iris-grid/src/IrisGridPartitionSelector.tsx +++ b/packages/iris-grid/src/IrisGridPartitionSelector.tsx @@ -4,7 +4,7 @@ import { DropdownMenu, Tooltip } from '@deephaven/components'; import { vsTriangleDown, vsClose } from '@deephaven/icons'; import Log from '@deephaven/log'; import debounce from 'lodash.debounce'; -import { Table } from '@deephaven/jsapi-shim'; +import { Table } from '@deephaven/jsapi-types'; import PartitionSelectorSearch from './PartitionSelectorSearch'; import './IrisGridPartitionSelector.scss'; import { ColumnName } from './CommonTypes'; diff --git a/packages/iris-grid/src/IrisGridRenderer.ts b/packages/iris-grid/src/IrisGridRenderer.ts index fa12dc155d..f7c87e0e83 100644 --- a/packages/iris-grid/src/IrisGridRenderer.ts +++ b/packages/iris-grid/src/IrisGridRenderer.ts @@ -13,7 +13,7 @@ import { GridUtils, VisibleIndex, } from '@deephaven/grid'; -import { Sort } from '@deephaven/jsapi-shim'; +import { Sort } from '@deephaven/jsapi-types'; import { TableUtils, ReverseType } from '@deephaven/jsapi-utils'; import { assertNotNull, getOrThrow } from '@deephaven/utils'; import { diff --git a/packages/iris-grid/src/IrisGridTableModel.ts b/packages/iris-grid/src/IrisGridTableModel.ts index afcc466182..16520976cd 100644 --- a/packages/iris-grid/src/IrisGridTableModel.ts +++ b/packages/iris-grid/src/IrisGridTableModel.ts @@ -5,6 +5,7 @@ import { Column, ColumnStatistics, CustomColumn, + dh as DhType, InputTable, LayoutHints, Table, @@ -36,17 +37,16 @@ class IrisGridTableModel extends IrisGridTableModelTemplate { /** * @param table Iris data table to be used in the model - * TODO: * @param formatter The formatter to use when getting formats * @param inputTable Iris input table associated with this table */ constructor( + dh: DhType, table: Table, - tableUtils: TableUtils, formatter = new Formatter(), inputTable: InputTable | null = null ) { - super(table, tableUtils, formatter, inputTable); + super(dh, table, formatter, inputTable); this.customColumnList = []; this.formatColumnList = []; } diff --git a/packages/iris-grid/src/IrisGridTableModelTemplate.ts b/packages/iris-grid/src/IrisGridTableModelTemplate.ts index 19a0d3eab5..a90de77783 100644 --- a/packages/iris-grid/src/IrisGridTableModelTemplate.ts +++ b/packages/iris-grid/src/IrisGridTableModelTemplate.ts @@ -11,10 +11,11 @@ import { MoveOperation, VisibleIndex, } from '@deephaven/grid'; -import dh, { +import { Column, ColumnStatistics, CustomColumn, + dh as DhType, FilterCondition, Format, InputTable, @@ -28,7 +29,7 @@ import dh, { TotalsTable, ValueTypeUnion, ViewportData, -} from '@deephaven/jsapi-shim'; +} from '@deephaven/jsapi-types'; import Log from '@deephaven/log'; import { CancelablePromise, @@ -140,10 +141,14 @@ class IrisGridTableModelTemplate< return this.table.columns; } - private irisFormatter: Formatter; + dh: DhType; + + irisGridUtils: IrisGridUtils; tableUtils: TableUtils; + private irisFormatter: Formatter; + inputTable: InputTable | null; private subscription: TableViewportSubscription | null; @@ -195,8 +200,8 @@ class IrisGridTableModelTemplate< * @param inputTable Iris input table associated with this table */ constructor( + dh: DhType, table: T, - tableUtils: TableUtils, formatter = new Formatter(), inputTable: InputTable | null = null ) { @@ -211,11 +216,13 @@ class IrisGridTableModelTemplate< this ); + this.dh = dh; this.irisFormatter = formatter; + this.irisGridUtils = new IrisGridUtils(dh); this.inputTable = inputTable; this.subscription = null; this.table = table; - this.tableUtils = tableUtils; + this.tableUtils = new TableUtils(dh); this.viewport = null; this.viewportData = null; this.formattedStringData = []; @@ -252,6 +259,8 @@ class IrisGridTableModelTemplate< startListening(): void { super.startListening(); + const { dh } = this; + this.table.addEventListener( dh.Table.EVENT_DISCONNECT, this.handleTableDisconnect @@ -280,6 +289,8 @@ class IrisGridTableModelTemplate< stopListening(): void { super.stopListening(); + const { dh } = this; + this.table.removeEventListener( dh.Table.EVENT_DISCONNECT, this.handleTableDisconnect @@ -1415,7 +1426,7 @@ class IrisGridTableModelTemplate< } if (tableRanges.length > 0) { - const rangeSet = IrisGridUtils.rangeSetFromRanges(dh, tableRanges); + const rangeSet = this.irisGridUtils.rangeSetFromRanges(tableRanges); const snapshot = await this.subscription.snapshot(rangeSet, columns); result.push( ...snapshot.rows.map(rowData => diff --git a/packages/iris-grid/src/IrisGridTreeTableModel.ts b/packages/iris-grid/src/IrisGridTreeTableModel.ts index 692f964c97..f180817a84 100644 --- a/packages/iris-grid/src/IrisGridTreeTableModel.ts +++ b/packages/iris-grid/src/IrisGridTreeTableModel.ts @@ -1,7 +1,7 @@ /* eslint class-methods-use-this: "off" */ import memoize from 'memoize-one'; import { GridRange, ModelIndex } from '@deephaven/grid'; -import { Column, TreeRow, TreeTable } from '@deephaven/jsapi-shim'; +import { Column, TreeRow, TreeTable } from '@deephaven/jsapi-types'; import { assertNotNull } from '@deephaven/utils'; import { UIRow, ColumnName } from './CommonTypes'; import IrisGridTableModelTemplate from './IrisGridTableModelTemplate'; diff --git a/packages/iris-grid/src/PartitionSelectorSearch.test.tsx b/packages/iris-grid/src/PartitionSelectorSearch.test.tsx index 577c0fd6b7..75de980eb3 100644 --- a/packages/iris-grid/src/PartitionSelectorSearch.test.tsx +++ b/packages/iris-grid/src/PartitionSelectorSearch.test.tsx @@ -1,7 +1,8 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import dh, { Table } from '@deephaven/jsapi-shim'; +import dh from '@deephaven/jsapi-shim'; +import { Table } from '@deephaven/jsapi-types'; import PartitionSelectorSearch from './PartitionSelectorSearch'; import IrisGridTestUtils from './IrisGridTestUtils'; diff --git a/packages/iris-grid/src/PartitionSelectorSearch.tsx b/packages/iris-grid/src/PartitionSelectorSearch.tsx index 6e732f3cf5..b459e97ea4 100644 --- a/packages/iris-grid/src/PartitionSelectorSearch.tsx +++ b/packages/iris-grid/src/PartitionSelectorSearch.tsx @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import debounce from 'lodash.debounce'; -import dh, { Table } from '@deephaven/jsapi-shim'; +import dh, { Table } from '@deephaven/jsapi-types'; import { ItemList, LoadingSpinner } from '@deephaven/components'; import Log from '@deephaven/log'; import { CanceledPromiseError } from '@deephaven/utils'; diff --git a/packages/iris-grid/src/TableViewportUpdater.tsx b/packages/iris-grid/src/TableViewportUpdater.tsx index c46c50430f..6379c50fb4 100644 --- a/packages/iris-grid/src/TableViewportUpdater.tsx +++ b/packages/iris-grid/src/TableViewportUpdater.tsx @@ -8,7 +8,7 @@ import { Sort, Table, TableViewportSubscription, -} from '@deephaven/jsapi-shim'; +} from '@deephaven/jsapi-types'; import Log from '@deephaven/log'; import { ColumnName } from './CommonTypes'; diff --git a/packages/iris-grid/src/TreeTableViewportUpdater.tsx b/packages/iris-grid/src/TreeTableViewportUpdater.tsx index 556b95e861..1a8ce9946d 100644 --- a/packages/iris-grid/src/TreeTableViewportUpdater.tsx +++ b/packages/iris-grid/src/TreeTableViewportUpdater.tsx @@ -1,12 +1,13 @@ import { PureComponent } from 'react'; import throttle from 'lodash.throttle'; -import dh, { +import { + dh as DhType, EventListener, FilterCondition, RemoverFn, Sort, Table, -} from '@deephaven/jsapi-shim'; +} from '@deephaven/jsapi-types'; import Log from '@deephaven/log'; const log = Log.module('TreeTableViewportUpdater'); @@ -14,6 +15,7 @@ const log = Log.module('TreeTableViewportUpdater'); const UPDATE_THROTTLE = 150; interface TreeTableViewportUpdaterProps { + dh: DhType; table: Table; top: number; bottom: number; @@ -91,7 +93,7 @@ class TreeTableViewportUpdater extends PureComponent< return; } - const { table, updateInterval } = this.props; + const { dh, table, updateInterval } = this.props; const viewSize = bottom - top; const viewportTop = Math.max(0, top - viewSize * 3); const viewportBottom = bottom + viewSize * 3 + 1; diff --git a/packages/iris-grid/src/mousehandlers/IrisGridColumnSelectMouseHandler.ts b/packages/iris-grid/src/mousehandlers/IrisGridColumnSelectMouseHandler.ts index 76e1bf1990..5f72a8107d 100644 --- a/packages/iris-grid/src/mousehandlers/IrisGridColumnSelectMouseHandler.ts +++ b/packages/iris-grid/src/mousehandlers/IrisGridColumnSelectMouseHandler.ts @@ -4,7 +4,7 @@ import { GridPoint, EventHandlerResult, } from '@deephaven/grid'; -import type { Column } from '@deephaven/jsapi-shim'; +import { Column } from '@deephaven/jsapi-types'; import { IrisGrid } from '../IrisGrid'; /** diff --git a/packages/iris-grid/src/mousehandlers/IrisGridContextMenuHandler.tsx b/packages/iris-grid/src/mousehandlers/IrisGridContextMenuHandler.tsx index a09ea84352..88d66ac820 100644 --- a/packages/iris-grid/src/mousehandlers/IrisGridContextMenuHandler.tsx +++ b/packages/iris-grid/src/mousehandlers/IrisGridContextMenuHandler.tsx @@ -25,12 +25,13 @@ import { isExpandableGridModel, ModelIndex, } from '@deephaven/grid'; -import dh, { +import { Column, + dh as DhType, FilterCondition, FilterValue, Sort, -} from '@deephaven/jsapi-shim'; +} from '@deephaven/jsapi-types'; import { TableColumnFormatter, DateTimeColumnFormatter, @@ -127,46 +128,6 @@ class IrisGridContextMenuHandler extends GridMouseHandler { return operator === '&&' ? 'And' : 'Or'; } - /** - * Gets an equality filter for the provided numeric value - * @param column The column to make the filter for - * @param value The value to get the equality filter for - */ - static getNumberValueEqualsFilter( - column: Column, - value: number - ): FilterCondition { - const columnFilter = column.filter(); - if (value === Number.POSITIVE_INFINITY) { - return dh.FilterCondition.invoke('isInf', columnFilter).and( - columnFilter.greaterThan(dh.FilterValue.ofNumber(0)) - ); - } - if (value === Number.NEGATIVE_INFINITY) { - return dh.FilterCondition.invoke('isInf', columnFilter).and( - columnFilter.lessThan(dh.FilterValue.ofNumber(0)) - ); - } - if (Number.isNaN(value)) { - return dh.FilterCondition.invoke('isNaN', columnFilter); - } - - const filterValue = IrisGridContextMenuHandler.getFilterValueForNumberOrChar( - column.type, - value - ); - return columnFilter.eq(filterValue); - } - - static getFilterValueForNumberOrChar( - columnType: string, - value: unknown - ): FilterValue { - return TableUtils.isCharType(columnType) - ? dh.FilterValue.ofString(String.fromCharCode(value as number)) - : dh.FilterValue.ofNumber(value); - } - static getRowOptionFormatted( command: string, cellValue: string, @@ -184,7 +145,7 @@ class IrisGridContextMenuHandler extends GridMouseHandler { (modelIndex: number, selectedFormat: TableColumnFormat | null) => void >; - constructor(irisGrid: IrisGrid) { + constructor(irisGrid: IrisGrid, dh: DhType) { super(); this.debouncedUpdateCustomFormat = debounce( @@ -192,6 +153,7 @@ class IrisGridContextMenuHandler extends GridMouseHandler { DEBOUNCE_UPDATE_FORMAT ); + this.dh = dh; this.irisGrid = irisGrid; } @@ -199,6 +161,8 @@ class IrisGridContextMenuHandler extends GridMouseHandler { this.debouncedUpdateCustomFormat.flush(); } + dh: DhType; + getHeaderActions( modelIndex: ModelIndex, gridPoint: GridPoint @@ -664,6 +628,42 @@ class IrisGridContextMenuHandler extends GridMouseHandler { return actions; } + /** + * Gets an equality filter for the provided numeric value + * @param column The column to make the filter for + * @param value The value to get the equality filter for + */ + getNumberValueEqualsFilter(column: Column, value: number): FilterCondition { + const { dh } = this; + const columnFilter = column.filter(); + if (value === Number.POSITIVE_INFINITY) { + return dh.FilterCondition.invoke('isInf', columnFilter).and( + columnFilter.greaterThan(dh.FilterValue.ofNumber(0)) + ); + } + if (value === Number.NEGATIVE_INFINITY) { + return dh.FilterCondition.invoke('isInf', columnFilter).and( + columnFilter.lessThan(dh.FilterValue.ofNumber(0)) + ); + } + if (Number.isNaN(value)) { + return dh.FilterCondition.invoke('isNaN', columnFilter); + } + + const filterValue = this.getFilterValueForNumberOrChar(column.type, value); + return columnFilter.eq(filterValue); + } + + getFilterValueForNumberOrChar( + columnType: string, + value: unknown + ): FilterValue { + const { dh } = this; + return TableUtils.isCharType(columnType) + ? dh.FilterValue.ofString(String.fromCharCode(value as number)) + : dh.FilterValue.ofNumber(value); + } + onContextMenu( gridPoint: GridPoint, grid: Grid, @@ -899,6 +899,7 @@ class IrisGridContextMenuHandler extends GridMouseHandler { quickFilter?: QuickFilter, operator?: '&&' | '||' | null ): ContextAction[] { + const { dh } = this; const filterValue = dh.FilterValue.ofString(value); let newQuickFilter: | { @@ -1065,10 +1066,7 @@ class IrisGridContextMenuHandler extends GridMouseHandler { quickFilter?: QuickFilter | null, operator?: '&&' | '||' | null ): ContextAction[] { - const filterValue = IrisGridContextMenuHandler.getFilterValueForNumberOrChar( - column.type, - value - ); + const filterValue = this.getFilterValueForNumberOrChar(column.type, value); let filter: FilterCondition | null = null; let filterText: string | null = null; if (quickFilter) { @@ -1099,7 +1097,7 @@ class IrisGridContextMenuHandler extends GridMouseHandler { title: 'is equal to', description: `Show only rows where ${column.name} is ${valueText}`, action: () => { - const valueFilter = IrisGridContextMenuHandler.getNumberValueEqualsFilter( + const valueFilter = this.getNumberValueEqualsFilter( column, value as number ); @@ -1124,7 +1122,7 @@ class IrisGridContextMenuHandler extends GridMouseHandler { title: 'is not equal to', description: `Show only rows where ${column.name} is not ${valueText}`, action: () => { - const valueFilter = IrisGridContextMenuHandler.getNumberValueEqualsFilter( + const valueFilter = this.getNumberValueEqualsFilter( column, value as number ).not(); @@ -1361,6 +1359,7 @@ class IrisGridContextMenuHandler extends GridMouseHandler { quickFilter?: QuickFilter | null, operator?: '&&' | '||' | null ): ContextAction[] { + const { dh } = this; const filterValue = dh.FilterValue.ofNumber(value); let filter: FilterCondition | null = null; diff --git a/packages/iris-grid/src/sidebar/ChartBuilder.tsx b/packages/iris-grid/src/sidebar/ChartBuilder.tsx index 7032281d44..de39533408 100644 --- a/packages/iris-grid/src/sidebar/ChartBuilder.tsx +++ b/packages/iris-grid/src/sidebar/ChartBuilder.tsx @@ -10,7 +10,7 @@ import { vsCircleLargeFilled, vsTrash, } from '@deephaven/icons'; -import { Column, dhType, SeriesPlotStyle } from '@deephaven/jsapi-shim'; +import { Column, dh as DhType, SeriesPlotStyle } from '@deephaven/jsapi-types'; import Log from '@deephaven/log'; import shortid from 'shortid'; import { @@ -37,7 +37,7 @@ export type SeriesItem = { }; interface ChartBuilderProps { - dh: dhType; + dh: DhType; model: IrisGridModel; onSubmit: (obj: ChartBuilderSettings) => void; onChange: (obj: ChartBuilderSettings) => void; @@ -59,7 +59,7 @@ interface ChartBuilderState { * Form for configuring all the settings when creating a console. */ class ChartBuilder extends PureComponent { - static getMaxSeriesCount(dh: dhType, type: SeriesPlotStyle): number { + static getMaxSeriesCount(dh: DhType, type: SeriesPlotStyle): number { switch (type) { case dh.plot.SeriesPlotStyle.PIE: return 1; @@ -75,7 +75,7 @@ class ChartBuilder extends PureComponent { } static makeDefaultSeriesItems( - dh: dhType, + dh: DhType, type: SeriesPlotStyle, columns: readonly Column[] ): SeriesItem[] { diff --git a/packages/iris-grid/src/sidebar/RollupRows.tsx b/packages/iris-grid/src/sidebar/RollupRows.tsx index 046d2f7bb0..a7fc4513d0 100644 --- a/packages/iris-grid/src/sidebar/RollupRows.tsx +++ b/packages/iris-grid/src/sidebar/RollupRows.tsx @@ -26,7 +26,7 @@ import debounce from 'lodash.debounce'; import Log from '@deephaven/log'; import { assertNotNull } from '@deephaven/utils'; import './RollupRows.scss'; -import { Column } from '@deephaven/jsapi-shim'; +import { Column } from '@deephaven/jsapi-types'; import IrisGridModel from '../IrisGridModel'; import { ColumnName } from '../CommonTypes'; diff --git a/packages/iris-grid/src/sidebar/SelectDistinctBuilder.tsx b/packages/iris-grid/src/sidebar/SelectDistinctBuilder.tsx index 69b96081d6..b839246eb2 100644 --- a/packages/iris-grid/src/sidebar/SelectDistinctBuilder.tsx +++ b/packages/iris-grid/src/sidebar/SelectDistinctBuilder.tsx @@ -4,7 +4,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { dhNewCircleLargeFilled, vsTrash } from '@deephaven/icons'; import { Button } from '@deephaven/components'; import Log from '@deephaven/log'; -import { Column } from '@deephaven/jsapi-shim'; +import { Column } from '@deephaven/jsapi-types'; import { ModelIndex } from '@deephaven/grid'; import IrisGridModel from '../IrisGridModel'; diff --git a/packages/iris-grid/src/sidebar/TableCsvExporter.test.tsx b/packages/iris-grid/src/sidebar/TableCsvExporter.test.tsx index 68b623e103..5f97b92658 100644 --- a/packages/iris-grid/src/sidebar/TableCsvExporter.test.tsx +++ b/packages/iris-grid/src/sidebar/TableCsvExporter.test.tsx @@ -35,6 +35,7 @@ function makeTableCsvExporterWrapper({ } = {}) { return render( Promise; isDownloading: boolean; onDownloadCompleted: () => void; @@ -511,7 +513,7 @@ export default class TableSaver extends PureComponent< convertSnapshotIntoCsv(snapshot: UpdateEventData): string { let csvString = ''; const snapshotIterator = snapshot.added.iterator(); - const { formatter } = this.props; + const { dh, formatter } = this.props; const rows = []; while (snapshotIterator.hasNext()) { @@ -576,6 +578,7 @@ export default class TableSaver extends PureComponent< if (n <= 0) { return; } + const { dh } = this.props; assertNotNull(this.gridRangeCounter); let i = 0; let currentGridRange = this.gridRanges[this.gridRangeCounter]; diff --git a/packages/iris-grid/src/sidebar/aggregations/AggregationEdit.tsx b/packages/iris-grid/src/sidebar/aggregations/AggregationEdit.tsx index 7bc720f329..17f98e1314 100644 --- a/packages/iris-grid/src/sidebar/aggregations/AggregationEdit.tsx +++ b/packages/iris-grid/src/sidebar/aggregations/AggregationEdit.tsx @@ -3,7 +3,7 @@ import classNames from 'classnames'; import { CSSTransition } from 'react-transition-group'; import { Button, Checkbox, ItemList, ThemeExport } from '@deephaven/components'; import { dhSortAlphaDown, dhSortAlphaUp } from '@deephaven/icons'; -import type { Column } from '@deephaven/jsapi-shim'; +import { Column } from '@deephaven/jsapi-types'; import { TableUtils } from '@deephaven/jsapi-utils'; import { Aggregation } from './Aggregations'; import { filterValidColumns } from './AggregationUtils'; diff --git a/packages/iris-grid/src/sidebar/aggregations/AggregationUtils.ts b/packages/iris-grid/src/sidebar/aggregations/AggregationUtils.ts index 04a7819841..2088e5eb9c 100644 --- a/packages/iris-grid/src/sidebar/aggregations/AggregationUtils.ts +++ b/packages/iris-grid/src/sidebar/aggregations/AggregationUtils.ts @@ -1,4 +1,4 @@ -import type { Column } from '@deephaven/jsapi-shim'; +import { Column } from '@deephaven/jsapi-types'; import { TableUtils } from '@deephaven/jsapi-utils'; import AggregationOperation from './AggregationOperation'; diff --git a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingAPIUtils.ts b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingAPIUtils.ts index 4170e00499..b6ddf57db0 100644 --- a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingAPIUtils.ts +++ b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingAPIUtils.ts @@ -1,4 +1,4 @@ -import { Column, CustomColumn } from '@deephaven/jsapi-shim'; +import { Column, CustomColumn } from '@deephaven/jsapi-types'; import { BaseFormatConfig, getConditionDBString, diff --git a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.test.ts b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.test.ts index 787be57c20..b3f97d7cb0 100644 --- a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.test.ts +++ b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.test.ts @@ -1,4 +1,4 @@ -import { Column } from '@deephaven/jsapi-shim'; +import { Column } from '@deephaven/jsapi-types'; import IrisGridTestUtils from '../../IrisGridTestUtils'; import { DateCondition, diff --git a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.ts b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.ts index 16376a1cfe..e896500f50 100644 --- a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.ts +++ b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.ts @@ -1,5 +1,5 @@ import Log from '@deephaven/log'; -import { Column, CustomColumn } from '@deephaven/jsapi-shim'; +import { Column, CustomColumn } from '@deephaven/jsapi-types'; import { DateUtils, TableUtils } from '@deephaven/jsapi-utils'; import { makeColumnFormatColumn, diff --git a/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilder.tsx b/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilder.tsx index 3bd22c9b7c..f8b4ed6be1 100644 --- a/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilder.tsx +++ b/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilder.tsx @@ -22,7 +22,7 @@ import { vsCircleLargeFilled, vsAdd, } from '@deephaven/icons'; -import type { Column } from '@deephaven/jsapi-shim'; +import { Column } from '@deephaven/jsapi-types'; import memoize from 'memoizee'; import debounce from 'lodash.debounce'; import { Button, SearchInput } from '@deephaven/components'; diff --git a/packages/iris-grid/src/sidebar/visibility-ordering-builder/sortable-tree/utilities.ts b/packages/iris-grid/src/sidebar/visibility-ordering-builder/sortable-tree/utilities.ts index a36628f42e..077ec66ace 100644 --- a/packages/iris-grid/src/sidebar/visibility-ordering-builder/sortable-tree/utilities.ts +++ b/packages/iris-grid/src/sidebar/visibility-ordering-builder/sortable-tree/utilities.ts @@ -1,5 +1,5 @@ import { arrayMove } from '@dnd-kit/sortable'; -import type { Column } from '@deephaven/jsapi-shim'; +import { Column } from '@deephaven/jsapi-types'; import { GridUtils, ModelIndex, MoveOperation } from '@deephaven/grid'; import type ColumnHeaderGroup from '../../../ColumnHeaderGroup'; import { isFlattenedTreeItem, ReadonlyTreeItems } from './types'; diff --git a/packages/jsapi-utils/src/DateUtils.ts b/packages/jsapi-utils/src/DateUtils.ts index eb6fea003e..590546b921 100644 --- a/packages/jsapi-utils/src/DateUtils.ts +++ b/packages/jsapi-utils/src/DateUtils.ts @@ -1,5 +1,4 @@ -import { dhType } from '@deephaven/jsapi-shim'; -import type { DateWrapper } from '@deephaven/jsapi-shim'; +import { DateWrapper, dh as DhType } from '@deephaven/jsapi-types'; interface DateParts { year: T; @@ -41,7 +40,7 @@ export class DateUtils { * @param ns The nanoseconds */ static makeDateWrapper( - dh: dhType, + dh: DhType, timeZone: string, year: number, month = 0, @@ -90,7 +89,7 @@ export class DateUtils { * @returns Returns the DateWrapper for the next date, or null if a full date was passed in */ static getNextDate( - dh: dhType, + dh: DhType, components: DateParts, values: DateParts, timeZone: string @@ -257,7 +256,7 @@ export class DateUtils { * @returns A tuple with the start and end value/null for that date range, or both null */ static parseDateRange( - dh: dhType, + dh: DhType, text: string, timeZone: string ): [DateWrapper, DateWrapper | null] | [null, null] { diff --git a/packages/jsapi-utils/src/TableUtils.ts b/packages/jsapi-utils/src/TableUtils.ts index 71e55d3c4d..a34145629f 100644 --- a/packages/jsapi-utils/src/TableUtils.ts +++ b/packages/jsapi-utils/src/TableUtils.ts @@ -8,6 +8,7 @@ import Log from '@deephaven/log'; import { Column, CustomColumn, + dh as DhType, FilterCondition, FilterValue, LongWrapper, @@ -16,7 +17,6 @@ import { Table, TreeTable, } from '@deephaven/jsapi-types'; -import { dhType } from '@deephaven/jsapi-shim'; import { CancelablePromise, PromiseUtils, @@ -831,9 +831,9 @@ export class TableUtils { }); } - dh: dhType; + dh: DhType; - constructor(dh: dhType) { + constructor(dh: DhType) { this.dh = dh; } From becc16b73546b65feb19a471cf319f129139d2d6 Mon Sep 17 00:00:00 2001 From: Vlad Babich Date: Wed, 3 May 2023 17:30:18 -0600 Subject: [PATCH 07/16] De-globalized CondtitionalFormattingEditor --- .../src/AdvancedFilterCreatorSelectValue.tsx | 5 ++-- packages/iris-grid/src/CrossColumnSearch.tsx | 1 + packages/iris-grid/src/GotoRow.tsx | 4 ++- packages/iris-grid/src/IrisGrid.tsx | 18 ++++++++++--- .../src/IrisGridPartitionSelector.tsx | 6 +++-- packages/iris-grid/src/IrisGridTableModel.ts | 2 +- .../src/PartitionSelectorSearch.test.tsx | 1 + .../iris-grid/src/PartitionSelectorSearch.tsx | 9 ++++--- .../src/TreeTableViewportUpdater.tsx | 4 +-- .../ColumnFormatEditor.tsx | 4 +++ .../ConditionEditor.tsx | 5 +++- .../ConditionalFormattingAPIUtils.ts | 4 +-- .../ConditionalFormattingUtils.test.ts | 27 ++++++++++--------- .../ConditionalFormattingUtils.ts | 11 +++++--- .../RowFormatEditor.tsx | 4 +++ 15 files changed, 71 insertions(+), 34 deletions(-) diff --git a/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx b/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx index 09a3ae2748..7448b4229c 100644 --- a/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx +++ b/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx @@ -3,13 +3,14 @@ import React, { PureComponent } from 'react'; import { CSSTransition } from 'react-transition-group'; import classNames from 'classnames'; import { Formatter, TableUtils } from '@deephaven/jsapi-utils'; -import { FilterCondition, Table } from '@deephaven/jsapi-types'; +import { dh as DhType, FilterCondition, Table } from '@deephaven/jsapi-types'; import { Button } from '@deephaven/components'; import AdvancedFilterCreatorSelectValueList from './AdvancedFilterCreatorSelectValueList'; import './AdvancedFilterCreatorSelectValue.scss'; import { ColumnName } from './CommonTypes'; interface AdvancedFilterCreatorSelectValueProps { + dh: DhType; invertSelection: boolean; selectedValues: T[]; table?: Table; @@ -267,7 +268,7 @@ class AdvancedFilterCreatorSelectValue extends PureComponent< searchText, table, } = this.state; - const { formatter, showSearch } = this.props; + const { dh, formatter, showSearch } = this.props; const columnName = this.getColumnName(); const displayedValuesText = this.getDisplayedValueText(); const placeholderText = columnName ? `Find ${columnName}...` : ''; diff --git a/packages/iris-grid/src/CrossColumnSearch.tsx b/packages/iris-grid/src/CrossColumnSearch.tsx index 47be5c8ab0..90423ababc 100644 --- a/packages/iris-grid/src/CrossColumnSearch.tsx +++ b/packages/iris-grid/src/CrossColumnSearch.tsx @@ -34,6 +34,7 @@ class CrossColumnSearch extends PureComponent< CrossColumnSearchState > { static createSearchFilter( + dh: DhType, searchValue: string, selectedColumns: readonly ColumnName[], columns: readonly Column[], diff --git a/packages/iris-grid/src/GotoRow.tsx b/packages/iris-grid/src/GotoRow.tsx index 5de62d200d..fb1a496b68 100644 --- a/packages/iris-grid/src/GotoRow.tsx +++ b/packages/iris-grid/src/GotoRow.tsx @@ -8,7 +8,7 @@ import React, { useRef, useState, } from 'react'; -import { Column } from '@deephaven/jsapi-types'; +import { dh as DhType, Column } from '@deephaven/jsapi-types'; import { Type as FilterType, TypeValue as FilterTypeValue, @@ -31,6 +31,7 @@ function isIrisGridProxyModel( const DEFAULT_FORMAT_STRING = '###,##0'; interface GotoRowProps { + dh: DhType; gotoRow: string; gotoRowError: string; gotoValueError: string; @@ -54,6 +55,7 @@ interface GotoRowProps { } function GotoRow({ + dh, gotoRow, gotoRowError, gotoValueError, diff --git a/packages/iris-grid/src/IrisGrid.tsx b/packages/iris-grid/src/IrisGrid.tsx index 9fde313e91..9090e952a7 100644 --- a/packages/iris-grid/src/IrisGrid.tsx +++ b/packages/iris-grid/src/IrisGrid.tsx @@ -744,6 +744,7 @@ export class IrisGrid extends Component { }); const searchColumns = selectedSearchColumns ?? []; const searchFilter = CrossColumnSearch.createSearchFilter( + dh, searchValue, searchColumns, model.columns, @@ -1224,8 +1225,11 @@ export class IrisGrid extends Component { ); getCachedFormatColumns = memoize( - (columns: readonly Column[], rules: readonly SidebarFormattingRule[]) => - getFormatColumns(columns, rules) + ( + dh: DhType, + columns: readonly Column[], + rules: readonly SidebarFormattingRule[] + ) => getFormatColumns(dh, columns, rules) ); /** @@ -1238,6 +1242,7 @@ export class IrisGrid extends Component { */ getCachedPreviewFormatColumns = memoize( ( + dh: DhType, columns: readonly Column[], rulesParam: readonly SidebarFormattingRule[], preview?: SidebarFormattingRule, @@ -1252,10 +1257,10 @@ export class IrisGrid extends Component { if (preview !== undefined && editIndex !== undefined) { const rules = [...rulesParam]; rules[editIndex] = preview; - return this.getCachedFormatColumns(columns, rules); + return this.getCachedFormatColumns(dh, columns, rules); } - return this.getCachedFormatColumns(columns, rulesParam); + return this.getCachedFormatColumns(dh, columns, rulesParam); } ); @@ -1850,6 +1855,7 @@ export class IrisGrid extends Component { initState(): void { const { applyInputFiltersOnInit, + dh, inputFilters, sorts, model, @@ -1862,6 +1868,7 @@ export class IrisGrid extends Component { const searchColumns = selectedSearchColumns ?? []; const searchFilter = CrossColumnSearch.createSearchFilter( + dh, searchValue, searchColumns, model.columns, @@ -2301,7 +2308,9 @@ export class IrisGrid extends Component { columns: readonly Column[], invertSearchColumns: boolean ): void => { + const { dh } = this.props; const searchFilter = CrossColumnSearch.createSearchFilter( + dh, searchValue, selectedSearchColumns, columns, @@ -4499,6 +4508,7 @@ export class IrisGrid extends Component { this.grid?.state.draggingColumn?.range )} formatColumns={this.getCachedPreviewFormatColumns( + dh, model.columns, conditionalFormats, conditionalFormatPreview, diff --git a/packages/iris-grid/src/IrisGridPartitionSelector.tsx b/packages/iris-grid/src/IrisGridPartitionSelector.tsx index 5e9155ca6e..569b3e6b54 100644 --- a/packages/iris-grid/src/IrisGridPartitionSelector.tsx +++ b/packages/iris-grid/src/IrisGridPartitionSelector.tsx @@ -4,7 +4,7 @@ import { DropdownMenu, Tooltip } from '@deephaven/components'; import { vsTriangleDown, vsClose } from '@deephaven/icons'; import Log from '@deephaven/log'; import debounce from 'lodash.debounce'; -import { Table } from '@deephaven/jsapi-types'; +import { dh as DhType, Table } from '@deephaven/jsapi-types'; import PartitionSelectorSearch from './PartitionSelectorSearch'; import './IrisGridPartitionSelector.scss'; import { ColumnName } from './CommonTypes'; @@ -13,6 +13,7 @@ const log = Log.module('IrisGridPartitionSelector'); const PARTITION_CHANGE_DEBOUNCE_MS = 250; interface IrisGridPartitionSelectorProps { + dh: dhType; getFormattedString: (value: T, type: string, name: string) => string; table: Table; columnName: ColumnName; @@ -155,10 +156,11 @@ class IrisGridPartitionSelector extends Component< } render(): JSX.Element { - const { columnName, getFormattedString, onDone, table } = this.props; + const { columnName, dh, getFormattedString, onDone, table } = this.props; const { partition } = this.state; const partitionSelectorSearch = ( { + dh: DhType; getFormattedString: (value: T, type: string, name: string) => string; table: Table; initialPageSize: number; @@ -255,7 +256,7 @@ class PartitionSelectorSearch extends Component< } startListening(): void { - const { initialPageSize, table } = this.props; + const { dh, initialPageSize, table } = this.props; table.addEventListener(dh.Table.EVENT_UPDATED, this.handleTableUpdate); table.addEventListener( dh.Table.EVENT_FILTERCHANGED, @@ -265,7 +266,7 @@ class PartitionSelectorSearch extends Component< } stopListening(): void { - const { table } = this.props; + const { dh, table } = this.props; table.removeEventListener(dh.Table.EVENT_UPDATED, this.handleTableUpdate); table.removeEventListener( dh.Table.EVENT_FILTERCHANGED, @@ -274,7 +275,7 @@ class PartitionSelectorSearch extends Component< } updateFilter(): void { - const { initialPageSize, table } = this.props; + const { dh, initialPageSize, table } = this.props; const { text } = this.state; const filterText = text.trim(); const filters = []; diff --git a/packages/iris-grid/src/TreeTableViewportUpdater.tsx b/packages/iris-grid/src/TreeTableViewportUpdater.tsx index 1a8ce9946d..a56daf66bc 100644 --- a/packages/iris-grid/src/TreeTableViewportUpdater.tsx +++ b/packages/iris-grid/src/TreeTableViewportUpdater.tsx @@ -93,7 +93,7 @@ class TreeTableViewportUpdater extends PureComponent< return; } - const { dh, table, updateInterval } = this.props; + const { table, updateInterval } = this.props; const viewSize = bottom - top; const viewportTop = Math.max(0, top - viewSize * 3); const viewportBottom = bottom + viewSize * 3 + 1; @@ -106,7 +106,7 @@ class TreeTableViewportUpdater extends PureComponent< ); if (!this.listenerCleanup) { - const { onViewportUpdate } = this.props; + const { dh, onViewportUpdate } = this.props; this.listenerCleanup = table.addEventListener( dh.TreeTable.EVENT_UPDATED, onViewportUpdate diff --git a/packages/iris-grid/src/sidebar/conditional-formatting/ColumnFormatEditor.tsx b/packages/iris-grid/src/sidebar/conditional-formatting/ColumnFormatEditor.tsx index 997020e7cc..ea0c359c5c 100644 --- a/packages/iris-grid/src/sidebar/conditional-formatting/ColumnFormatEditor.tsx +++ b/packages/iris-grid/src/sidebar/conditional-formatting/ColumnFormatEditor.tsx @@ -1,6 +1,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import Log from '@deephaven/log'; import { ComboBox } from '@deephaven/components'; +import { dh as DhType } from '@deephaven/jsapi-types'; import { BaseFormatConfig, ChangeCallback, @@ -18,6 +19,7 @@ const log = Log.module('ColumnFormatEditor'); export interface ColumnFormatEditorProps { columns: ModelColumn[]; config?: BaseFormatConfig; + dh: DhType; onChange?: ChangeCallback; } @@ -38,6 +40,7 @@ function ColumnFormatEditor(props: ColumnFormatEditorProps): JSX.Element { const { columns, config = makeDefaultConfig(columns), + dh, onChange = DEFAULT_CALLBACK, } = props; @@ -134,6 +137,7 @@ function ColumnFormatEditor(props: ColumnFormatEditorProps): JSX.Element { {selectedColumn !== undefined && ( <> void; @@ -253,7 +255,7 @@ function getCharInputs( } function ConditionEditor(props: ConditionEditorProps): JSX.Element { - const { column, config, onChange = DEFAULT_CALLBACK } = props; + const { column, config, dh, onChange = DEFAULT_CALLBACK } = props; const selectedColumnType = column.type; const [prevColumnType, setPrevColumnType] = useState(selectedColumnType); const [selectedCondition, setCondition] = useState(config.condition); @@ -341,6 +343,7 @@ function ConditionEditor(props: ConditionEditorProps): JSX.Element { } else if ( TableUtils.isDateType(column.type) && !isDateConditionValid( + dh, selectedCondition as DateCondition, conditionValue ) diff --git a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingAPIUtils.ts b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingAPIUtils.ts index b6ddf57db0..dcb91d54a2 100644 --- a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingAPIUtils.ts +++ b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingAPIUtils.ts @@ -1,4 +1,4 @@ -import { Column, CustomColumn } from '@deephaven/jsapi-types'; +import { Column, dh as DhType, CustomColumn } from '@deephaven/jsapi-types'; import { BaseFormatConfig, getConditionDBString, @@ -21,7 +21,7 @@ export function makeTernaryFormatRule( return `${conditionDBString} ? ${styleDBString} : ${prevRule}`; } -export function makeRowFormatColumn(rule: string): CustomColumn { +export function makeRowFormatColumn(dh: DhType, rule: string): CustomColumn { return dh.Column.formatRowColor(rule); } diff --git a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.test.ts b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.test.ts index b3f97d7cb0..0c9e38537d 100644 --- a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.test.ts +++ b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.test.ts @@ -1,3 +1,4 @@ +import dh from '@deephaven/jsapi-shim'; import { Column } from '@deephaven/jsapi-types'; import IrisGridTestUtils from '../../IrisGridTestUtils'; import { @@ -16,7 +17,7 @@ jest.mock('./ConditionalFormattingAPIUtils', () => ({ `${rule.column.name} - ${rule.style.type} : ${prevRule}` ), makeColumnFormatColumn: jest.fn((col, rule) => `[col] ${rule}`), - makeRowFormatColumn: jest.fn(rule => `[row] ${rule}`), + makeRowFormatColumn: jest.fn(dh, rule => `[row] ${rule}`), })); describe('getFormatColumns', () => { @@ -47,12 +48,12 @@ describe('getFormatColumns', () => { } it('returns empty array for empty rules array', () => { - expect(getFormatColumns(makeColumns(), [])).toEqual([]); + expect(getFormatColumns(dh, makeColumns(), [])).toEqual([]); }); it('returns mocked formatColumn for a given config', () => { expect( - getFormatColumns(makeColumns(), [ + getFormatColumns(dh, makeColumns(), [ makeFormatRule({ columnName: '0', styleType: FormatStyleType.POSITIVE, @@ -63,7 +64,7 @@ describe('getFormatColumns', () => { it('ignores rules referring to missing columns', () => { expect( - getFormatColumns(makeColumns(1), [ + getFormatColumns(dh, makeColumns(1), [ makeFormatRule({ columnName: '2', styleType: FormatStyleType.POSITIVE, @@ -82,7 +83,7 @@ describe('getFormatColumns', () => { it('stacks multiple rules for the same column in the correct order', () => { expect( - getFormatColumns(makeColumns(), [ + getFormatColumns(dh, makeColumns(), [ makeFormatRule({ columnName: '0', styleType: FormatStyleType.POSITIVE, @@ -99,7 +100,7 @@ describe('getFormatColumns', () => { it('returns one rule stack for each column', () => { expect( - getFormatColumns(makeColumns(), [ + getFormatColumns(dh, makeColumns(), [ makeFormatRule({ columnName: '0', styleType: FormatStyleType.POSITIVE, @@ -125,7 +126,7 @@ describe('getFormatColumns', () => { it('keeps column/row rule stacks based on the same column separate', () => { expect( - getFormatColumns(makeColumns(), [ + getFormatColumns(dh, makeColumns(), [ makeFormatRule({ columnName: '0', styleType: FormatStyleType.POSITIVE, @@ -155,7 +156,7 @@ describe('getFormatColumns', () => { it('handles rules with mixed column order correctly', () => { expect( - getFormatColumns(makeColumns(), [ + getFormatColumns(dh, makeColumns(), [ makeFormatRule({ columnName: '0', styleType: FormatStyleType.POSITIVE, @@ -181,7 +182,7 @@ describe('getFormatColumns', () => { it('returns a single condition for multiple rules on the same column', () => { expect( - getFormatColumns(makeColumns(), [ + getFormatColumns(dh, makeColumns(), [ makeFormatRule({ columnName: '0', formatterType: FormatterType.ROWS, @@ -200,7 +201,7 @@ describe('getFormatColumns', () => { it('returns a single condition for row rules on different columns', () => { expect( - getFormatColumns(makeColumns(), [ + getFormatColumns(dh, makeColumns(), [ makeFormatRule({ columnName: '0', formatterType: FormatterType.ROWS, @@ -251,7 +252,7 @@ describe('isDateConditionValid', () => { values.empty, values.undefined, ])('should ignore value when not required: %s', testValue => { - expect(isDateConditionValid(condition, testValue)).toBeTruthy(); + expect(isDateConditionValid(dh, condition, testValue)).toBeTruthy(); }); } ); @@ -268,7 +269,9 @@ describe('isDateConditionValid', () => { 'should return true only if value is valid date format: %s, %s', (testValues, expected) => { [testValues].flat().forEach(value => { - expect(isDateConditionValid(condition, value)).toEqual(expected); + expect(isDateConditionValid(dh, condition, value)).toEqual( + expected + ); }); } ); diff --git a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.ts b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.ts index e896500f50..2662094649 100644 --- a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.ts +++ b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.ts @@ -1,5 +1,5 @@ import Log from '@deephaven/log'; -import { Column, CustomColumn } from '@deephaven/jsapi-types'; +import { Column, CustomColumn, dh as DhType } from '@deephaven/jsapi-types'; import { DateUtils, TableUtils } from '@deephaven/jsapi-utils'; import { makeColumnFormatColumn, @@ -650,6 +650,7 @@ export function getShortLabelForConditionType( * @returns Array of format columns */ export function getFormatColumns( + dh: DhType, columns: readonly Column[], rules: readonly FormattingRule[] ): CustomColumn[] { @@ -694,7 +695,7 @@ export function getFormatColumns( const formatColumn = formatterType === FormatterType.CONDITIONAL ? makeColumnFormatColumn(col, rule) - : makeRowFormatColumn(rule); + : makeRowFormatColumn(dh, rule); result.push(formatColumn); if (formatterType === FormatterType.CONDITIONAL) { columnFormatConfigMap.set(col.name, [rule, formatColumn]); @@ -711,7 +712,11 @@ export function getFormatColumns( * @param condition * @param value */ -export function isDateConditionValid(condition: DateCondition, value?: string) { +export function isDateConditionValid( + dh: DhType, + condition: DateCondition, + value?: string +) { switch (condition) { case DateCondition.IS_NULL: case DateCondition.IS_NOT_NULL: diff --git a/packages/iris-grid/src/sidebar/conditional-formatting/RowFormatEditor.tsx b/packages/iris-grid/src/sidebar/conditional-formatting/RowFormatEditor.tsx index 27f4f441fe..d54219cf52 100644 --- a/packages/iris-grid/src/sidebar/conditional-formatting/RowFormatEditor.tsx +++ b/packages/iris-grid/src/sidebar/conditional-formatting/RowFormatEditor.tsx @@ -1,5 +1,6 @@ import React, { useCallback, useEffect, useState } from 'react'; import Log from '@deephaven/log'; +import { dh as DhType } from '@deephaven/jsapi-types'; import { ComboBox } from '@deephaven/components'; import { BaseFormatConfig, @@ -17,6 +18,7 @@ const log = Log.module('RowFormatEditor'); export interface RowFormatEditorProps { columns: ModelColumn[]; config?: BaseFormatConfig; + dh: DhType; onChange?: ChangeCallback; } @@ -37,6 +39,7 @@ function RowFormatEditor(props: RowFormatEditorProps): JSX.Element { const { columns, config = makeDefaultConfig(columns), + dh, onChange = DEFAULT_CALLBACK, } = props; @@ -129,6 +132,7 @@ function RowFormatEditor(props: RowFormatEditorProps): JSX.Element { From 01911e8fe8675acbb3e6861cdbf1edad69eed04f Mon Sep 17 00:00:00 2001 From: Vlad Babich Date: Wed, 3 May 2023 17:59:08 -0600 Subject: [PATCH 08/16] IrisGridProxyModel --- packages/iris-grid/src/IrisGrid.tsx | 1 + packages/iris-grid/src/IrisGridProxyModel.ts | 21 ++++++++++--------- .../src/IrisGridTableModelTemplate.ts | 1 + .../ConditionalFormattingUtils.test.ts | 2 +- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/iris-grid/src/IrisGrid.tsx b/packages/iris-grid/src/IrisGrid.tsx index 9090e952a7..59aac0e531 100644 --- a/packages/iris-grid/src/IrisGrid.tsx +++ b/packages/iris-grid/src/IrisGrid.tsx @@ -4559,6 +4559,7 @@ export class IrisGrid extends Component { this.getLinkHoverTooltip(linkHoverTooltipProps)} makeModel(table, this.tableUtils, this.formatter)); + .then(table => makeModel(this.dh, table, this.formatter)); } this.setNextModel(modelPromise); } @@ -564,7 +565,7 @@ class IrisGridProxyModel extends IrisGridModel { ) { modelPromise = this.originalModel.table .selectDistinct(selectDistinctColumns) - .then(table => makeModel(table, this.tableUtils, this.formatter)); + .then(table => makeModel(this.dh, table, this.formatter)); } this.setNextModel(modelPromise); } diff --git a/packages/iris-grid/src/IrisGridTableModelTemplate.ts b/packages/iris-grid/src/IrisGridTableModelTemplate.ts index a90de77783..99963e0767 100644 --- a/packages/iris-grid/src/IrisGridTableModelTemplate.ts +++ b/packages/iris-grid/src/IrisGridTableModelTemplate.ts @@ -195,6 +195,7 @@ class IrisGridTableModelTemplate< private _movedColumns: MoveOperation[] | null = null; /** + * @param dh JSAPI instance * @param table Iris data table to be used in the model * @param formatter The formatter to use when getting formats * @param inputTable Iris input table associated with this table diff --git a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.test.ts b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.test.ts index 0c9e38537d..b0d9c713d9 100644 --- a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.test.ts +++ b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.test.ts @@ -17,7 +17,7 @@ jest.mock('./ConditionalFormattingAPIUtils', () => ({ `${rule.column.name} - ${rule.style.type} : ${prevRule}` ), makeColumnFormatColumn: jest.fn((col, rule) => `[col] ${rule}`), - makeRowFormatColumn: jest.fn(dh, rule => `[row] ${rule}`), + makeRowFormatColumn: jest.fn((dh, rule) => `[row] ${rule}`), })); describe('getFormatColumns', () => { From fd767dc4e59c6f884b191d0a2762250bdc186e89 Mon Sep 17 00:00:00 2001 From: Vlad Babich Date: Thu, 4 May 2023 09:13:06 -0600 Subject: [PATCH 09/16] Fixup --- .../code-studio/src/main/AppMainContainer.tsx | 4 ++-- packages/code-studio/src/main/WidgetUtils.ts | 17 ++++++----------- .../dashboard-core-plugins/src/GridPlugin.tsx | 4 +--- .../dashboard-core-plugins/src/PandasPlugin.tsx | 3 +-- .../src/panels/IrisGridPanel.test.tsx | 5 ++++- packages/embed-grid/src/App.tsx | 8 ++------ packages/iris-grid/README.md | 2 +- .../iris-grid/src/AdvancedFilterCreator.tsx | 4 ++-- .../src/AdvancedFilterCreatorSelectValue.tsx | 10 +++++++--- packages/iris-grid/src/IrisGrid.tsx | 1 + packages/iris-grid/src/IrisGridModelFactory.ts | 6 +++--- .../iris-grid/src/IrisGridPartitionSelector.tsx | 2 +- packages/iris-grid/src/IrisGridTestUtils.ts | 9 ++------- .../conditional-formatting/ConditionEditor.tsx | 1 + .../ConditionalFormatEditor.tsx | 2 ++ .../ConditionalFormattingUtils.test.ts | 2 +- 16 files changed, 37 insertions(+), 43 deletions(-) diff --git a/packages/code-studio/src/main/AppMainContainer.tsx b/packages/code-studio/src/main/AppMainContainer.tsx index ccb825c86e..e5a4e502c8 100644 --- a/packages/code-studio/src/main/AppMainContainer.tsx +++ b/packages/code-studio/src/main/AppMainContainer.tsx @@ -755,7 +755,7 @@ export class AppMainContainer extends Component< getDownloadWorker: DownloadServiceWorkerUtils.getServiceWorker, loadPlugin: this.handleLoadTablePlugin, localDashboardId: id, - makeModel: () => createGridModel(connection, props.metadata, type), + makeModel: () => createGridModel(dh, connection, props.metadata, type), }; } @@ -767,7 +767,7 @@ export class AppMainContainer extends Component< makeApi: () => Promise.resolve(dh), makeModel: () => { const { metadata, panelState } = props; - return createChartModel(connection, metadata, panelState); + return createChartModel(dh, connection, metadata, panelState); }, }; } diff --git a/packages/code-studio/src/main/WidgetUtils.ts b/packages/code-studio/src/main/WidgetUtils.ts index 3bf4868891..3aca796b5e 100644 --- a/packages/code-studio/src/main/WidgetUtils.ts +++ b/packages/code-studio/src/main/WidgetUtils.ts @@ -1,10 +1,10 @@ import { ChartModel, ChartModelFactory } from '@deephaven/chart'; import { + dh as DhType, Table, VariableTypeUnion, IdeConnection, - dhType, -} from '@deephaven/jsapi-shim'; +} from '@deephaven/jsapi-types'; import { IrisGridModel, IrisGridModelFactory, @@ -16,14 +16,13 @@ import { GLChartPanelState, isChartPanelTableMetadata, } from '@deephaven/dashboard-core-plugins'; -import { TableUtils } from '@deephaven/jsapi-utils'; export type GridPanelMetadata = { table: string; }; export const createChartModel = async ( - dh: dhType, + dh: DhType, connection: IdeConnection, metadata: ChartPanelMetadata, panelState?: GLChartPanelState @@ -79,12 +78,9 @@ export const createChartModel = async ( type: dh.VariableType.TABLE, }; const table = await connection.getObject(definition); - const tableUtils = new TableUtils(dh); - IrisGridUtils.applyTableSettings( - dh, + new IrisGridUtils(dh).applyTableSettings( table, tableSettings, - tableUtils, getTimeZone(store.getState()) ); @@ -93,7 +89,7 @@ export const createChartModel = async ( }; export const createGridModel = async ( - dh: dhType, + dh: DhType, connection: IdeConnection, metadata: GridPanelMetadata, type: VariableTypeUnion = dh.VariableType.TABLE @@ -101,8 +97,7 @@ export const createGridModel = async ( const { table: tableName } = metadata; const definition = { title: tableName, name: tableName, type }; const table = (await connection.getObject(definition)) as Table; - const tableUtils = new TableUtils(dh); - return IrisGridModelFactory.makeModel(table, tableUtils); + return IrisGridModelFactory.makeModel(dh, table); }; export default { createChartModel, createGridModel }; diff --git a/packages/dashboard-core-plugins/src/GridPlugin.tsx b/packages/dashboard-core-plugins/src/GridPlugin.tsx index 77a40c5948..47988cedd9 100644 --- a/packages/dashboard-core-plugins/src/GridPlugin.tsx +++ b/packages/dashboard-core-plugins/src/GridPlugin.tsx @@ -9,7 +9,6 @@ import { } from '@deephaven/dashboard'; import { IrisGridModelFactory, IrisGridThemeType } from '@deephaven/iris-grid'; import { Table, VariableDefinition } from '@deephaven/jsapi-shim'; -import { TableUtils } from '@deephaven/jsapi-utils'; import shortid from 'shortid'; import { IrisGridPanel, IrisGridPanelProps } from './panels'; @@ -55,10 +54,9 @@ export function GridPlugin(props: GridPluginProps): JSX.Element | null { } const metadata = { name, table: name, type: widget.type }; - const tableUtils = new TableUtils(dh); const makeModel = () => fetch().then((table: Table) => - IrisGridModelFactory.makeModel(table, tableUtils) + IrisGridModelFactory.makeModel(dh, table) ); const config = { type: 'react-component' as const, diff --git a/packages/dashboard-core-plugins/src/PandasPlugin.tsx b/packages/dashboard-core-plugins/src/PandasPlugin.tsx index 4f16cb7ef1..c33b12f5ee 100644 --- a/packages/dashboard-core-plugins/src/PandasPlugin.tsx +++ b/packages/dashboard-core-plugins/src/PandasPlugin.tsx @@ -29,10 +29,9 @@ export function PandasPlugin(props: PandasPluginProps): JSX.Element | null { } const metadata = { name, table: name }; - const tableUtils = new TableUtils(dh); const makeModel = () => fetch().then((table: Table) => - IrisGridModelFactory.makeModel(table, tableUtils) + IrisGridModelFactory.makeModel(dh, table) ); const config = { type: 'react-component' as const, diff --git a/packages/dashboard-core-plugins/src/panels/IrisGridPanel.test.tsx b/packages/dashboard-core-plugins/src/panels/IrisGridPanel.test.tsx index d7737e43eb..5cf4d8bbb0 100644 --- a/packages/dashboard-core-plugins/src/panels/IrisGridPanel.test.tsx +++ b/packages/dashboard-core-plugins/src/panels/IrisGridPanel.test.tsx @@ -44,7 +44,10 @@ function makeGlComponent() { } function makeMakeModel(table = makeTable()) { - return () => Promise.resolve(table).then(IrisGridModelFactory.makeModel); + return () => + Promise.resolve(table).then(resolved => + IrisGridModelFactory.makeModel(dh, resolved) + ); } function makeIrisGridPanelWrapper( diff --git a/packages/embed-grid/src/App.tsx b/packages/embed-grid/src/App.tsx index 77bf41415e..afbd55a574 100644 --- a/packages/embed-grid/src/App.tsx +++ b/packages/embed-grid/src/App.tsx @@ -95,12 +95,7 @@ function App(): JSX.Element { const table = await loadTable(connection, name); // Create the `IrisGridModel` for use with the `IrisGrid` component log.debug(`Creating model...`); - const tableUtils = new TableUtils(dh); - const newModel = await IrisGridModelFactory.makeModel( - table, - tableUtils - ); - + const newModel = await IrisGridModelFactory.makeModel(dh, table); setModel(newModel); log.debug('Table successfully loaded!'); } catch (e: unknown) { @@ -173,6 +168,7 @@ function App(): JSX.Element { diff --git a/packages/iris-grid/src/AdvancedFilterCreator.tsx b/packages/iris-grid/src/AdvancedFilterCreator.tsx index 2a972ce411..9a482b5e21 100644 --- a/packages/iris-grid/src/AdvancedFilterCreator.tsx +++ b/packages/iris-grid/src/AdvancedFilterCreator.tsx @@ -589,7 +589,8 @@ class AdvancedFilterCreator extends PureComponent< <> {!isBoolean &&
}
- +
diff --git a/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx b/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx index 7448b4229c..9ee083d1a0 100644 --- a/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx +++ b/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx @@ -18,7 +18,6 @@ interface AdvancedFilterCreatorSelectValueProps { onChange: (selectedValues: T[], invertSelection: boolean) => void; showSearch: boolean; timeZone: string; - tableUtils: TableUtils; } interface AdvancedFilterCreatorSelectValueState { @@ -52,7 +51,9 @@ class AdvancedFilterCreatorSelectValue extends PureComponent< this.handleSearchChange = this.handleSearchChange.bind(this); this.handleUpdateFilterTimeout = this.handleUpdateFilterTimeout.bind(this); - const { invertSelection, selectedValues } = props; + const { dh, invertSelection, selectedValues } = props; + + this.tableUtils = new TableUtils(dh); this.state = { filters: [], @@ -111,6 +112,8 @@ class AdvancedFilterCreatorSelectValue extends PureComponent< searchTablePromise?: Promise
; + tableUtils: TableUtils; + updateFilterTimer?: ReturnType; getColumnName(): ColumnName { @@ -224,7 +227,8 @@ class AdvancedFilterCreatorSelectValue extends PureComponent< updateTableFilter(): void { const { table, searchText } = this.state; - const { timeZone, tableUtils } = this.props; + const { timeZone } = this.props; + const { tableUtils } = this; const column = table?.columns[0]; const filters = []; if (column == null) { diff --git a/packages/iris-grid/src/IrisGrid.tsx b/packages/iris-grid/src/IrisGrid.tsx index 59aac0e531..589e1e8611 100644 --- a/packages/iris-grid/src/IrisGrid.tsx +++ b/packages/iris-grid/src/IrisGrid.tsx @@ -4413,6 +4413,7 @@ export class IrisGrid extends Component {
{partitionTable && partitionColumn && partition != null && ( { let inputTable = null; if (!TableUtils.isTreeTable(table) && table.hasInputTable) { inputTable = await table.inputTable(); } - return new IrisGridProxyModel(table, tableUtils, formatter, inputTable); + return new IrisGridProxyModel(dh, table, formatter, inputTable); } } diff --git a/packages/iris-grid/src/IrisGridPartitionSelector.tsx b/packages/iris-grid/src/IrisGridPartitionSelector.tsx index 569b3e6b54..d1fefae6d9 100644 --- a/packages/iris-grid/src/IrisGridPartitionSelector.tsx +++ b/packages/iris-grid/src/IrisGridPartitionSelector.tsx @@ -13,7 +13,7 @@ const log = Log.module('IrisGridPartitionSelector'); const PARTITION_CHANGE_DEBOUNCE_MS = 250; interface IrisGridPartitionSelectorProps { - dh: dhType; + dh: DhType; getFormattedString: (value: T, type: string, name: string) => string; table: Table; columnName: ColumnName; diff --git a/packages/iris-grid/src/IrisGridTestUtils.ts b/packages/iris-grid/src/IrisGridTestUtils.ts index bf247c1beb..424ad6afcd 100644 --- a/packages/iris-grid/src/IrisGridTestUtils.ts +++ b/packages/iris-grid/src/IrisGridTestUtils.ts @@ -12,7 +12,7 @@ import { TableViewportSubscription, TreeTable, } from '@deephaven/jsapi-types'; -import { Formatter, TableUtils } from '@deephaven/jsapi-utils'; +import { Formatter } from '@deephaven/jsapi-utils'; import IrisGridProxyModel from './IrisGridProxyModel'; class IrisGridTestUtils { @@ -126,12 +126,7 @@ class IrisGridTestUtils { formatter = new Formatter(), inputTable: InputTable | null = null ): IrisGridProxyModel { - return new IrisGridProxyModel( - table, - new TableUtils(dh), - formatter, - inputTable - ); + return new IrisGridProxyModel(dh, table, formatter, inputTable); } } diff --git a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionEditor.tsx b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionEditor.tsx index 06aafb2163..331fdc64ee 100644 --- a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionEditor.tsx +++ b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionEditor.tsx @@ -368,6 +368,7 @@ function ConditionEditor(props: ConditionEditorProps): JSX.Element { [ onChange, column.type, + dh, selectedCondition, conditionValue, startValue, diff --git a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormatEditor.tsx b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormatEditor.tsx index 29aaefd9d6..a22a150cec 100644 --- a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormatEditor.tsx +++ b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormatEditor.tsx @@ -130,6 +130,7 @@ function ConditionalFormatEditor( {selectedFormatter === FormatterType.CONDITIONAL && ( @@ -138,6 +139,7 @@ function ConditionalFormatEditor( )} diff --git a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.test.ts b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.test.ts index b0d9c713d9..14be55c139 100644 --- a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.test.ts +++ b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.test.ts @@ -17,7 +17,7 @@ jest.mock('./ConditionalFormattingAPIUtils', () => ({ `${rule.column.name} - ${rule.style.type} : ${prevRule}` ), makeColumnFormatColumn: jest.fn((col, rule) => `[col] ${rule}`), - makeRowFormatColumn: jest.fn((dh, rule) => `[row] ${rule}`), + makeRowFormatColumn: jest.fn((_dh, rule) => `[row] ${rule}`), })); describe('getFormatColumns', () => { From cf50f89e46ab7f84472bf40edb8b9ccd967a2106 Mon Sep 17 00:00:00 2001 From: Vlad Babich Date: Thu, 4 May 2023 09:31:14 -0600 Subject: [PATCH 10/16] Remove unused import --- packages/dashboard-core-plugins/src/PandasPlugin.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/dashboard-core-plugins/src/PandasPlugin.tsx b/packages/dashboard-core-plugins/src/PandasPlugin.tsx index c33b12f5ee..7518a32077 100644 --- a/packages/dashboard-core-plugins/src/PandasPlugin.tsx +++ b/packages/dashboard-core-plugins/src/PandasPlugin.tsx @@ -9,7 +9,6 @@ import { } from '@deephaven/dashboard'; import { IrisGridModelFactory } from '@deephaven/iris-grid'; import { Table } from '@deephaven/jsapi-types'; -import { TableUtils } from '@deephaven/jsapi-utils'; import shortid from 'shortid'; import { PandasPanel, PandasPanelProps } from './panels'; From 6b62852a9baffd7a76e2dd473614472ab54bb45e Mon Sep 17 00:00:00 2001 From: Vlad Babich Date: Fri, 5 May 2023 09:29:34 -0600 Subject: [PATCH 11/16] IrisGridPanel makeApi --- .../src/panels/IrisGridPanel.test.tsx | 6 +++ .../src/panels/IrisGridPanel.tsx | 48 +++++++++++-------- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/packages/dashboard-core-plugins/src/panels/IrisGridPanel.test.tsx b/packages/dashboard-core-plugins/src/panels/IrisGridPanel.test.tsx index 5cf4d8bbb0..e7466f888e 100644 --- a/packages/dashboard-core-plugins/src/panels/IrisGridPanel.test.tsx +++ b/packages/dashboard-core-plugins/src/panels/IrisGridPanel.test.tsx @@ -50,8 +50,13 @@ function makeMakeModel(table = makeTable()) { ); } +function makeMakeApi() { + return () => dh; +} + function makeIrisGridPanelWrapper( makeModel = makeMakeModel(), + makeApi = makeMakeApi(), metadata = { table: 'table' }, glContainer = makeGlComponent(), glEventHub = makeGlComponent(), @@ -64,6 +69,7 @@ function makeIrisGridPanelWrapper( ) { return render( DhType | Promise; makeModel: () => IrisGridModel | Promise; inputFilters: InputFilter[]; links: Link[]; @@ -246,11 +246,11 @@ export class IrisGridPanel extends PureComponent< this.irisGrid = React.createRef(); this.pluginRef = React.createRef(); - const { panelState, dh } = props; + const { panelState } = props; this.pluginState = null; - this.dh = dh; - this.irisGridUtils = new IrisGridUtils(dh); + this.dh = null; + this.irisGridUtils = null; this.state = { error: null, @@ -345,9 +345,9 @@ export class IrisGridPanel extends PureComponent< pluginState: unknown; - private dh: DhType; + private dh: DhType | null; - private irisGridUtils: IrisGridUtils; + private irisGridUtils: IrisGridUtils | null; getTableName(): string { const { metadata } = this.props; @@ -468,8 +468,9 @@ export class IrisGridPanel extends PureComponent< frozenColumns: readonly ColumnName[], conditionalFormats: readonly SidebarFormattingRule[], columnHeaderGroups: readonly ColumnHeaderGroup[] - ) => - this.irisGridUtils.dehydrateIrisGridState(model, { + ) => { + assertNotNull(this.irisGridUtils); + return this.irisGridUtils.dehydrateIrisGridState(model, { advancedFilters, aggregationSettings, customColumnFormatMap, @@ -492,7 +493,8 @@ export class IrisGridPanel extends PureComponent< frozenColumns, conditionalFormats, columnHeaderGroups, - }) + }); + } ); getDehydratedGridState = memoize( @@ -527,12 +529,17 @@ export class IrisGridPanel extends PureComponent< initModel(): void { this.setState({ isModelReady: false, isLoading: true, error: null }); - const { makeModel } = this.props; + const { makeApi, makeModel } = this.props; if (this.modelPromise != null) { this.modelPromise.cancel(); } - this.modelPromise = PromiseUtils.makeCancelable(makeModel(), resolved => - resolved.close() + this.modelPromise = PromiseUtils.makeCancelable( + Promise.resolve(makeApi()).then(dh => { + this.dh = dh; + this.irisGridUtils = new IrisGridUtils(dh); + return makeModel(); + }), + resolved => resolved.close() ); this.modelPromise.then(this.handleLoadSuccess).catch(this.handleLoadError); } @@ -615,11 +622,12 @@ export class IrisGridPanel extends PureComponent< const { model } = this.state; assertNotNull(model); const { columns, formatter } = model; - const pluginFilters = this.irisGridUtils.getFiltersFromInputFilters( - columns, - filters, - formatter.timeZone - ); + const pluginFilters = + this.irisGridUtils?.getFiltersFromInputFilters( + columns, + filters, + formatter.timeZone + ) ?? []; this.setState({ pluginFilters }); } @@ -946,7 +954,7 @@ export class IrisGridPanel extends PureComponent< model.columns, advancedFilters ).filter(([columnIndex]) => model.isFilterable(columnIndex)); - + assertNotNull(this.irisGridUtils); irisGrid.clearAllFilters(); irisGrid.setFilters({ quickFilters: this.irisGridUtils.hydrateQuickFilters( @@ -1024,6 +1032,7 @@ export class IrisGridPanel extends PureComponent< partitionColumn, advancedSettings, } = IrisGridUtils.hydrateIrisGridPanelState(model, irisGridPanelState); + assertNotNull(this.irisGridUtils); const { advancedFilters, customColumns, @@ -1196,6 +1205,7 @@ export class IrisGridPanel extends PureComponent< } render(): ReactElement { + const { dh } = this; const { children, glContainer, @@ -1288,7 +1298,7 @@ export class IrisGridPanel extends PureComponent< /> )} > - {isModelReady && model && ( + {isModelReady && model && dh && ( Date: Sun, 7 May 2023 21:43:35 -0600 Subject: [PATCH 12/16] import -> import type --- packages/dashboard-core-plugins/src/PandasPlugin.tsx | 2 +- .../dashboard-core-plugins/src/panels/IrisGridPanel.tsx | 2 +- packages/iris-grid/src/AdvancedFilterCreator.tsx | 2 +- packages/iris-grid/src/AdvancedFilterCreatorFilterItem.tsx | 2 +- packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx | 6 +++++- .../iris-grid/src/AdvancedFilterCreatorSelectValueList.tsx | 6 +++++- packages/iris-grid/src/ColumnStatistics.tsx | 2 +- packages/iris-grid/src/CommonTypes.tsx | 2 +- packages/iris-grid/src/CrossColumnSearch.tsx | 6 +++++- packages/iris-grid/src/GotoRow.tsx | 2 +- packages/iris-grid/src/IrisGrid.tsx | 2 +- packages/iris-grid/src/IrisGridCopyHandler.tsx | 2 +- packages/iris-grid/src/IrisGridModel.test.ts | 2 +- packages/iris-grid/src/IrisGridModel.ts | 2 +- packages/iris-grid/src/IrisGridModelFactory.ts | 2 +- packages/iris-grid/src/IrisGridModelUpdater.tsx | 2 +- packages/iris-grid/src/IrisGridPartitionSelector.tsx | 2 +- packages/iris-grid/src/IrisGridProxyModel.ts | 2 +- packages/iris-grid/src/IrisGridRenderer.ts | 2 +- packages/iris-grid/src/IrisGridTableModel.ts | 2 +- packages/iris-grid/src/IrisGridTableModelTemplate.ts | 2 +- packages/iris-grid/src/IrisGridTestUtils.ts | 2 +- packages/iris-grid/src/IrisGridTreeTableModel.ts | 2 +- packages/iris-grid/src/IrisGridUtils.test.ts | 2 +- packages/iris-grid/src/IrisGridUtils.ts | 2 +- packages/iris-grid/src/PartitionSelectorSearch.test.tsx | 2 +- packages/iris-grid/src/PartitionSelectorSearch.tsx | 2 +- packages/iris-grid/src/TableViewportUpdater.tsx | 2 +- packages/iris-grid/src/TreeTableViewportUpdater.tsx | 2 +- .../src/mousehandlers/IrisGridColumnSelectMouseHandler.ts | 2 +- .../src/mousehandlers/IrisGridContextMenuHandler.tsx | 2 +- packages/iris-grid/src/sidebar/ChartBuilder.tsx | 6 +++++- packages/iris-grid/src/sidebar/RollupRows.tsx | 2 +- packages/iris-grid/src/sidebar/SelectDistinctBuilder.tsx | 2 +- packages/iris-grid/src/sidebar/TableCsvExporter.tsx | 2 +- packages/iris-grid/src/sidebar/TableSaver.tsx | 2 +- .../iris-grid/src/sidebar/aggregations/AggregationEdit.tsx | 2 +- .../iris-grid/src/sidebar/aggregations/AggregationUtils.ts | 2 +- .../sidebar/conditional-formatting/ColumnFormatEditor.tsx | 2 +- .../src/sidebar/conditional-formatting/ConditionEditor.tsx | 2 +- .../conditional-formatting/ConditionalFormattingAPIUtils.ts | 6 +++++- .../ConditionalFormattingUtils.test.ts | 2 +- .../conditional-formatting/ConditionalFormattingUtils.ts | 6 +++++- .../src/sidebar/conditional-formatting/RowFormatEditor.tsx | 2 +- .../VisibilityOrderingBuilder.test.tsx | 2 +- .../VisibilityOrderingBuilder.tsx | 2 +- .../VisibilityOrderingBuilderUtils.test.ts | 2 +- .../visibility-ordering-builder/sortable-tree/utilities.ts | 2 +- packages/jsapi-utils/src/DateUtils.ts | 2 +- packages/jsapi-utils/src/SessionUtils.ts | 2 +- packages/jsapi-utils/src/TableUtils.test.ts | 2 +- packages/jsapi-utils/src/TableUtils.ts | 2 +- 52 files changed, 76 insertions(+), 52 deletions(-) diff --git a/packages/dashboard-core-plugins/src/PandasPlugin.tsx b/packages/dashboard-core-plugins/src/PandasPlugin.tsx index 7518a32077..d3b66b3e05 100644 --- a/packages/dashboard-core-plugins/src/PandasPlugin.tsx +++ b/packages/dashboard-core-plugins/src/PandasPlugin.tsx @@ -8,7 +8,7 @@ import { useListener, } from '@deephaven/dashboard'; import { IrisGridModelFactory } from '@deephaven/iris-grid'; -import { Table } from '@deephaven/jsapi-types'; +import type { Table } from '@deephaven/jsapi-types'; import shortid from 'shortid'; import { PandasPanel, PandasPanelProps } from './panels'; diff --git a/packages/dashboard-core-plugins/src/panels/IrisGridPanel.tsx b/packages/dashboard-core-plugins/src/panels/IrisGridPanel.tsx index aee8baaaad..a70654c9df 100644 --- a/packages/dashboard-core-plugins/src/panels/IrisGridPanel.tsx +++ b/packages/dashboard-core-plugins/src/panels/IrisGridPanel.tsx @@ -61,7 +61,7 @@ import { PromiseUtils, } from '@deephaven/utils'; import { ContextAction, ContextMenuRoot } from '@deephaven/components'; -import { +import type { Column, dh as DhType, FilterCondition, diff --git a/packages/iris-grid/src/AdvancedFilterCreator.tsx b/packages/iris-grid/src/AdvancedFilterCreator.tsx index 9a482b5e21..3d91e30079 100644 --- a/packages/iris-grid/src/AdvancedFilterCreator.tsx +++ b/packages/iris-grid/src/AdvancedFilterCreator.tsx @@ -21,7 +21,7 @@ import { import { Button, ContextActionUtils } from '@deephaven/components'; import Log from '@deephaven/log'; import { CancelablePromise, PromiseUtils } from '@deephaven/utils'; -import { Column, FilterCondition, Table } from '@deephaven/jsapi-types'; +import type { Column, FilterCondition, Table } from '@deephaven/jsapi-types'; import shortid from 'shortid'; import AdvancedFilterCreatorFilterItem from './AdvancedFilterCreatorFilterItem'; import AdvancedFilterCreatorSelectValue from './AdvancedFilterCreatorSelectValue'; diff --git a/packages/iris-grid/src/AdvancedFilterCreatorFilterItem.tsx b/packages/iris-grid/src/AdvancedFilterCreatorFilterItem.tsx index d8bef808ed..4fb5c33bff 100644 --- a/packages/iris-grid/src/AdvancedFilterCreatorFilterItem.tsx +++ b/packages/iris-grid/src/AdvancedFilterCreatorFilterItem.tsx @@ -9,7 +9,7 @@ import { TypeValue as FilterTypeValue, } from '@deephaven/filters'; import { vsTrash } from '@deephaven/icons'; -import { Column } from '@deephaven/jsapi-types'; +import type { Column } from '@deephaven/jsapi-types'; import { AdvancedFilterItemType, Formatter, diff --git a/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx b/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx index 9ee083d1a0..7f1fcf1f33 100644 --- a/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx +++ b/packages/iris-grid/src/AdvancedFilterCreatorSelectValue.tsx @@ -3,7 +3,11 @@ import React, { PureComponent } from 'react'; import { CSSTransition } from 'react-transition-group'; import classNames from 'classnames'; import { Formatter, TableUtils } from '@deephaven/jsapi-utils'; -import { dh as DhType, FilterCondition, Table } from '@deephaven/jsapi-types'; +import type { + dh as DhType, + FilterCondition, + Table, +} from '@deephaven/jsapi-types'; import { Button } from '@deephaven/components'; import AdvancedFilterCreatorSelectValueList from './AdvancedFilterCreatorSelectValueList'; import './AdvancedFilterCreatorSelectValue.scss'; diff --git a/packages/iris-grid/src/AdvancedFilterCreatorSelectValueList.tsx b/packages/iris-grid/src/AdvancedFilterCreatorSelectValueList.tsx index a9dec921d6..048142a093 100644 --- a/packages/iris-grid/src/AdvancedFilterCreatorSelectValueList.tsx +++ b/packages/iris-grid/src/AdvancedFilterCreatorSelectValueList.tsx @@ -1,7 +1,11 @@ /* eslint react/no-did-update-set-state: "off" */ import React, { PureComponent } from 'react'; import { CSSTransition } from 'react-transition-group'; -import { dh as DhType, FilterCondition, Table } from '@deephaven/jsapi-types'; +import type { + dh as DhType, + FilterCondition, + Table, +} from '@deephaven/jsapi-types'; import { Formatter } from '@deephaven/jsapi-utils'; import { LoadingSpinner, diff --git a/packages/iris-grid/src/ColumnStatistics.tsx b/packages/iris-grid/src/ColumnStatistics.tsx index 61e96c0624..05a7747792 100644 --- a/packages/iris-grid/src/ColumnStatistics.tsx +++ b/packages/iris-grid/src/ColumnStatistics.tsx @@ -3,7 +3,7 @@ import classNames from 'classnames'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Button, CopyButton, LoadingSpinner } from '@deephaven/components'; import { dhFreeze, dhRefresh, vsLock } from '@deephaven/icons'; -import { +import type { Column, ColumnStatistics as APIColumnStatistics, } from '@deephaven/jsapi-types'; diff --git a/packages/iris-grid/src/CommonTypes.tsx b/packages/iris-grid/src/CommonTypes.tsx index 444a082caa..5f2369cd36 100644 --- a/packages/iris-grid/src/CommonTypes.tsx +++ b/packages/iris-grid/src/CommonTypes.tsx @@ -1,6 +1,6 @@ import { AdvancedFilterOptions } from '@deephaven/jsapi-utils'; import { ModelIndex } from '@deephaven/grid'; -import { +import type { TotalsTableConfig, FilterCondition, Format, diff --git a/packages/iris-grid/src/CrossColumnSearch.tsx b/packages/iris-grid/src/CrossColumnSearch.tsx index 90423ababc..a570610f60 100644 --- a/packages/iris-grid/src/CrossColumnSearch.tsx +++ b/packages/iris-grid/src/CrossColumnSearch.tsx @@ -9,7 +9,11 @@ import { dhWarningCircleFilled, vsCircleLargeFilled, } from '@deephaven/icons'; -import { Column, dh as DhType, FilterCondition } from '@deephaven/jsapi-types'; +import type { + Column, + dh as DhType, + FilterCondition, +} from '@deephaven/jsapi-types'; import { TableUtils } from '@deephaven/jsapi-utils'; import './CrossColumnSearch.scss'; import { ColumnName } from './CommonTypes'; diff --git a/packages/iris-grid/src/GotoRow.tsx b/packages/iris-grid/src/GotoRow.tsx index fb1a496b68..4bb94d6cd1 100644 --- a/packages/iris-grid/src/GotoRow.tsx +++ b/packages/iris-grid/src/GotoRow.tsx @@ -8,7 +8,7 @@ import React, { useRef, useState, } from 'react'; -import { dh as DhType, Column } from '@deephaven/jsapi-types'; +import type { dh as DhType, Column } from '@deephaven/jsapi-types'; import { Type as FilterType, TypeValue as FilterTypeValue, diff --git a/packages/iris-grid/src/IrisGrid.tsx b/packages/iris-grid/src/IrisGrid.tsx index 589e1e8611..87297a8d0e 100644 --- a/packages/iris-grid/src/IrisGrid.tsx +++ b/packages/iris-grid/src/IrisGrid.tsx @@ -61,7 +61,7 @@ import { vsSymbolOperator, vsTools, } from '@deephaven/icons'; -import { +import type { Column, ColumnGroup, CustomColumn, diff --git a/packages/iris-grid/src/IrisGridCopyHandler.tsx b/packages/iris-grid/src/IrisGridCopyHandler.tsx index 5f009d8fa4..54470f0e83 100644 --- a/packages/iris-grid/src/IrisGridCopyHandler.tsx +++ b/packages/iris-grid/src/IrisGridCopyHandler.tsx @@ -15,7 +15,7 @@ import { PromiseUtils, } from '@deephaven/utils'; import Log from '@deephaven/log'; -import { Column } from '@deephaven/jsapi-types'; +import type { Column } from '@deephaven/jsapi-types'; import IrisGridUtils from './IrisGridUtils'; import IrisGridBottomBar from './IrisGridBottomBar'; import './IrisGridCopyHandler.scss'; diff --git a/packages/iris-grid/src/IrisGridModel.test.ts b/packages/iris-grid/src/IrisGridModel.test.ts index 2a51bdfcf7..4f026caf88 100644 --- a/packages/iris-grid/src/IrisGridModel.test.ts +++ b/packages/iris-grid/src/IrisGridModel.test.ts @@ -1,5 +1,5 @@ import { waitFor } from '@testing-library/react'; -import { +import type { InputTable, Table, TableViewportSubscription, diff --git a/packages/iris-grid/src/IrisGridModel.ts b/packages/iris-grid/src/IrisGridModel.ts index 832bbf13fd..4c7930d226 100644 --- a/packages/iris-grid/src/IrisGridModel.ts +++ b/packages/iris-grid/src/IrisGridModel.ts @@ -7,7 +7,7 @@ import { MoveOperation, VisibleIndex, } from '@deephaven/grid'; -import { +import type { Column, ColumnStatistics, CustomColumn, diff --git a/packages/iris-grid/src/IrisGridModelFactory.ts b/packages/iris-grid/src/IrisGridModelFactory.ts index 3d9d434cfb..f303d1a938 100644 --- a/packages/iris-grid/src/IrisGridModelFactory.ts +++ b/packages/iris-grid/src/IrisGridModelFactory.ts @@ -1,4 +1,4 @@ -import { dh as DhType, Table, TreeTable } from '@deephaven/jsapi-types'; +import type { dh as DhType, Table, TreeTable } from '@deephaven/jsapi-types'; import { Formatter, TableUtils } from '@deephaven/jsapi-utils'; import IrisGridModel from './IrisGridModel'; import IrisGridProxyModel from './IrisGridProxyModel'; diff --git a/packages/iris-grid/src/IrisGridModelUpdater.tsx b/packages/iris-grid/src/IrisGridModelUpdater.tsx index 809347d256..0a73101ff8 100644 --- a/packages/iris-grid/src/IrisGridModelUpdater.tsx +++ b/packages/iris-grid/src/IrisGridModelUpdater.tsx @@ -1,7 +1,7 @@ /* eslint-disable react/require-default-props */ /* eslint-disable no-param-reassign */ import React, { useEffect, useMemo } from 'react'; -import { +import type { Column, CustomColumn, dh as DhType, diff --git a/packages/iris-grid/src/IrisGridPartitionSelector.tsx b/packages/iris-grid/src/IrisGridPartitionSelector.tsx index d1fefae6d9..b566e7dadb 100644 --- a/packages/iris-grid/src/IrisGridPartitionSelector.tsx +++ b/packages/iris-grid/src/IrisGridPartitionSelector.tsx @@ -4,7 +4,7 @@ import { DropdownMenu, Tooltip } from '@deephaven/components'; import { vsTriangleDown, vsClose } from '@deephaven/icons'; import Log from '@deephaven/log'; import debounce from 'lodash.debounce'; -import { dh as DhType, Table } from '@deephaven/jsapi-types'; +import type { dh as DhType, Table } from '@deephaven/jsapi-types'; import PartitionSelectorSearch from './PartitionSelectorSearch'; import './IrisGridPartitionSelector.scss'; import { ColumnName } from './CommonTypes'; diff --git a/packages/iris-grid/src/IrisGridProxyModel.ts b/packages/iris-grid/src/IrisGridProxyModel.ts index 8d32d5f791..cf626ed740 100644 --- a/packages/iris-grid/src/IrisGridProxyModel.ts +++ b/packages/iris-grid/src/IrisGridProxyModel.ts @@ -7,7 +7,7 @@ import { EventShimCustomEvent, PromiseUtils, } from '@deephaven/utils'; -import { +import type { Column, ColumnStatistics, CustomColumn, diff --git a/packages/iris-grid/src/IrisGridRenderer.ts b/packages/iris-grid/src/IrisGridRenderer.ts index f7c87e0e83..9dcb1b3ebc 100644 --- a/packages/iris-grid/src/IrisGridRenderer.ts +++ b/packages/iris-grid/src/IrisGridRenderer.ts @@ -13,7 +13,7 @@ import { GridUtils, VisibleIndex, } from '@deephaven/grid'; -import { Sort } from '@deephaven/jsapi-types'; +import type { Sort } from '@deephaven/jsapi-types'; import { TableUtils, ReverseType } from '@deephaven/jsapi-utils'; import { assertNotNull, getOrThrow } from '@deephaven/utils'; import { diff --git a/packages/iris-grid/src/IrisGridTableModel.ts b/packages/iris-grid/src/IrisGridTableModel.ts index a2cd483adf..00ea607f95 100644 --- a/packages/iris-grid/src/IrisGridTableModel.ts +++ b/packages/iris-grid/src/IrisGridTableModel.ts @@ -1,7 +1,7 @@ /* eslint class-methods-use-this: "off" */ import memoize from 'memoize-one'; import { GridRange, ModelIndex } from '@deephaven/grid'; -import { +import type { Column, ColumnStatistics, CustomColumn, diff --git a/packages/iris-grid/src/IrisGridTableModelTemplate.ts b/packages/iris-grid/src/IrisGridTableModelTemplate.ts index 99963e0767..37e04358ff 100644 --- a/packages/iris-grid/src/IrisGridTableModelTemplate.ts +++ b/packages/iris-grid/src/IrisGridTableModelTemplate.ts @@ -11,7 +11,7 @@ import { MoveOperation, VisibleIndex, } from '@deephaven/grid'; -import { +import type { Column, ColumnStatistics, CustomColumn, diff --git a/packages/iris-grid/src/IrisGridTestUtils.ts b/packages/iris-grid/src/IrisGridTestUtils.ts index 424ad6afcd..787e9087c0 100644 --- a/packages/iris-grid/src/IrisGridTestUtils.ts +++ b/packages/iris-grid/src/IrisGridTestUtils.ts @@ -1,5 +1,5 @@ import { GridRangeIndex, ModelSizeMap } from '@deephaven/grid'; -import { +import type { Column, dh as DhType, FilterCondition, diff --git a/packages/iris-grid/src/IrisGridTreeTableModel.ts b/packages/iris-grid/src/IrisGridTreeTableModel.ts index f180817a84..75f02a6681 100644 --- a/packages/iris-grid/src/IrisGridTreeTableModel.ts +++ b/packages/iris-grid/src/IrisGridTreeTableModel.ts @@ -1,7 +1,7 @@ /* eslint class-methods-use-this: "off" */ import memoize from 'memoize-one'; import { GridRange, ModelIndex } from '@deephaven/grid'; -import { Column, TreeRow, TreeTable } from '@deephaven/jsapi-types'; +import type { Column, TreeRow, TreeTable } from '@deephaven/jsapi-types'; import { assertNotNull } from '@deephaven/utils'; import { UIRow, ColumnName } from './CommonTypes'; import IrisGridTableModelTemplate from './IrisGridTableModelTemplate'; diff --git a/packages/iris-grid/src/IrisGridUtils.test.ts b/packages/iris-grid/src/IrisGridUtils.test.ts index 533b463586..5eedef5f1f 100644 --- a/packages/iris-grid/src/IrisGridUtils.test.ts +++ b/packages/iris-grid/src/IrisGridUtils.test.ts @@ -1,6 +1,6 @@ import { GridUtils, GridRange, MoveOperation } from '@deephaven/grid'; import dh from '@deephaven/jsapi-shim'; -import { Column, Table, Sort } from '@deephaven/jsapi-types'; +import type { Column, Table, Sort } from '@deephaven/jsapi-types'; import { TypeValue as FilterTypeValue } from '@deephaven/filters'; import { DateUtils } from '@deephaven/jsapi-utils'; import type { AdvancedFilter } from './CommonTypes'; diff --git a/packages/iris-grid/src/IrisGridUtils.ts b/packages/iris-grid/src/IrisGridUtils.ts index 3c5975e764..8f1b90d383 100644 --- a/packages/iris-grid/src/IrisGridUtils.ts +++ b/packages/iris-grid/src/IrisGridUtils.ts @@ -7,7 +7,7 @@ import { MoveOperation, VisibleIndex, } from '@deephaven/grid'; -import { +import type { Column, ColumnGroup, DateWrapper, diff --git a/packages/iris-grid/src/PartitionSelectorSearch.test.tsx b/packages/iris-grid/src/PartitionSelectorSearch.test.tsx index f49fdeae60..c130e77508 100644 --- a/packages/iris-grid/src/PartitionSelectorSearch.test.tsx +++ b/packages/iris-grid/src/PartitionSelectorSearch.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import dh from '@deephaven/jsapi-shim'; -import { Table } from '@deephaven/jsapi-types'; +import type { Table } from '@deephaven/jsapi-types'; import PartitionSelectorSearch from './PartitionSelectorSearch'; import IrisGridTestUtils from './IrisGridTestUtils'; diff --git a/packages/iris-grid/src/PartitionSelectorSearch.tsx b/packages/iris-grid/src/PartitionSelectorSearch.tsx index 6932abc338..20d92db32c 100644 --- a/packages/iris-grid/src/PartitionSelectorSearch.tsx +++ b/packages/iris-grid/src/PartitionSelectorSearch.tsx @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import debounce from 'lodash.debounce'; -import { dh as DhType, Table } from '@deephaven/jsapi-types'; +import type { dh as DhType, Table } from '@deephaven/jsapi-types'; import { ItemList, LoadingSpinner } from '@deephaven/components'; import Log from '@deephaven/log'; import { CanceledPromiseError } from '@deephaven/utils'; diff --git a/packages/iris-grid/src/TableViewportUpdater.tsx b/packages/iris-grid/src/TableViewportUpdater.tsx index 6379c50fb4..78f9a98675 100644 --- a/packages/iris-grid/src/TableViewportUpdater.tsx +++ b/packages/iris-grid/src/TableViewportUpdater.tsx @@ -2,7 +2,7 @@ import { PureComponent } from 'react'; import memoize from 'memoize-one'; import throttle from 'lodash.throttle'; import { GridUtils, MoveOperation } from '@deephaven/grid'; -import { +import type { Column, FilterCondition, Sort, diff --git a/packages/iris-grid/src/TreeTableViewportUpdater.tsx b/packages/iris-grid/src/TreeTableViewportUpdater.tsx index a56daf66bc..57c5e4701f 100644 --- a/packages/iris-grid/src/TreeTableViewportUpdater.tsx +++ b/packages/iris-grid/src/TreeTableViewportUpdater.tsx @@ -1,6 +1,6 @@ import { PureComponent } from 'react'; import throttle from 'lodash.throttle'; -import { +import type { dh as DhType, EventListener, FilterCondition, diff --git a/packages/iris-grid/src/mousehandlers/IrisGridColumnSelectMouseHandler.ts b/packages/iris-grid/src/mousehandlers/IrisGridColumnSelectMouseHandler.ts index 5f72a8107d..44db274e02 100644 --- a/packages/iris-grid/src/mousehandlers/IrisGridColumnSelectMouseHandler.ts +++ b/packages/iris-grid/src/mousehandlers/IrisGridColumnSelectMouseHandler.ts @@ -4,7 +4,7 @@ import { GridPoint, EventHandlerResult, } from '@deephaven/grid'; -import { Column } from '@deephaven/jsapi-types'; +import type { Column } from '@deephaven/jsapi-types'; import { IrisGrid } from '../IrisGrid'; /** diff --git a/packages/iris-grid/src/mousehandlers/IrisGridContextMenuHandler.tsx b/packages/iris-grid/src/mousehandlers/IrisGridContextMenuHandler.tsx index 88d66ac820..d55a00a0a2 100644 --- a/packages/iris-grid/src/mousehandlers/IrisGridContextMenuHandler.tsx +++ b/packages/iris-grid/src/mousehandlers/IrisGridContextMenuHandler.tsx @@ -25,7 +25,7 @@ import { isExpandableGridModel, ModelIndex, } from '@deephaven/grid'; -import { +import type { Column, dh as DhType, FilterCondition, diff --git a/packages/iris-grid/src/sidebar/ChartBuilder.tsx b/packages/iris-grid/src/sidebar/ChartBuilder.tsx index de39533408..113d3b08ed 100644 --- a/packages/iris-grid/src/sidebar/ChartBuilder.tsx +++ b/packages/iris-grid/src/sidebar/ChartBuilder.tsx @@ -10,7 +10,11 @@ import { vsCircleLargeFilled, vsTrash, } from '@deephaven/icons'; -import { Column, dh as DhType, SeriesPlotStyle } from '@deephaven/jsapi-types'; +import type { + Column, + dh as DhType, + SeriesPlotStyle, +} from '@deephaven/jsapi-types'; import Log from '@deephaven/log'; import shortid from 'shortid'; import { diff --git a/packages/iris-grid/src/sidebar/RollupRows.tsx b/packages/iris-grid/src/sidebar/RollupRows.tsx index a7fc4513d0..0d2e5b61fa 100644 --- a/packages/iris-grid/src/sidebar/RollupRows.tsx +++ b/packages/iris-grid/src/sidebar/RollupRows.tsx @@ -26,7 +26,7 @@ import debounce from 'lodash.debounce'; import Log from '@deephaven/log'; import { assertNotNull } from '@deephaven/utils'; import './RollupRows.scss'; -import { Column } from '@deephaven/jsapi-types'; +import type { Column } from '@deephaven/jsapi-types'; import IrisGridModel from '../IrisGridModel'; import { ColumnName } from '../CommonTypes'; diff --git a/packages/iris-grid/src/sidebar/SelectDistinctBuilder.tsx b/packages/iris-grid/src/sidebar/SelectDistinctBuilder.tsx index b839246eb2..5a8a8613f0 100644 --- a/packages/iris-grid/src/sidebar/SelectDistinctBuilder.tsx +++ b/packages/iris-grid/src/sidebar/SelectDistinctBuilder.tsx @@ -4,7 +4,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { dhNewCircleLargeFilled, vsTrash } from '@deephaven/icons'; import { Button } from '@deephaven/components'; import Log from '@deephaven/log'; -import { Column } from '@deephaven/jsapi-types'; +import type { Column } from '@deephaven/jsapi-types'; import { ModelIndex } from '@deephaven/grid'; import IrisGridModel from '../IrisGridModel'; diff --git a/packages/iris-grid/src/sidebar/TableCsvExporter.tsx b/packages/iris-grid/src/sidebar/TableCsvExporter.tsx index 3b3e083f7d..de085342b8 100644 --- a/packages/iris-grid/src/sidebar/TableCsvExporter.tsx +++ b/packages/iris-grid/src/sidebar/TableCsvExporter.tsx @@ -15,7 +15,7 @@ import { MoveOperation, } from '@deephaven/grid'; import { vsWarning } from '@deephaven/icons'; -import { +import type { dh as DhType, Table, TableViewportSubscription, diff --git a/packages/iris-grid/src/sidebar/TableSaver.tsx b/packages/iris-grid/src/sidebar/TableSaver.tsx index da69414df7..ab29552959 100644 --- a/packages/iris-grid/src/sidebar/TableSaver.tsx +++ b/packages/iris-grid/src/sidebar/TableSaver.tsx @@ -1,5 +1,5 @@ import { PureComponent } from 'react'; -import { +import type { Column, dh as DhType, DateWrapper, diff --git a/packages/iris-grid/src/sidebar/aggregations/AggregationEdit.tsx b/packages/iris-grid/src/sidebar/aggregations/AggregationEdit.tsx index 17f98e1314..4e7e33ab47 100644 --- a/packages/iris-grid/src/sidebar/aggregations/AggregationEdit.tsx +++ b/packages/iris-grid/src/sidebar/aggregations/AggregationEdit.tsx @@ -3,7 +3,7 @@ import classNames from 'classnames'; import { CSSTransition } from 'react-transition-group'; import { Button, Checkbox, ItemList, ThemeExport } from '@deephaven/components'; import { dhSortAlphaDown, dhSortAlphaUp } from '@deephaven/icons'; -import { Column } from '@deephaven/jsapi-types'; +import type { Column } from '@deephaven/jsapi-types'; import { TableUtils } from '@deephaven/jsapi-utils'; import { Aggregation } from './Aggregations'; import { filterValidColumns } from './AggregationUtils'; diff --git a/packages/iris-grid/src/sidebar/aggregations/AggregationUtils.ts b/packages/iris-grid/src/sidebar/aggregations/AggregationUtils.ts index 2088e5eb9c..db03167e9e 100644 --- a/packages/iris-grid/src/sidebar/aggregations/AggregationUtils.ts +++ b/packages/iris-grid/src/sidebar/aggregations/AggregationUtils.ts @@ -1,4 +1,4 @@ -import { Column } from '@deephaven/jsapi-types'; +import type { Column } from '@deephaven/jsapi-types'; import { TableUtils } from '@deephaven/jsapi-utils'; import AggregationOperation from './AggregationOperation'; diff --git a/packages/iris-grid/src/sidebar/conditional-formatting/ColumnFormatEditor.tsx b/packages/iris-grid/src/sidebar/conditional-formatting/ColumnFormatEditor.tsx index ea0c359c5c..7ead0181c1 100644 --- a/packages/iris-grid/src/sidebar/conditional-formatting/ColumnFormatEditor.tsx +++ b/packages/iris-grid/src/sidebar/conditional-formatting/ColumnFormatEditor.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import Log from '@deephaven/log'; import { ComboBox } from '@deephaven/components'; -import { dh as DhType } from '@deephaven/jsapi-types'; +import type { dh as DhType } from '@deephaven/jsapi-types'; import { BaseFormatConfig, ChangeCallback, diff --git a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionEditor.tsx b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionEditor.tsx index 331fdc64ee..1b3fdcae5e 100644 --- a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionEditor.tsx +++ b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionEditor.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { TableUtils } from '@deephaven/jsapi-utils'; -import { dh as DhType } from '@deephaven/jsapi-types'; +import type { dh as DhType } from '@deephaven/jsapi-types'; import Log from '@deephaven/log'; import { StringCondition, diff --git a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingAPIUtils.ts b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingAPIUtils.ts index dcb91d54a2..30ab8de431 100644 --- a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingAPIUtils.ts +++ b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingAPIUtils.ts @@ -1,4 +1,8 @@ -import { Column, dh as DhType, CustomColumn } from '@deephaven/jsapi-types'; +import type { + Column, + dh as DhType, + CustomColumn, +} from '@deephaven/jsapi-types'; import { BaseFormatConfig, getConditionDBString, diff --git a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.test.ts b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.test.ts index 14be55c139..a0b4571dfa 100644 --- a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.test.ts +++ b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.test.ts @@ -1,5 +1,5 @@ import dh from '@deephaven/jsapi-shim'; -import { Column } from '@deephaven/jsapi-types'; +import type { Column } from '@deephaven/jsapi-types'; import IrisGridTestUtils from '../../IrisGridTestUtils'; import { DateCondition, diff --git a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.ts b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.ts index 2662094649..070514745c 100644 --- a/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.ts +++ b/packages/iris-grid/src/sidebar/conditional-formatting/ConditionalFormattingUtils.ts @@ -1,5 +1,9 @@ import Log from '@deephaven/log'; -import { Column, CustomColumn, dh as DhType } from '@deephaven/jsapi-types'; +import type { + Column, + CustomColumn, + dh as DhType, +} from '@deephaven/jsapi-types'; import { DateUtils, TableUtils } from '@deephaven/jsapi-utils'; import { makeColumnFormatColumn, diff --git a/packages/iris-grid/src/sidebar/conditional-formatting/RowFormatEditor.tsx b/packages/iris-grid/src/sidebar/conditional-formatting/RowFormatEditor.tsx index d54219cf52..9178c9d760 100644 --- a/packages/iris-grid/src/sidebar/conditional-formatting/RowFormatEditor.tsx +++ b/packages/iris-grid/src/sidebar/conditional-formatting/RowFormatEditor.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useState } from 'react'; import Log from '@deephaven/log'; -import { dh as DhType } from '@deephaven/jsapi-types'; +import type { dh as DhType } from '@deephaven/jsapi-types'; import { ComboBox } from '@deephaven/components'; import { BaseFormatConfig, diff --git a/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilder.test.tsx b/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilder.test.tsx index 487d465a07..0b036edf2d 100644 --- a/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilder.test.tsx +++ b/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilder.test.tsx @@ -5,7 +5,7 @@ import { GridUtils } from '@deephaven/grid'; import type { MoveOperation } from '@deephaven/grid'; import { assertNotNull, TestUtils } from '@deephaven/utils'; import dh from '@deephaven/jsapi-shim'; -import { ColumnGroup } from '@deephaven/jsapi-types'; +import type { ColumnGroup } from '@deephaven/jsapi-types'; import VisibilityOrderingBuilder from './VisibilityOrderingBuilder'; import IrisGridTestUtils from '../../IrisGridTestUtils'; import ColumnHeaderGroup from '../../ColumnHeaderGroup'; diff --git a/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilder.tsx b/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilder.tsx index f8b4ed6be1..548df0e0db 100644 --- a/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilder.tsx +++ b/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilder.tsx @@ -22,7 +22,7 @@ import { vsCircleLargeFilled, vsAdd, } from '@deephaven/icons'; -import { Column } from '@deephaven/jsapi-types'; +import type { Column } from '@deephaven/jsapi-types'; import memoize from 'memoizee'; import debounce from 'lodash.debounce'; import { Button, SearchInput } from '@deephaven/components'; diff --git a/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilderUtils.test.ts b/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilderUtils.test.ts index b78b6350ee..cd3ab59de2 100644 --- a/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilderUtils.test.ts +++ b/packages/iris-grid/src/sidebar/visibility-ordering-builder/VisibilityOrderingBuilderUtils.test.ts @@ -1,5 +1,5 @@ import dh from '@deephaven/jsapi-shim'; -import { ColumnGroup } from '@deephaven/jsapi-types'; +import type { ColumnGroup } from '@deephaven/jsapi-types'; import IrisGridTestUtils from '../../IrisGridTestUtils'; import { moveItemsFromDrop, diff --git a/packages/iris-grid/src/sidebar/visibility-ordering-builder/sortable-tree/utilities.ts b/packages/iris-grid/src/sidebar/visibility-ordering-builder/sortable-tree/utilities.ts index 077ec66ace..b49aa78090 100644 --- a/packages/iris-grid/src/sidebar/visibility-ordering-builder/sortable-tree/utilities.ts +++ b/packages/iris-grid/src/sidebar/visibility-ordering-builder/sortable-tree/utilities.ts @@ -1,5 +1,5 @@ import { arrayMove } from '@dnd-kit/sortable'; -import { Column } from '@deephaven/jsapi-types'; +import type { Column } from '@deephaven/jsapi-types'; import { GridUtils, ModelIndex, MoveOperation } from '@deephaven/grid'; import type ColumnHeaderGroup from '../../../ColumnHeaderGroup'; import { isFlattenedTreeItem, ReadonlyTreeItems } from './types'; diff --git a/packages/jsapi-utils/src/DateUtils.ts b/packages/jsapi-utils/src/DateUtils.ts index 590546b921..d5a9766775 100644 --- a/packages/jsapi-utils/src/DateUtils.ts +++ b/packages/jsapi-utils/src/DateUtils.ts @@ -1,4 +1,4 @@ -import { DateWrapper, dh as DhType } from '@deephaven/jsapi-types'; +import type { DateWrapper, dh as DhType } from '@deephaven/jsapi-types'; interface DateParts { year: T; diff --git a/packages/jsapi-utils/src/SessionUtils.ts b/packages/jsapi-utils/src/SessionUtils.ts index aadb1dd687..420f05e214 100644 --- a/packages/jsapi-utils/src/SessionUtils.ts +++ b/packages/jsapi-utils/src/SessionUtils.ts @@ -1,4 +1,4 @@ -import { +import type { ConnectOptions, CoreClient, IdeConnection, diff --git a/packages/jsapi-utils/src/TableUtils.test.ts b/packages/jsapi-utils/src/TableUtils.test.ts index 42fd943be0..9f306f28f8 100644 --- a/packages/jsapi-utils/src/TableUtils.test.ts +++ b/packages/jsapi-utils/src/TableUtils.test.ts @@ -1,5 +1,5 @@ import dh from '@deephaven/jsapi-shim'; -import { +import type { Column, CustomColumn, DateWrapper, diff --git a/packages/jsapi-utils/src/TableUtils.ts b/packages/jsapi-utils/src/TableUtils.ts index a34145629f..bedb44375d 100644 --- a/packages/jsapi-utils/src/TableUtils.ts +++ b/packages/jsapi-utils/src/TableUtils.ts @@ -5,7 +5,7 @@ import { OperatorValue as FilterOperatorValue, } from '@deephaven/filters'; import Log from '@deephaven/log'; -import { +import type { Column, CustomColumn, dh as DhType, From 8e6aa60b59a59ee217332255d1ac1945aa183a2a Mon Sep 17 00:00:00 2001 From: Vlad Babich Date: Sun, 7 May 2023 22:25:19 -0600 Subject: [PATCH 13/16] Update tsconfig in iris-grid and dashboard-core-plugins --- packages/iris-grid/tsconfig.json | 56 ++++++++------------------------ 1 file changed, 14 insertions(+), 42 deletions(-) diff --git a/packages/iris-grid/tsconfig.json b/packages/iris-grid/tsconfig.json index baa95121b7..3cf2d5dfd7 100644 --- a/packages/iris-grid/tsconfig.json +++ b/packages/iris-grid/tsconfig.json @@ -4,47 +4,19 @@ "rootDir": "src/", "outDir": "dist/" }, - "include": [ - "src/**/*.ts", - "src/**/*.tsx", - "src/**/*.js", - "src/**/*.jsx" - ], - "exclude": [ - "node_modules", - "src/**/*.test.*", - "src/**/__mocks__/*" - ], + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.js", "src/**/*.jsx"], + "exclude": ["node_modules", "src/**/*.test.*", "src/**/__mocks__/*"], "references": [ - { - "path": "../console" - }, - { - "path": "../components" - }, - { - "path": "../filters" - }, - { - "path": "../grid" - }, - { - "path": "../jsapi-shim" - }, - { - "path": "../jsapi-utils" - }, - { - "path": "../log" - }, - { - "path": "../react-hooks" - }, - { - "path": "../storage" - }, - { - "path": "../utils" - } + { "path": "../console" }, + { "path": "../components" }, + { "path": "../filters" }, + { "path": "../grid" }, + { "path": "../jsapi-shim" }, + { "path": "../jsapi-types" }, + { "path": "../jsapi-utils" }, + { "path": "../log" }, + { "path": "../react-hooks" }, + { "path": "../storage" }, + { "path": "../utils" } ] -} \ No newline at end of file +} From f1f7f75d14565d6f82f1eb0ebf2a5115b584d392 Mon Sep 17 00:00:00 2001 From: Vlad Babich Date: Mon, 8 May 2023 00:36:13 -0600 Subject: [PATCH 14/16] Fix unit tests after rebase --- ...ts => useDebouncedViewportSearch.test.tsx} | 49 +++++++++++++------ .../src/useDebouncedViewportSearch.ts | 7 ++- 2 files changed, 38 insertions(+), 18 deletions(-) rename packages/jsapi-components/src/{useDebouncedViewportSearch.test.ts => useDebouncedViewportSearch.test.tsx} (68%) diff --git a/packages/jsapi-components/src/useDebouncedViewportSearch.test.ts b/packages/jsapi-components/src/useDebouncedViewportSearch.test.tsx similarity index 68% rename from packages/jsapi-components/src/useDebouncedViewportSearch.test.ts rename to packages/jsapi-components/src/useDebouncedViewportSearch.test.tsx index 76e32e39da..0548031c8d 100644 --- a/packages/jsapi-components/src/useDebouncedViewportSearch.test.ts +++ b/packages/jsapi-components/src/useDebouncedViewportSearch.test.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import { renderHook } from '@testing-library/react-hooks'; import type { Column, @@ -5,13 +6,21 @@ import type { FilterValue, Table, } from '@deephaven/jsapi-types'; +import dh from '@deephaven/jsapi-shim'; import { TableUtils } from '@deephaven/jsapi-utils'; import { TestUtils } from '@deephaven/utils'; +import { ApiContext } from '@deephaven/jsapi-bootstrap'; import useDebouncedViewportSearch, { DEBOUNCE_VIEWPORT_SEARCH_MS, } from './useDebouncedViewportSearch'; import { UseViewportDataResult } from './useViewportData'; +const tableUtils = new TableUtils(dh); + +const wrapper = ({ children }) => ( + {children} +); + // Mock js api objects const column = TestUtils.createMockProxy({ type: 'java.lang.String', @@ -32,7 +41,9 @@ beforeEach(() => { TestUtils.asMock(columnFilterValue.contains).mockReturnValue(filterCondition); TestUtils.asMock(table.findColumn).mockReturnValue(column); - jest.spyOn(TableUtils, 'makeFilterValue').mockReturnValue(matchFilterValue); + jest + .spyOn(TableUtils.prototype, 'makeFilterValue') + .mockReturnValue(matchFilterValue); }); it.each([undefined, 400])( @@ -40,8 +51,9 @@ it.each([undefined, 400])( debounceMs => { const searchText = 'mock.searchText'; - const { result } = renderHook(() => - useDebouncedViewportSearch(viewportData, 'mock.column', debounceMs) + const { result } = renderHook( + () => useDebouncedViewportSearch(viewportData, 'mock.column', debounceMs), + { wrapper } ); result.current(searchText); @@ -50,7 +62,7 @@ it.each([undefined, 400])( jest.advanceTimersByTime(debounceMs ?? DEBOUNCE_VIEWPORT_SEARCH_MS); - expect(TableUtils.makeFilterValue).toHaveBeenCalledWith( + expect(tableUtils.makeFilterValue).toHaveBeenCalledWith( column.type, searchText ); @@ -68,14 +80,16 @@ it('should return a function that safely ignores no table', () => { const searchText = 'mock.searchText'; const debounceMs = 400; - const { result } = renderHook(() => - useDebouncedViewportSearch(viewportNoTable, 'mock.column', debounceMs) + const { result } = renderHook( + () => + useDebouncedViewportSearch(viewportNoTable, 'mock.column', debounceMs), + { wrapper } ); result.current(searchText); jest.advanceTimersByTime(debounceMs); - expect(TableUtils.makeFilterValue).not.toHaveBeenCalled(); + expect(tableUtils.makeFilterValue).not.toHaveBeenCalled(); expect(viewportNoTable.applyFiltersAndRefresh).not.toHaveBeenCalled(); }); @@ -84,14 +98,15 @@ it('should trim search text', () => { const trimmedSearchText = 'mock.searchText'; const debounceMs = 400; - const { result } = renderHook(() => - useDebouncedViewportSearch(viewportData, 'mock.column', debounceMs) + const { result } = renderHook( + () => useDebouncedViewportSearch(viewportData, 'mock.column', debounceMs), + { wrapper } ); result.current(searchText); jest.advanceTimersByTime(debounceMs); - expect(TableUtils.makeFilterValue).toHaveBeenCalledWith( + expect(tableUtils.makeFilterValue).toHaveBeenCalledWith( column.type, trimmedSearchText ); @@ -102,14 +117,15 @@ it.each(['', ' '])( searchText => { const debounceMs = 400; - const { result } = renderHook(() => - useDebouncedViewportSearch(viewportData, 'mock.column', debounceMs) + const { result } = renderHook( + () => useDebouncedViewportSearch(viewportData, 'mock.column', debounceMs), + { wrapper } ); result.current(searchText); jest.advanceTimersByTime(debounceMs); - expect(TableUtils.makeFilterValue).not.toHaveBeenCalled(); + expect(tableUtils.makeFilterValue).not.toHaveBeenCalled(); expect(viewportData.applyFiltersAndRefresh).toHaveBeenCalledWith([]); } ); @@ -118,8 +134,9 @@ it('should cancel debounce on unmount', () => { const searchText = 'mock.searchText'; const debounceMs = 400; - const { result, unmount } = renderHook(() => - useDebouncedViewportSearch(viewportData, 'mock.column', debounceMs) + const { result, unmount } = renderHook( + () => useDebouncedViewportSearch(viewportData, 'mock.column', debounceMs), + { wrapper } ); result.current(searchText); @@ -127,6 +144,6 @@ it('should cancel debounce on unmount', () => { unmount(); jest.advanceTimersByTime(debounceMs); - expect(TableUtils.makeFilterValue).not.toHaveBeenCalled(); + expect(tableUtils.makeFilterValue).not.toHaveBeenCalled(); expect(viewportData.applyFiltersAndRefresh).not.toHaveBeenCalled(); }); diff --git a/packages/jsapi-components/src/useDebouncedViewportSearch.ts b/packages/jsapi-components/src/useDebouncedViewportSearch.ts index ec70093da1..de0850ba00 100644 --- a/packages/jsapi-components/src/useDebouncedViewportSearch.ts +++ b/packages/jsapi-components/src/useDebouncedViewportSearch.ts @@ -1,6 +1,7 @@ import debounce from 'lodash.debounce'; import type { Table, TreeTable } from '@deephaven/jsapi-types'; import { TableUtils } from '@deephaven/jsapi-utils'; +import { useApi } from '@deephaven/jsapi-bootstrap'; import { useEffect, useMemo } from 'react'; import { UseViewportDataResult } from './useViewportData'; @@ -21,6 +22,8 @@ export default function useDebouncedViewportSearch< columnName: string, debounceMs = DEBOUNCE_VIEWPORT_SEARCH_MS ): (searchText: string) => void { + const dh = useApi(); + const tableUtils = useMemo(() => new TableUtils(dh), [dh]); const debouncedSearch = useMemo( () => debounce((searchText: string) => { @@ -36,7 +39,7 @@ export default function useDebouncedViewportSearch< } const column = viewportData.table.findColumn(columnName); - const value = TableUtils.makeFilterValue( + const value = tableUtils.makeFilterValue( column.type, searchTextTrimmed ); @@ -44,7 +47,7 @@ export default function useDebouncedViewportSearch< viewportData.applyFiltersAndRefresh(filter); }, debounceMs), - [columnName, debounceMs, viewportData] + [columnName, debounceMs, tableUtils, viewportData] ); useEffect( From f3a0a05f9c3d93f3c5863827d1894e732d706bad Mon Sep 17 00:00:00 2001 From: Vlad Babich Date: Mon, 8 May 2023 10:01:21 -0600 Subject: [PATCH 15/16] GridPlugin makeApi --- .../dashboard-core-plugins/src/GridPlugin.tsx | 2 ++ ...ble-operations-go-to-1-chromium-darwin.png | Bin 39389 -> 39429 bytes 2 files changed, 2 insertions(+) diff --git a/packages/dashboard-core-plugins/src/GridPlugin.tsx b/packages/dashboard-core-plugins/src/GridPlugin.tsx index 47988cedd9..acf2323079 100644 --- a/packages/dashboard-core-plugins/src/GridPlugin.tsx +++ b/packages/dashboard-core-plugins/src/GridPlugin.tsx @@ -54,6 +54,7 @@ export function GridPlugin(props: GridPluginProps): JSX.Element | null { } const metadata = { name, table: name, type: widget.type }; + const makeApi = () => Promise.resolve(dh); const makeModel = () => fetch().then((table: Table) => IrisGridModelFactory.makeModel(dh, table) @@ -67,6 +68,7 @@ export function GridPlugin(props: GridPluginProps): JSX.Element | null { localDashboardId: id, id: panelId, metadata, + makeApi, makeModel, theme, }, diff --git a/tests/table.spec.ts-snapshots/tests-table-operations-go-to-1-chromium-darwin.png b/tests/table.spec.ts-snapshots/tests-table-operations-go-to-1-chromium-darwin.png index 6218cd18ef17a3fdf8bc4033e472d0d6e6802ee3..becbc2600a3f01acbceff74c26981961d8ec1075 100644 GIT binary patch delta 8720 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 delta 8678 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 v;T-p$uiyVK`u+ki From 5128dbf0c6d8a9e064cd7a7fc98aca09fd7589b2 Mon Sep 17 00:00:00 2001 From: Vlad Babich Date: Mon, 8 May 2023 10:06:12 -0600 Subject: [PATCH 16/16] AppMainContainer makeApi --- packages/code-studio/src/main/AppMainContainer.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/code-studio/src/main/AppMainContainer.tsx b/packages/code-studio/src/main/AppMainContainer.tsx index e5a4e502c8..a37c262d7a 100644 --- a/packages/code-studio/src/main/AppMainContainer.tsx +++ b/packages/code-studio/src/main/AppMainContainer.tsx @@ -755,6 +755,7 @@ export class AppMainContainer extends Component< getDownloadWorker: DownloadServiceWorkerUtils.getServiceWorker, loadPlugin: this.handleLoadTablePlugin, localDashboardId: id, + makeApi: () => Promise.resolve(dh), makeModel: () => createGridModel(dh, connection, props.metadata, type), }; }