diff --git a/package-lock.json b/package-lock.json index c617a16f0d..feecb516ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26895,6 +26895,7 @@ "version": "0.38.0", "license": "Apache-2.0", "dependencies": { + "@deephaven/jsapi-types": "file:../jsapi-types", "@deephaven/jsapi-utils": "file:../jsapi-utils", "@deephaven/log": "file:../log", "deep-equal": "^2.0.5", @@ -28829,6 +28830,7 @@ "@deephaven/redux": { "version": "file:packages/redux", "requires": { + "@deephaven/jsapi-types": "file:../jsapi-types", "@deephaven/jsapi-utils": "file:../jsapi-utils", "@deephaven/log": "file:../log", "@deephaven/tsconfig": "file:../tsconfig", diff --git a/packages/chart/README.md b/packages/chart/README.md index 38b1aee411..ce9391639e 100644 --- a/packages/chart/README.md +++ b/packages/chart/README.md @@ -14,5 +14,5 @@ Then, import and use the component from the package: import { Chart } from '@deephaven/chart'; // In your render function - + ``` \ No newline at end of file diff --git a/packages/chart/src/Chart.tsx b/packages/chart/src/Chart.tsx index d418b83a9b..7f8f0b2900 100644 --- a/packages/chart/src/Chart.tsx +++ b/packages/chart/src/Chart.tsx @@ -7,6 +7,7 @@ import { dhWarningFilled, IconDefinition, } from '@deephaven/icons'; +import type { dh as DhType } from '@deephaven/jsapi-types'; import { Formatter, FormatterUtils, @@ -35,6 +36,7 @@ type FormatterSettings = ColumnFormatSettings & }; interface ChartProps { + dh: DhType; model: ChartModel; settings: FormatterSettings; isActive: boolean; @@ -505,7 +507,9 @@ export class Chart extends Component { } updateFormatter(): void { + const { dh } = this.props; const formatter = new Formatter( + dh, this.columnFormats, this.dateTimeFormatterOptions, this.decimalFormatOptions, diff --git a/packages/chart/src/ChartUtils.test.ts b/packages/chart/src/ChartUtils.test.ts index ebc4abeb60..ce57fdaf9b 100644 --- a/packages/chart/src/ChartUtils.test.ts +++ b/packages/chart/src/ChartUtils.test.ts @@ -8,7 +8,7 @@ import ChartTheme from './ChartTheme'; const chartUtils = new ChartUtils(dh); function makeFormatter() { - return new Formatter(); + return new Formatter(dh); } it('groups the axes by type properly', () => { diff --git a/packages/code-studio/src/main/AppInit.tsx b/packages/code-studio/src/main/AppInit.tsx index 1406c6975d..e2a5661fa7 100644 --- a/packages/code-studio/src/main/AppInit.tsx +++ b/packages/code-studio/src/main/AppInit.tsx @@ -20,8 +20,8 @@ import { ToolType, } from '@deephaven/dashboard-core-plugins'; import { FileStorage } from '@deephaven/file-explorer'; -import { useClient } from '@deephaven/jsapi-bootstrap'; -import type { IdeConnection } from '@deephaven/jsapi-types'; +import { useApi, useClient } from '@deephaven/jsapi-bootstrap'; +import type { dh as DhType, IdeConnection } from '@deephaven/jsapi-types'; import { DecimalColumnFormatter, getSessionDetails, @@ -36,6 +36,7 @@ import { getWorkspaceStorage, RootState, setActiveTool as setActiveToolAction, + setApi as setApiAction, setCommandHistoryStorage as setCommandHistoryStorageAction, setFileStorage as setFileStorageAction, setPlugins as setPluginsAction, @@ -64,6 +65,7 @@ interface AppInitProps { workspaceStorage: WorkspaceStorage; setActiveTool: (type: typeof ToolType[keyof typeof ToolType]) => void; + setApi: (api: DhType) => void; setCommandHistoryStorage: (storage: PouchCommandHistoryStorage) => void; setDashboardData: ( id: string, @@ -87,6 +89,7 @@ function AppInit(props: AppInitProps) { const { workspace, setActiveTool, + setApi, setCommandHistoryStorage, setDashboardData, setFileStorage, @@ -100,6 +103,7 @@ function AppInit(props: AppInitProps) { setServerConfigValues, } = props; + const api = useApi(); const client = useClient(); const connection = useConnection(); const plugins = usePlugins(); @@ -212,7 +216,7 @@ function AppInit(props: AppInitProps) { ...userPermissionsOverrides, }, }; - + setApi(api); setActiveTool(ToolType.DEFAULT); setServerConfigValues(serverConfig); setCommandHistoryStorage(commandHistoryStorage); @@ -234,9 +238,11 @@ function AppInit(props: AppInitProps) { loadApp(); }, [ + api, client, connection, setActiveTool, + setApi, setCommandHistoryStorage, setDashboardData, setFileStorage, @@ -297,6 +303,7 @@ const mapStateToProps = (state: RootState) => ({ const ConnectedAppInit = connect(mapStateToProps, { setActiveTool: setActiveToolAction, + setApi: setApiAction, setCommandHistoryStorage: setCommandHistoryStorageAction, setDashboardData: setDashboardDataAction, setFileStorage: setFileStorageAction, diff --git a/packages/code-studio/src/main/WidgetUtils.ts b/packages/code-studio/src/main/WidgetUtils.ts index 3aca796b5e..102af7f5ff 100644 --- a/packages/code-studio/src/main/WidgetUtils.ts +++ b/packages/code-studio/src/main/WidgetUtils.ts @@ -1,5 +1,5 @@ import { ChartModel, ChartModelFactory } from '@deephaven/chart'; -import { +import type { dh as DhType, Table, VariableTypeUnion, diff --git a/packages/code-studio/src/settings/ColumnSpecificSectionContent.test.tsx b/packages/code-studio/src/settings/ColumnSpecificSectionContent.test.tsx index d3dfbc258a..ad25436759 100644 --- a/packages/code-studio/src/settings/ColumnSpecificSectionContent.test.tsx +++ b/packages/code-studio/src/settings/ColumnSpecificSectionContent.test.tsx @@ -7,7 +7,8 @@ import { WorkspaceSettings } from '@deephaven/redux'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { Formatter } from '@deephaven/jsapi-utils'; - +import { ApiContext } from '@deephaven/jsapi-bootstrap'; +import dh from '@deephaven/jsapi-shim'; import { ColumnSpecificSectionContent, ColumnSpecificSectionContentProps, @@ -34,19 +35,22 @@ function renderContent({ }, }: Partial = {}) { return render( - + + + ); } diff --git a/packages/code-studio/src/settings/ColumnSpecificSectionContent.tsx b/packages/code-studio/src/settings/ColumnSpecificSectionContent.tsx index a2e86bab7b..f107fe6b6e 100644 --- a/packages/code-studio/src/settings/ColumnSpecificSectionContent.tsx +++ b/packages/code-studio/src/settings/ColumnSpecificSectionContent.tsx @@ -19,7 +19,9 @@ import { TableColumnFormat, FormattingRule, } from '@deephaven/jsapi-utils'; +import { dh as DhType } from '@deephaven/jsapi-types'; import { + getApi, getDefaultDateTimeFormat, getDefaultDecimalFormatOptions, getDefaultIntegerFormatOptions, @@ -42,12 +44,14 @@ import { isValidFormat, removeFormatRuleExtraProps, isFormatRuleValidForSave, + ValidFormatterItem, } from './SettingsUtils'; import type { FormatterItem, FormatOption } from './SettingsUtils'; import ColumnTypeOptions from './ColumnTypeOptions'; import DateTimeOptions from './DateTimeOptions'; export interface ColumnSpecificSectionContentProps { + dh: DhType; formatter: FormatterItem[]; defaultDateTimeFormat: string; showTimeZone: boolean; @@ -288,10 +292,13 @@ export class ColumnSpecificSectionContent extends PureComponent< defaultIntegerFormatOptions, truncateNumbersWithPound, } = this.state; + const { dh } = this.props; const formatter = formatSettings - .filter(isFormatRuleValidForSave) + .filter((format): format is ValidFormatterItem => + isFormatRuleValidForSave(dh, format) + ) .map(removeFormatRuleExtraProps) ?? []; const { settings, saveSettings } = this.props; @@ -306,6 +313,7 @@ export class ColumnSpecificSectionContent extends PureComponent< }; if ( isValidFormat( + dh, TableUtils.dataType.DECIMAL, DecimalColumnFormatter.makeCustomFormat( defaultDecimalFormatOptions.defaultFormatString @@ -316,6 +324,7 @@ export class ColumnSpecificSectionContent extends PureComponent< } if ( isValidFormat( + dh, TableUtils.dataType.INT, IntegerColumnFormatter.makeCustomFormat( defaultIntegerFormatOptions.defaultFormatString @@ -339,6 +348,7 @@ export class ColumnSpecificSectionContent extends PureComponent< getRuleError( rule: FormatterItem ): { hasColumnNameError: boolean; hasFormatError: boolean; message: string } { + const { dh } = this.props; const error = { hasColumnNameError: false, hasFormatError: false, @@ -369,7 +379,7 @@ export class ColumnSpecificSectionContent extends PureComponent< ) { error.hasFormatError = true; errorMessages.push('Empty formatting rule.'); - } else if (!isValidFormat(rule.columnType, rule.format)) { + } else if (!isValidFormat(dh, rule.columnType, rule.format)) { error.hasFormatError = true; errorMessages.push('Invalid formatting rule.'); } @@ -637,6 +647,7 @@ const mapStateToProps = (state: RootState) => ({ defaultDateTimeFormat: getDefaultDateTimeFormat(state), defaultDecimalFormatOptions: getDefaultDecimalFormatOptions(state), defaultIntegerFormatOptions: getDefaultIntegerFormatOptions(state), + dh: getApi(state), showTimeZone: getShowTimeZone(state), showTSeparator: getShowTSeparator(state), truncateNumbersWithPound: getTruncateNumbersWithPound(state), diff --git a/packages/code-studio/src/settings/DateTimeOptions.tsx b/packages/code-studio/src/settings/DateTimeOptions.tsx index 4ff8aafa2f..e797a3572c 100644 --- a/packages/code-studio/src/settings/DateTimeOptions.tsx +++ b/packages/code-studio/src/settings/DateTimeOptions.tsx @@ -1,9 +1,10 @@ -import React, { ReactElement } from 'react'; +import React, { ReactElement, useMemo } from 'react'; import { Formatter, DateTimeColumnFormatter, TableUtils, } from '@deephaven/jsapi-utils'; +import { useApi } from '@deephaven/jsapi-bootstrap'; interface DateTimeOptionProps { timestamp: Date; @@ -26,11 +27,17 @@ export default function DateTimeOptions( legacyGlobalFormat, } = props; - const formatter = new Formatter([], { - timeZone, - showTimeZone, - showTSeparator, - }); + const dh = useApi(); + + const formatter = useMemo( + () => + new Formatter(dh, [], { + timeZone, + showTimeZone, + showTSeparator, + }), + [dh, showTimeZone, showTSeparator, timeZone] + ); const formats = isGlobalOptions ? DateTimeColumnFormatter.getGlobalFormats(showTimeZone, showTSeparator) : DateTimeColumnFormatter.getFormats(showTimeZone, showTSeparator); diff --git a/packages/code-studio/src/settings/FormattingSectionContent.test.tsx b/packages/code-studio/src/settings/FormattingSectionContent.test.tsx index 341b3e3e7b..85fa85c612 100644 --- a/packages/code-studio/src/settings/FormattingSectionContent.test.tsx +++ b/packages/code-studio/src/settings/FormattingSectionContent.test.tsx @@ -1,6 +1,8 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import { ApiContext } from '@deephaven/jsapi-bootstrap'; +import dh from '@deephaven/jsapi-shim'; import { DateTimeColumnFormatter } from '@deephaven/jsapi-utils'; import { WorkspaceSettings } from '@deephaven/redux'; import { assertNotNull } from '@deephaven/utils'; @@ -45,20 +47,23 @@ function renderSectionContent({ truncateNumbersWithPound = false, } = {}) { return render( - + + + ); } diff --git a/packages/code-studio/src/settings/FormattingSectionContent.tsx b/packages/code-studio/src/settings/FormattingSectionContent.tsx index a62f7308e5..94d68f2858 100644 --- a/packages/code-studio/src/settings/FormattingSectionContent.tsx +++ b/packages/code-studio/src/settings/FormattingSectionContent.tsx @@ -16,8 +16,10 @@ import { DecimalColumnFormatter, TableUtils, } from '@deephaven/jsapi-utils'; +import { dh as DhType } from '@deephaven/jsapi-types'; import Log from '@deephaven/log'; import { + getApi, getDefaultDateTimeFormat, getDefaultDecimalFormatOptions, getDefaultIntegerFormatOptions, @@ -40,6 +42,7 @@ import { isValidFormat, removeFormatRuleExtraProps, isFormatRuleValidForSave, + ValidFormatterItem, } from './SettingsUtils'; import type { FormatterItem, FormatOption } from './SettingsUtils'; import DateTimeOptions from './DateTimeOptions'; @@ -48,6 +51,7 @@ import TimeZoneOptions from './TimeZoneOptions'; const log = Log.module('FormattingSectionContent'); interface FormattingSectionContentProps { + dh: DhType; formatter: FormatterItem[]; defaultDateTimeFormat: string; showTimeZone: boolean; @@ -353,10 +357,13 @@ export class FormattingSectionContent extends PureComponent< defaultIntegerFormatOptions, truncateNumbersWithPound, } = this.state; + const { dh } = this.props; const formatter = formatSettings - .filter(isFormatRuleValidForSave) + .filter((format): format is ValidFormatterItem => + isFormatRuleValidForSave(dh, format) + ) .map(removeFormatRuleExtraProps) ?? []; const { settings, saveSettings } = this.props; @@ -371,6 +378,7 @@ export class FormattingSectionContent extends PureComponent< }; if ( isValidFormat( + dh, TableUtils.dataType.DECIMAL, DecimalColumnFormatter.makeCustomFormat( defaultDecimalFormatOptions.defaultFormatString @@ -381,6 +389,7 @@ export class FormattingSectionContent extends PureComponent< } if ( isValidFormat( + dh, TableUtils.dataType.INT, IntegerColumnFormatter.makeCustomFormat( defaultIntegerFormatOptions.defaultFormatString @@ -393,7 +402,7 @@ export class FormattingSectionContent extends PureComponent< } render(): ReactElement { - const { defaults } = this.props; + const { defaults, dh } = this.props; const { defaultDateTimeFormat, defaultDecimalFormatOptions, @@ -532,6 +541,7 @@ export class FormattingSectionContent extends PureComponent< 'default-decimal-format-input', { 'is-invalid': !isValidFormat( + dh, TableUtils.dataType.DECIMAL, DecimalColumnFormatter.makeCustomFormat( defaultDecimalFormatString @@ -575,6 +585,7 @@ export class FormattingSectionContent extends PureComponent< 'default-integer-format-input', { 'is-invalid': !isValidFormat( + dh, TableUtils.dataType.INT, IntegerColumnFormatter.makeCustomFormat( defaultIntegerFormatString @@ -624,6 +635,7 @@ const mapStateToProps = (state: RootState) => ({ defaultDateTimeFormat: getDefaultDateTimeFormat(state), defaultDecimalFormatOptions: getDefaultDecimalFormatOptions(state), defaultIntegerFormatOptions: getDefaultIntegerFormatOptions(state), + dh: getApi(state), showTimeZone: getShowTimeZone(state), showTSeparator: getShowTSeparator(state), truncateNumbersWithPound: getTruncateNumbersWithPound(state), diff --git a/packages/code-studio/src/settings/SettingsUtils.tsx b/packages/code-studio/src/settings/SettingsUtils.tsx index db069bd9e1..1a648e5aa6 100644 --- a/packages/code-studio/src/settings/SettingsUtils.tsx +++ b/packages/code-studio/src/settings/SettingsUtils.tsx @@ -6,9 +6,10 @@ import { TableColumnFormat, FormattingRule, } from '@deephaven/jsapi-utils'; +import { dh as DhType } from '@deephaven/jsapi-types'; import Log from '@deephaven/log'; -const log = Log.module('FormattingSectionContent'); +const log = Log.module('SettingsUtils'); export type FormatOption = { defaultFormatString?: string; @@ -68,6 +69,7 @@ export function isValidColumnName(name: string): boolean { } export function isValidFormat( + dh: DhType, columnType: string, format: Partial ): boolean { @@ -81,11 +83,11 @@ export function isValidFormat( } switch (columnType) { case 'datetime': - return DateTimeColumnFormatter.isValid(format); + return DateTimeColumnFormatter.isValid(dh, format); case 'decimal': - return DecimalColumnFormatter.isValid(format); + return DecimalColumnFormatter.isValid(dh, format); case 'int': - return IntegerColumnFormatter.isValid(format); + return IntegerColumnFormatter.isValid(dh, format); default: { log.warn('Trying to validate format of unknown type'); return true; @@ -101,11 +103,12 @@ export function removeFormatRuleExtraProps( } export function isFormatRuleValidForSave( + dh: DhType, rule: FormatterItem ): rule is ValidFormatterItem { return ( isValidColumnName(rule.columnName) && - isValidFormat(rule.columnType, rule.format) + isValidFormat(dh, rule.columnType, rule.format) ); } diff --git a/packages/code-studio/src/styleguide/Charts.tsx b/packages/code-studio/src/styleguide/Charts.tsx index 8e375b70b0..1368702093 100644 --- a/packages/code-studio/src/styleguide/Charts.tsx +++ b/packages/code-studio/src/styleguide/Charts.tsx @@ -10,7 +10,7 @@ function Charts(): ReactElement {

Chart

- +
); diff --git a/packages/dashboard-core-plugins/src/linker/Linker.test.tsx b/packages/dashboard-core-plugins/src/linker/Linker.test.tsx index 6d5afdbcf1..27bec46446 100644 --- a/packages/dashboard-core-plugins/src/linker/Linker.test.tsx +++ b/packages/dashboard-core-plugins/src/linker/Linker.test.tsx @@ -6,6 +6,7 @@ import { PanelManager, } from '@deephaven/dashboard'; import GoldenLayout, { Config } from '@deephaven/golden-layout'; +import dh from '@deephaven/jsapi-shim'; import { TypeValue as FilterTypeValue } from '@deephaven/filters'; import ToolType from './ToolType'; import { Linker } from './Linker'; @@ -77,6 +78,7 @@ function mountLinker({ } = {}) { return render( ({ activeTool: getActiveTool(state), + dh: getApi(state), isolatedLinkerPanelId: getIsolatedLinkerPanelIdForDashboard( state, ownProps.localDashboardId @@ -479,7 +483,7 @@ export class Linker extends Component { handleUpdateValues(panel: PanelComponent, dataMap: RowDataMap): void { const panelId = LayoutUtils.getIdFromPanel(panel); - const { links, timeZone } = this.props; + const { dh, links, timeZone } = this.props; // Map of panel ID to filterMap const panelFilterMap = new Map(); // Instead of setting filters one by one for each link, @@ -508,7 +512,7 @@ export class Linker extends Component { value = undefined; } if (columnType != null && TableUtils.isDateType(columnType)) { - const dateFilterFormatter = new DateTimeColumnFormatter({ + const dateFilterFormatter = new DateTimeColumnFormatter(dh, { timeZone, showTimeZone: false, showTSeparator: true, diff --git a/packages/dashboard-core-plugins/src/panels/ChartPanel.tsx b/packages/dashboard-core-plugins/src/panels/ChartPanel.tsx index a12084ef6f..eaf6455120 100644 --- a/packages/dashboard-core-plugins/src/panels/ChartPanel.tsx +++ b/packages/dashboard-core-plugins/src/panels/ChartPanel.tsx @@ -1056,6 +1056,7 @@ export class ChartPanel extends Component { } = this.props; const { columnMap, + dh, filterMap, error, model, @@ -1123,8 +1124,9 @@ export class ChartPanel extends Component { className="chart-panel-container h-100 w-100" >
- {isLoaded && model && ( + {isLoaded && model && dh && ( = {}) { return render( { state, localDashboardId ), + dh: getApi(state), isLinkerActive, disableLinking, settings: getSettings(state), @@ -131,13 +143,6 @@ export class DropdownFilterPanel extends Component< static MAX_TABLE_SIZE = 256; - // Filter dropdown needs to show and send full timestamp format with nanoseconds - static DATETIME_FORMATTER = new DateTimeColumnFormatter({ - showTimeZone: false, - showTSeparator: true, - defaultDateTimeFormatString: `yyyy-MM-dd HH:mm:ss.SSSSSSSSS`, - }); - static SOURCE_COLUMN = Object.freeze({ name: 'FilterSource', type: null, @@ -165,14 +170,20 @@ export class DropdownFilterPanel extends Component< this.panelContainer = React.createRef(); this.pending = new Pending(); - const { panelState, settings } = props; + const { dh, panelState, settings } = props; this.columnFormats = FormatterUtils.getColumnFormats(settings); + // Filter dropdown needs to show and send full timestamp format with nanoseconds + this.dateTimeFormatter = new DateTimeColumnFormatter(dh, { + showTimeZone: false, + showTSeparator: true, + defaultDateTimeFormatString: `yyyy-MM-dd HH:mm:ss.SSSSSSSSS`, + }); const { value = '', isValueShown = false, name, type, timestamp } = panelState ?? {}; const column = name != null && type != null ? { name, type } : undefined; this.state = { column, - formatter: new Formatter(this.columnFormats), + formatter: new Formatter(dh, this.columnFormats), valuesTable: undefined, valuesColumn: undefined, sourceSize: 0, @@ -266,6 +277,8 @@ export class DropdownFilterPanel extends Component< } } + dateTimeFormatter: DateTimeColumnFormatter; + dropdownFilterRef: RefObject; panelContainer: RefObject; @@ -284,7 +297,7 @@ export class DropdownFilterPanel extends Component< ) => { if (type !== undefined && TableUtils.isDateType(type)) { return rawValues.map(value => - DropdownFilterPanel.DATETIME_FORMATTER.format(value as number) + this.dateTimeFormatter.format(value as number) ); } return rawValues.map(value => @@ -395,6 +408,7 @@ export class DropdownFilterPanel extends Component< } startListeningToSource(sourceTable: TableTemplate): void { + const { dh } = this.props; log.debug('startListeningToSource'); sourceTable.addEventListener( dh.Table.EVENT_FILTERCHANGED, @@ -419,6 +433,7 @@ export class DropdownFilterPanel extends Component< } stopListeningToSource(sourceTable: TableTemplate): void { + const { dh } = this.props; log.debug('stopListeningToSource'); sourceTable.removeEventListener( dh.Table.EVENT_FILTERCHANGED, @@ -650,6 +665,7 @@ export class DropdownFilterPanel extends Component< } updateViewportListener(valuesTable: TableTemplate): void { + const { dh } = this.props; log.debug('updateViewportListener', valuesTable?.size); if (this.cleanup) { @@ -684,10 +700,11 @@ export class DropdownFilterPanel extends Component< updateFormatterSettings(settings: { formatter?: FormattingRule[] | undefined; }): void { + const { dh } = this.props; const columnFormats = FormatterUtils.getColumnFormats(settings); if (!deepEqual(this.columnFormats, columnFormats)) { this.columnFormats = columnFormats; - this.setState({ formatter: new Formatter(columnFormats) }); + this.setState({ formatter: new Formatter(dh, columnFormats) }); } } diff --git a/packages/embed-chart/src/App.tsx b/packages/embed-chart/src/App.tsx index d50c296885..7a79bfd0a7 100644 --- a/packages/embed-chart/src/App.tsx +++ b/packages/embed-chart/src/App.tsx @@ -84,7 +84,7 @@ function App(): JSX.Element { return (
- {isLoaded && } + {isLoaded && } {!isLoaded && ( { shownColumnTooltip: null, - formatter: new Formatter(), + formatter: new Formatter(dh), isMenuShown: false, customColumnFormatMap: new Map(customColumnFormatMap), @@ -1822,6 +1822,7 @@ export class IrisGrid extends Component { forceUpdate = true ): void { const { customColumnFormatMap } = this.state; + const { dh } = this.props; const update = { customColumnFormatMap, ...updatedFormats, @@ -1831,6 +1832,7 @@ export class IrisGrid extends Component { ...update.customColumnFormatMap.values(), ]; const formatter = new Formatter( + dh, mergedColumnFormats, this.dateTimeFormatterOptions, this.decimalFormatOptions, diff --git a/packages/iris-grid/src/IrisGridModel.test.ts b/packages/iris-grid/src/IrisGridModel.test.ts index 4f026caf88..849cbf8316 100644 --- a/packages/iris-grid/src/IrisGridModel.test.ts +++ b/packages/iris-grid/src/IrisGridModel.test.ts @@ -228,7 +228,12 @@ describe('pending new rows tests', () => { inputTable = IrisGridTestUtils.makeInputTable(table.columns.slice(0, 3)); - model = IrisGridTestUtils.makeModel(dh, table, new Formatter(), inputTable); + model = IrisGridTestUtils.makeModel( + dh, + table, + new Formatter(dh), + inputTable + ); model.pendingRowCount = PENDING_ROW_COUNT; }); diff --git a/packages/iris-grid/src/IrisGridModelFactory.ts b/packages/iris-grid/src/IrisGridModelFactory.ts index f303d1a938..455cb51f0c 100644 --- a/packages/iris-grid/src/IrisGridModelFactory.ts +++ b/packages/iris-grid/src/IrisGridModelFactory.ts @@ -15,7 +15,7 @@ class IrisGridModelFactory { static async makeModel( dh: DhType, table: Table | TreeTable, - formatter = new Formatter() + formatter = new Formatter(dh) ): Promise { let inputTable = null; if (!TableUtils.isTreeTable(table) && table.hasInputTable) { diff --git a/packages/iris-grid/src/IrisGridProxyModel.ts b/packages/iris-grid/src/IrisGridProxyModel.ts index cf626ed740..dab6750756 100644 --- a/packages/iris-grid/src/IrisGridProxyModel.ts +++ b/packages/iris-grid/src/IrisGridProxyModel.ts @@ -81,7 +81,7 @@ class IrisGridProxyModel extends IrisGridModel { constructor( dh: DhType, table: Table | TreeTable, - formatter = new Formatter(), + formatter = new Formatter(dh), inputTable: InputTable | null = null ) { super(); diff --git a/packages/iris-grid/src/IrisGridTableModel.ts b/packages/iris-grid/src/IrisGridTableModel.ts index 00ea607f95..6c4f523c73 100644 --- a/packages/iris-grid/src/IrisGridTableModel.ts +++ b/packages/iris-grid/src/IrisGridTableModel.ts @@ -43,7 +43,7 @@ class IrisGridTableModel extends IrisGridTableModelTemplate { constructor( dh: DhType, table: Table, - formatter = new Formatter(), + formatter = new Formatter(dh), inputTable: InputTable | null = null ) { super(dh, table, formatter, inputTable); diff --git a/packages/iris-grid/src/IrisGridTableModelTemplate.ts b/packages/iris-grid/src/IrisGridTableModelTemplate.ts index 37e04358ff..66bf67bb21 100644 --- a/packages/iris-grid/src/IrisGridTableModelTemplate.ts +++ b/packages/iris-grid/src/IrisGridTableModelTemplate.ts @@ -203,7 +203,7 @@ class IrisGridTableModelTemplate< constructor( dh: DhType, table: T, - formatter = new Formatter(), + formatter = new Formatter(dh), inputTable: InputTable | null = null ) { super(); diff --git a/packages/iris-grid/src/IrisGridTestUtils.ts b/packages/iris-grid/src/IrisGridTestUtils.ts index 787e9087c0..5e224f4e94 100644 --- a/packages/iris-grid/src/IrisGridTestUtils.ts +++ b/packages/iris-grid/src/IrisGridTestUtils.ts @@ -123,7 +123,7 @@ class IrisGridTestUtils { static makeModel( dh: DhType, table = IrisGridTestUtils.makeTable(), - formatter = new Formatter(), + formatter = new Formatter(dh), inputTable: InputTable | null = null ): IrisGridProxyModel { return new IrisGridProxyModel(dh, table, formatter, inputTable); diff --git a/packages/iris-grid/src/format-context-menus/DecimalFormatContextMenu.ts b/packages/iris-grid/src/format-context-menus/DecimalFormatContextMenu.ts index 647ec86295..f200e3513e 100644 --- a/packages/iris-grid/src/format-context-menus/DecimalFormatContextMenu.ts +++ b/packages/iris-grid/src/format-context-menus/DecimalFormatContextMenu.ts @@ -2,6 +2,7 @@ import { DecimalColumnFormat, DecimalColumnFormatter, } from '@deephaven/jsapi-utils'; +import type { dh as DhType } from '@deephaven/jsapi-types'; import FormatContextMenuUtils, { FormatContextMenuOption, } from './FormatContextMenuUtils'; @@ -17,11 +18,13 @@ class DecimalFormatContextMenu { /** * Creates list of formatting options for Decimal context menu + * @param dh JSAPI instance * @param selectedFormat Selected format object, null for no selected format * @param onCustomFormatChange Callback to call when the custom format is changed * @returns Array of formatting options for the context menu */ static getOptions( + dh: DhType, selectedFormat: DecimalColumnFormat, onCustomFormatChange: (value: DecimalColumnFormat | null) => void ): FormatContextMenuOption[] { @@ -90,7 +93,7 @@ class DecimalFormatContextMenu { const newCustomFormat = DecimalColumnFormatter.makeCustomFormat( formatString ); - if (DecimalColumnFormatter.isValid(newCustomFormat)) { + if (DecimalColumnFormatter.isValid(dh, newCustomFormat)) { onCustomFormatChange(newCustomFormat); } } else { diff --git a/packages/iris-grid/src/format-context-menus/IntegerFormatContextMenu.ts b/packages/iris-grid/src/format-context-menus/IntegerFormatContextMenu.ts index d00e6f4be5..c854c08313 100644 --- a/packages/iris-grid/src/format-context-menus/IntegerFormatContextMenu.ts +++ b/packages/iris-grid/src/format-context-menus/IntegerFormatContextMenu.ts @@ -2,6 +2,7 @@ import { IntegerColumnFormat, IntegerColumnFormatter, } from '@deephaven/jsapi-utils'; +import type { dh as DhType } from '@deephaven/jsapi-types'; import FormatContextMenuUtils, { FormatContextMenuOption, } from './FormatContextMenuUtils'; @@ -15,11 +16,13 @@ class IntegerFormatContextMenu { /** * Creates list of formatting options for Integer context menu + * @param dh JSAPI instance * @param selectedFormat Selected format object, null for no selected format * @param onCustomFormatChange Callback to call when the custom format is changed * @returns Array of formatting options for the context menu */ static getOptions( + dh: DhType, selectedFormat: IntegerColumnFormat, onCustomFormatChange: (value: IntegerColumnFormat | null) => void ): FormatContextMenuOption[] { @@ -68,7 +71,7 @@ class IntegerFormatContextMenu { const newCustomFormat = IntegerColumnFormatter.makeCustomFormat( formatString ); - if (IntegerColumnFormatter.isValid(newCustomFormat)) { + if (IntegerColumnFormatter.isValid(dh, newCustomFormat)) { onCustomFormatChange(newCustomFormat); } } else { diff --git a/packages/iris-grid/src/mousehandlers/IrisGridContextMenuHandler.tsx b/packages/iris-grid/src/mousehandlers/IrisGridContextMenuHandler.tsx index d55a00a0a2..4b3013f867 100644 --- a/packages/iris-grid/src/mousehandlers/IrisGridContextMenuHandler.tsx +++ b/packages/iris-grid/src/mousehandlers/IrisGridContextMenuHandler.tsx @@ -360,7 +360,7 @@ class IrisGridContextMenuHandler extends GridMouseHandler { gridPoint: GridPoint ): ContextAction[] { assertNotNull(modelColumn); - const { irisGrid } = this; + const { dh, irisGrid } = this; const { column: columnIndex, row: rowIndex } = gridPoint; const { model, canCopy } = irisGrid.props; const { columns } = model; @@ -378,13 +378,13 @@ class IrisGridContextMenuHandler extends GridMouseHandler { const { filterIconColor } = theme; const { settings } = irisGrid.props; - const dateFilterFormatter = new DateTimeColumnFormatter({ + const dateFilterFormatter = new DateTimeColumnFormatter(dh, { timeZone: settings?.timeZone, showTimeZone: false, showTSeparator: true, defaultDateTimeFormatString: CONTEXT_MENU_DATE_FORMAT, }); - const previewFilterFormatter = new DateTimeColumnFormatter({ + const previewFilterFormatter = new DateTimeColumnFormatter(dh, { timeZone: settings?.timeZone, showTimeZone: settings?.showTimeZone, showTSeparator: settings?.showTSeparator, @@ -840,6 +840,7 @@ class IrisGridContextMenuHandler extends GridMouseHandler { numberFormatActions(column: Column): ContextAction[] | null { const { model } = this.irisGrid.props; const { formatter } = model; + const { dh } = this; const selectedFormat = formatter.getColumnFormat( column.type, column.name @@ -849,6 +850,7 @@ class IrisGridContextMenuHandler extends GridMouseHandler { const columnIndex = model.getColumnIndexByName(column.name); if (TableUtils.isDecimalType(column.type)) { formatOptions = DecimalFormatContextMenu.getOptions( + dh, selectedFormat, format => { assertNotNull(columnIndex); @@ -857,6 +859,7 @@ class IrisGridContextMenuHandler extends GridMouseHandler { ); } else if (TableUtils.isIntegerType(column.type)) { formatOptions = IntegerFormatContextMenu.getOptions( + dh, selectedFormat, format => { assertNotNull(columnIndex); diff --git a/packages/jsapi-components/src/TableInput.tsx b/packages/jsapi-components/src/TableInput.tsx index b739c97dbc..540ae4eea5 100644 --- a/packages/jsapi-components/src/TableInput.tsx +++ b/packages/jsapi-components/src/TableInput.tsx @@ -15,6 +15,7 @@ import type { LongWrapper, Table } from '@deephaven/jsapi-types'; import { PromiseUtils } from '@deephaven/utils'; import Log from '@deephaven/log'; import { Formatter, FormatterUtils, Settings } from '@deephaven/jsapi-utils'; +import { useApi } from '@deephaven/jsapi-bootstrap'; import useTableColumn from './useTableColumn'; import './TableInput.scss'; @@ -54,6 +55,7 @@ function TableInput(props: TableInputProps): JSX.Element { table: tablePromise, } = props; const parentRef = useRef(null); + const dh = useApi(); const formatter = useMemo(() => { const columnFormats = FormatterUtils.getColumnFormats(settings); const dateTimeFormatterOptions = FormatterUtils.getDateTimeFormatterOptions( @@ -64,12 +66,13 @@ function TableInput(props: TableInputProps): JSX.Element { defaultIntegerFormatOptions = {}, } = settings; return new Formatter( + dh, columnFormats, dateTimeFormatterOptions, defaultDecimalFormatOptions, defaultIntegerFormatOptions ); - }, [settings]); + }, [dh, settings]); const [searchValue, setSearchValue] = useState(''); const [selection, setSelection] = useState(new Set(defaultValue)); const [table, setTable] = useState(); diff --git a/packages/jsapi-components/src/useViewportData.test.ts b/packages/jsapi-components/src/useViewportData.test.tsx similarity index 88% rename from packages/jsapi-components/src/useViewportData.test.ts rename to packages/jsapi-components/src/useViewportData.test.tsx index 791bdf7c8c..326a838cd9 100644 --- a/packages/jsapi-components/src/useViewportData.test.ts +++ b/packages/jsapi-components/src/useViewportData.test.tsx @@ -1,5 +1,8 @@ +import React from 'react'; import { act, renderHook } from '@testing-library/react-hooks'; import type { FilterCondition, Table } from '@deephaven/jsapi-types'; +import { ApiContext } from '@deephaven/jsapi-bootstrap'; +import dh from '@deephaven/jsapi-shim'; import { OnTableUpdatedEvent, ViewportRow, @@ -53,6 +56,10 @@ const optionsUseDefaults: UseViewportDataProps = { table, }; +const wrapper: React.FC
= ({ children }) => ( + {children} +); + beforeEach(() => { jest.clearAllMocks(); TestUtils.asMock(useTableSize).mockImplementation(t => t?.size ?? 0); @@ -61,7 +68,7 @@ beforeEach(() => { it.each([options, optionsUseDefaults])( 'should initialize viewport data: %o', opt => { - const { result } = renderHook(() => useViewportData(opt)); + const { result } = renderHook(() => useViewportData(opt), { wrapper }); const expected = { initialItems: [...generateEmptyKeyedItems(0, table.size - 1)], @@ -75,12 +82,12 @@ it.each([options, optionsUseDefaults])( ); it('should return table', () => { - const { result } = renderHook(() => useViewportData(options)); + const { result } = renderHook(() => useViewportData(options), { wrapper }); expect(result.current.table).toBe(options.table); }); it('should return a callback that can apply filters and refresh viewport', () => { - const { result } = renderHook(() => useViewportData(options)); + const { result } = renderHook(() => useViewportData(options), { wrapper }); jest.clearAllMocks(); const filters: FilterCondition[] = []; @@ -95,7 +102,7 @@ it('should return a callback that can apply filters and refresh viewport', () => }); it('should set viewport if table size changes', () => { - const { rerender } = renderHook(() => useViewportData(options)); + const { rerender } = renderHook(() => useViewportData(options), { wrapper }); jest.clearAllMocks(); rerender(); @@ -113,6 +120,7 @@ it('should set viewport if table reference changes', () => { t => useViewportData({ ...options, table: t }), { initialProps: table, + wrapper, } ); jest.clearAllMocks(); @@ -128,7 +136,7 @@ it('should set viewport if table reference changes', () => { }); it('should update state on dh.Table.EVENT_UPDATED event', () => { - const { result } = renderHook(() => useViewportData(options)); + const { result } = renderHook(() => useViewportData(options), { wrapper }); // Extract the last event handler that was registered since it should have // a closure over our latest `viewportData` instance. diff --git a/packages/jsapi-components/src/useViewportData.ts b/packages/jsapi-components/src/useViewportData.ts index 3eeae4d235..49057b89c7 100644 --- a/packages/jsapi-components/src/useViewportData.ts +++ b/packages/jsapi-components/src/useViewportData.ts @@ -8,6 +8,7 @@ import { defaultRowDeserializer, isClosed, } from '@deephaven/jsapi-utils'; +import { useApi } from '@deephaven/jsapi-bootstrap'; import useInitializeViewportData from './useInitializeViewportData'; import useSetPaddedViewportCallback from './useSetPaddedViewportCallback'; import useTableListener from './useTableListener'; @@ -77,6 +78,8 @@ export default function useViewportData< [setViewport, table] ); + const dh = useApi(); + useTableListener( table, dh.Table.EVENT_UPDATED, diff --git a/packages/jsapi-utils/src/Formatter.test.ts b/packages/jsapi-utils/src/Formatter.test.ts index 8ea59a6275..9d6a5c50cc 100644 --- a/packages/jsapi-utils/src/Formatter.test.ts +++ b/packages/jsapi-utils/src/Formatter.test.ts @@ -1,3 +1,4 @@ +import dh from '@deephaven/jsapi-shim'; import Formatter from './Formatter'; import { BooleanColumnFormatter, @@ -82,7 +83,7 @@ describe('makeColumnFormatMap', () => { }); it('returns correct formatters for given column types', () => { - const formatter = makeFormatter(); + const formatter = makeFormatter(dh); expect(formatter.getColumnTypeFormatter(TYPE_DATETIME)).toBeInstanceOf( DateTimeColumnFormatter ); @@ -102,7 +103,7 @@ it('returns correct formatters for given column types', () => { it('uses default formatter for types that have no custom formatter', () => { expect( - makeFormatter().getColumnTypeFormatter('randomTypeWithNoCustomFormatter') + makeFormatter(dh).getColumnTypeFormatter('randomTypeWithNoCustomFormatter') ).toBeInstanceOf(DefaultColumnFormatter); }); @@ -121,7 +122,7 @@ describe('getColumnFormat', () => { ), ]; - const formatter = makeFormatter(columnFormats); + const formatter = makeFormatter(dh, columnFormats); it('returns null for DateTime column with no custom format', () => { const formatString = formatter.getColumnFormat( TYPE_DATETIME, @@ -143,7 +144,7 @@ describe('getColumnFormat', () => { describe('getFormattedString', () => { it('returns an empty string when value is null', () => { - const formatter = makeFormatter(); + const formatter = makeFormatter(dh); expect(formatter.getFormattedString(null, 'decimal')).toBe(''); }); @@ -151,7 +152,7 @@ describe('getFormattedString', () => { const value = 'randomValue'; const columnType = TYPE_DATETIME; const columnName = 'randomColumnName'; - const formatter = makeFormatter(); + const formatter = makeFormatter(dh); const columnTypeFormatter = formatter.getColumnTypeFormatter(columnType); const originalFormatFn = columnTypeFormatter.format; columnTypeFormatter.format = jest.fn(); @@ -176,7 +177,7 @@ describe('getFormattedString', () => { customFormat ), ]; - const formatter = makeFormatter(customColumnFormats); + const formatter = makeFormatter(dh, customColumnFormats); const columnTypeFormatter = formatter.getColumnTypeFormatter(columnType); const originalFormatFn = columnTypeFormatter.format; @@ -196,7 +197,7 @@ describe('getFormattedString', () => { describe('getColumnFormatMapForType', () => { it('should get columnFormatMap for a given column type and create new map entry', () => { - const formatter = makeFormatter(); + const formatter = makeFormatter(dh); const formatMap = formatter.getColumnFormatMapForType('decimal', true); if (formatMap) { expect(formatMap).not.toBeUndefined(); @@ -205,7 +206,7 @@ describe('getColumnFormatMapForType', () => { }); it('returns undefined if no formatmap exists and createIfNecessary is false', () => { - const formatter = new Formatter(); + const formatter = makeFormatter(dh); const formatMap = formatter.getColumnFormatMapForType('decimal'); expect(formatMap).toBeUndefined(); }); @@ -213,7 +214,7 @@ describe('getColumnFormatMapForType', () => { describe('timeZone', () => { it('should return the time zone name', () => { - const formatter = makeFormatter(); + const formatter = makeFormatter(dh); expect(formatter.timeZone).toBe('America/New_York'); }); }); diff --git a/packages/jsapi-utils/src/Formatter.ts b/packages/jsapi-utils/src/Formatter.ts index 4b4c21b59a..48ff2cb868 100644 --- a/packages/jsapi-utils/src/Formatter.ts +++ b/packages/jsapi-utils/src/Formatter.ts @@ -1,3 +1,4 @@ +import type { dh as DhType } from '@deephaven/jsapi-types'; import TableUtils, { DataType } from './TableUtils'; import { BooleanColumnFormatter, @@ -65,6 +66,7 @@ export class Formatter { } /** + * @param dh JSAPI instance * @param columnFormattingRules Optional array of column formatting rules * @param dateTimeOptions Optional object with DateTime configuration * @param decimalFormatOptions Optional object with Decimal configuration @@ -72,14 +74,15 @@ export class Formatter { * @param truncateNumbersWithPound Determine if numbers should be truncated w/ repeating # instead of ellipsis at the end */ constructor( + dh: DhType, columnFormattingRules: FormattingRule[] = [], - dateTimeOptions?: ConstructorParameters[0], + dateTimeOptions?: ConstructorParameters[1], decimalFormatOptions?: ConstructorParameters< typeof DecimalColumnFormatter - >[0], + >[1], integerFormatOptions?: ConstructorParameters< typeof IntegerColumnFormatter - >[0], + >[1], truncateNumbersWithPound = false ) { // Formatting order: @@ -95,15 +98,15 @@ export class Formatter { [TableUtils.dataType.CHAR, new CharColumnFormatter()], [ TableUtils.dataType.DATETIME, - new DateTimeColumnFormatter(dateTimeOptions), + new DateTimeColumnFormatter(dh, dateTimeOptions), ], [ TableUtils.dataType.DECIMAL, - new DecimalColumnFormatter(decimalFormatOptions), + new DecimalColumnFormatter(dh, decimalFormatOptions), ], [ TableUtils.dataType.INT, - new IntegerColumnFormatter(integerFormatOptions), + new IntegerColumnFormatter(dh, integerFormatOptions), ], [TableUtils.dataType.STRING, new StringColumnFormatter()], ]); diff --git a/packages/jsapi-utils/src/FormatterUtils.test.ts b/packages/jsapi-utils/src/FormatterUtils.test.ts index c8bf9d9555..abd01caab7 100644 --- a/packages/jsapi-utils/src/FormatterUtils.test.ts +++ b/packages/jsapi-utils/src/FormatterUtils.test.ts @@ -51,7 +51,7 @@ describe('isCustomColumnFormatDefined', () => { ]; it('is true for columns with custom or preset formats', () => { - const formatter = makeFormatter(columnFormats); + const formatter = makeFormatter(dh, columnFormats); expect( FormatterUtils.isCustomColumnFormatDefined( formatter, @@ -69,7 +69,7 @@ describe('isCustomColumnFormatDefined', () => { }); it('is false for columns with global or no format', () => { - const formatter = makeFormatter(columnFormats); + const formatter = makeFormatter(dh, columnFormats); expect( FormatterUtils.isCustomColumnFormatDefined( formatter, diff --git a/packages/jsapi-utils/src/FormatterUtils.ts b/packages/jsapi-utils/src/FormatterUtils.ts index 08c92ddcd1..55e837fb89 100644 --- a/packages/jsapi-utils/src/FormatterUtils.ts +++ b/packages/jsapi-utils/src/FormatterUtils.ts @@ -15,7 +15,7 @@ export function getColumnFormats( export function getDateTimeFormatterOptions( settings?: DateTimeFormatSettings | null -): ConstructorParameters[0] { +): ConstructorParameters[1] { return { timeZone: settings?.timeZone, defaultDateTimeFormatString: settings?.defaultDateTimeFormat, diff --git a/packages/jsapi-utils/src/SessionUtils.ts b/packages/jsapi-utils/src/SessionUtils.ts index 420f05e214..f77d807737 100644 --- a/packages/jsapi-utils/src/SessionUtils.ts +++ b/packages/jsapi-utils/src/SessionUtils.ts @@ -1,6 +1,7 @@ import type { ConnectOptions, CoreClient, + dh as DhType, IdeConnection, IdeSession, } from '@deephaven/jsapi-types'; @@ -34,7 +35,10 @@ export interface SessionWrapper { /** * @returns New connection to the server */ -export function createConnection(websocketUrl: string): IdeConnection { +export function createConnection( + dh: DhType, + websocketUrl: string +): IdeConnection { log.info(`Starting connection to '${websocketUrl}'...`); return new dh.IdeConnection(websocketUrl); @@ -77,6 +81,7 @@ export async function createSessionWrapper( } export function createCoreClient( + dh: DhType, websocketUrl: string, options?: ConnectOptions ): CoreClient { diff --git a/packages/jsapi-utils/src/TableUtils.test.ts b/packages/jsapi-utils/src/TableUtils.test.ts index 9f306f28f8..b801f780b3 100644 --- a/packages/jsapi-utils/src/TableUtils.test.ts +++ b/packages/jsapi-utils/src/TableUtils.test.ts @@ -77,7 +77,7 @@ describe('applyCustomColumns', () => { 'executeAndWaitForEvent' ); - TableUtils.applyCustomColumns(table, columns, timeout); + tableUtils.applyCustomColumns(table, columns, timeout); expect(TableUtils.executeAndWaitForEvent).toHaveBeenCalledWith( expect.any(Function), @@ -105,7 +105,7 @@ describe('applyFilter', () => { 'executeAndWaitForEvent' ); - TableUtils.applyFilter(table, filters, timeout); + tableUtils.applyFilter(table, filters, timeout); expect(TableUtils.executeAndWaitForEvent).toHaveBeenCalledWith( expect.any(Function), @@ -130,12 +130,16 @@ describe.each([undefined, 400])('applyNeverFilter - timeout: %s', timeout => { findColumn: jest.fn().mockReturnValue(column), }); - beforeEach(() => { - makeNeverFilter = jest.spyOn(TableUtils, 'makeNeverFilter'); + beforeAll(() => { + makeNeverFilter = jest.spyOn(TableUtils.prototype, 'makeNeverFilter'); makeNeverFilter.mockReturnValue(neverFilter); }); - afterEach(() => { + beforeEach(() => { + makeNeverFilter.mockClear(); + }); + + afterAll(() => { makeNeverFilter.mockRestore(); }); @@ -143,7 +147,7 @@ describe.each([undefined, 400])('applyNeverFilter - timeout: %s', timeout => { 'should resolve to null when not given a table: %s', async notTable => { const columnName = 'mock.column'; - const result = await TableUtils.applyNeverFilter( + const result = await tableUtils.applyNeverFilter( notTable, columnName, timeout @@ -154,12 +158,12 @@ describe.each([undefined, 400])('applyNeverFilter - timeout: %s', timeout => { it('should call TableUtils.applyFilter with a "never filter": %s', async () => { const applyFilter = jest - .spyOn(TableUtils, 'applyFilter') + .spyOn(TableUtils.prototype, 'applyFilter') .mockResolvedValue(table); const columnName = 'mock.column'; - const result = await TableUtils.applyNeverFilter( + const result = await tableUtils.applyNeverFilter( table, columnName, timeout @@ -187,7 +191,7 @@ describe('applySort', () => { 'executeAndWaitForEvent' ); - TableUtils.applySort(table, sorts, timeout); + tableUtils.applySort(table, sorts, timeout); expect(TableUtils.executeAndWaitForEvent).toHaveBeenCalledWith( expect.any(Function), diff --git a/packages/jsapi-utils/src/TableUtils.ts b/packages/jsapi-utils/src/TableUtils.ts index bedb44375d..497a3015c8 100644 --- a/packages/jsapi-utils/src/TableUtils.ts +++ b/packages/jsapi-utils/src/TableUtils.ts @@ -125,97 +125,6 @@ export class TableUtils { return table; }; - /** - * Apply custom columns to a given table. Return a Promise that resolves with - * the table once the dh.Table.EVENT_CUSTOMCOLUMNSCHANGED event has fired. - * @param table The table to apply custom columns to. - * @param columns The list of column expressions or definitions to apply. - * @returns A Promise that will be resolved with the given table after the - * columns are applied. - */ - static async applyCustomColumns( - table: Table | null | undefined, - columns: (string | CustomColumn)[], - timeout = TableUtils.APPLY_TABLE_CHANGE_TIMEOUT_MS - ): Promise
{ - return TableUtils.executeAndWaitForEvent( - t => t?.applyCustomColumns(columns), - table, - dh.Table.EVENT_CUSTOMCOLUMNSCHANGED, - timeout - ); - } - - /** - * Apply filters to a given table. - * @param table Table to apply filters to - * @param filters Filters to apply - * @param timeout Timeout before cancelling the promise that waits for the next - * dh.Table.EVENT_FILTERCHANGED event - * @returns a Promise to the Table that resolves after the next - * dh.Table.EVENT_FILTERCHANGED event - */ - static async applyFilter( - table: T | null | undefined, - filters: FilterCondition[], - timeout = TableUtils.APPLY_TABLE_CHANGE_TIMEOUT_MS - ): Promise { - return TableUtils.executeAndWaitForEvent( - t => t?.applyFilter(filters), - table, - dh.Table.EVENT_FILTERCHANGED, - timeout - ); - } - - /** - * Apply a filter to a table that won't match anything. - * @table The table to apply the filter to - * @columnName The name of the column to apploy the filter to - * @param timeout Timeout before cancelling the promise that waits for the next - * dh.Table.EVENT_FILTERCHANGED event - * @returns a Promise to the Table that resolves after the next - * dh.Table.EVENT_FILTERCHANGED event - */ - static async applyNeverFilter( - table: T | null | undefined, - columnName: string, - timeout = TableUtils.APPLY_TABLE_CHANGE_TIMEOUT_MS - ): Promise { - if (table == null) { - return null; - } - - const column = table.findColumn(columnName); - const filters = [TableUtils.makeNeverFilter(column)]; - - await TableUtils.applyFilter(table, filters, timeout); - - return table; - } - - /** - * Apply sorts to a given Table. - * @param table The table to apply sorts to - * @param sorts The sorts to apply - * @param timeout Timeout before cancelling the promise that waits for the next - * dh.Table.EVENT_SORTCHANGED event - * @returns a Promise to the Table that resolves after the next - * dh.Table.EVENT_SORTCHANGED event - */ - static async applySort( - table: T | null | undefined, - sorts: Sort[], - timeout = TableUtils.APPLY_TABLE_CHANGE_TIMEOUT_MS - ): Promise { - return TableUtils.executeAndWaitForEvent( - t => t?.applySort(sorts), - table, - dh.Table.EVENT_SORTCHANGED, - timeout - ); - } - static getSortIndex( sort: readonly Sort[], columnName: ColumnName @@ -1526,11 +1435,106 @@ export class TableUtils { } } + /** + * Apply a filter to a table that won't match anything. + * @table The table to apply the filter to + * @columnName The name of the column to apploy the filter to + * @param timeout Timeout before cancelling the promise that waits for the next + * dh.Table.EVENT_FILTERCHANGED event + * @returns a Promise to the Table that resolves after the next + * dh.Table.EVENT_FILTERCHANGED event + */ + async applyNeverFilter( + table: T | null | undefined, + columnName: string, + timeout = TableUtils.APPLY_TABLE_CHANGE_TIMEOUT_MS + ): Promise { + if (table == null) { + return null; + } + + const column = table.findColumn(columnName); + const filters = [this.makeNeverFilter(column)]; + + await this.applyFilter(table, filters, timeout); + + return table; + } + + /** + * Apply custom columns to a given table. Return a Promise that resolves with + * the table once the dh.Table.EVENT_CUSTOMCOLUMNSCHANGED event has fired. + * @param table The table to apply custom columns to. + * @param columns The list of column expressions or definitions to apply. + * @returns A Promise that will be resolved with the given table after the + * columns are applied. + */ + async applyCustomColumns( + table: Table | null | undefined, + columns: (string | CustomColumn)[], + timeout = TableUtils.APPLY_TABLE_CHANGE_TIMEOUT_MS + ): Promise
{ + const { dh } = this; + return TableUtils.executeAndWaitForEvent( + t => t?.applyCustomColumns(columns), + table, + dh.Table.EVENT_CUSTOMCOLUMNSCHANGED, + timeout + ); + } + + /** + * Apply filters to a given table. + * @param table Table to apply filters to + * @param filters Filters to apply + * @param timeout Timeout before cancelling the promise that waits for the next + * dh.Table.EVENT_FILTERCHANGED event + * @returns a Promise to the Table that resolves after the next + * dh.Table.EVENT_FILTERCHANGED event + */ + async applyFilter( + table: T | null | undefined, + filters: FilterCondition[], + timeout = TableUtils.APPLY_TABLE_CHANGE_TIMEOUT_MS + ): Promise { + const { dh } = this; + return TableUtils.executeAndWaitForEvent( + t => t?.applyFilter(filters), + table, + dh.Table.EVENT_FILTERCHANGED, + timeout + ); + } + + /** + * Apply sorts to a given Table. + * @param table The table to apply sorts to + * @param sorts The sorts to apply + * @param timeout Timeout before cancelling the promise that waits for the next + * dh.Table.EVENT_SORTCHANGED event + * @returns a Promise to the Table that resolves after the next + * dh.Table.EVENT_SORTCHANGED event + */ + async applySort( + table: T | null | undefined, + sorts: Sort[], + timeout = TableUtils.APPLY_TABLE_CHANGE_TIMEOUT_MS + ): Promise { + const { dh } = this; + return TableUtils.executeAndWaitForEvent( + t => t?.applySort(sorts), + table, + dh.Table.EVENT_SORTCHANGED, + timeout + ); + } + /** * Create a filter condition that results in zero results for a given column * @param column */ - static makeNeverFilter(column: Column): FilterCondition { + makeNeverFilter(column: Column): FilterCondition { + const { dh } = this; let value = null; if (TableUtils.isTextType(column.type)) { @@ -1653,7 +1657,7 @@ export class TableUtils { // KLUDGE: Return a conflicting filter to show no results. // Could recognize this situation at a higher or lower level and pause updates on the // table, but this situation should be rare and that wouldn't be much gains for some added complexity - return TableUtils.makeNeverFilter(column); + return this.makeNeverFilter(column); } const values = []; diff --git a/packages/jsapi-utils/src/formatters/DateTimeColumnFormatter.test.ts b/packages/jsapi-utils/src/formatters/DateTimeColumnFormatter.test.ts index 4cef77396d..4f699a2a64 100644 --- a/packages/jsapi-utils/src/formatters/DateTimeColumnFormatter.test.ts +++ b/packages/jsapi-utils/src/formatters/DateTimeColumnFormatter.test.ts @@ -13,7 +13,7 @@ function makeFormatter({ showTSeparator?: boolean; defaultDateTimeFormatString?: string; } = {}) { - return new DateTimeColumnFormatter({ + return new DateTimeColumnFormatter(dh, { timeZone, showTimeZone, showTSeparator, @@ -198,7 +198,7 @@ describe('isValid', () => { it('should return true if a format is valid', () => { for (let i = 0; i < VALID_FORMATS.length; i += 1) { expect( - DateTimeColumnFormatter.isValid({ + DateTimeColumnFormatter.isValid(dh, { formatString: VALID_FORMATS[i], }) ).toBe(true); diff --git a/packages/jsapi-utils/src/formatters/DateTimeColumnFormatter.ts b/packages/jsapi-utils/src/formatters/DateTimeColumnFormatter.ts index 9b21f430ba..3165eb0522 100644 --- a/packages/jsapi-utils/src/formatters/DateTimeColumnFormatter.ts +++ b/packages/jsapi-utils/src/formatters/DateTimeColumnFormatter.ts @@ -1,6 +1,9 @@ /* eslint class-methods-use-this: "off" */ -import dh from '@deephaven/jsapi-shim'; -import type { DateWrapper, TimeZone } from '@deephaven/jsapi-types'; +import type { + dh as DhType, + DateWrapper, + TimeZone, +} from '@deephaven/jsapi-types'; import Log from '@deephaven/log'; import TableColumnFormatter, { TableColumnFormat, @@ -27,10 +30,14 @@ export class DateTimeColumnFormatter extends TableColumnFormatter< > { /** * Validates format object + * @param dh JSAPI instance * @param format Format object * @returns true for valid object */ - static isValid(format: Pick): boolean { + static isValid( + dh: DhType, + format: Pick + ): boolean { try { dh.i18n.DateTimeFormat.format(format.formatString, new Date()); return true; @@ -134,6 +141,8 @@ export class DateTimeColumnFormatter extends TableColumnFormatter< return [...formatStringMap.keys()]; } + dh: DhType; + dhTimeZone: TimeZone; defaultDateTimeFormatString: string; @@ -144,12 +153,15 @@ export class DateTimeColumnFormatter extends TableColumnFormatter< formatStringMap: Map; - constructor({ - timeZone: timeZoneParam = '', - showTimeZone = true, - showTSeparator = false, - defaultDateTimeFormatString = DateTimeColumnFormatter.DEFAULT_DATETIME_FORMAT_STRING, - }: DateTimeColumnFormatterOptions = {}) { + constructor( + dh: DhType, + { + timeZone: timeZoneParam = '', + showTimeZone = true, + showTSeparator = false, + defaultDateTimeFormatString = DateTimeColumnFormatter.DEFAULT_DATETIME_FORMAT_STRING, + }: DateTimeColumnFormatterOptions = {} + ) { super(); const timeZone = @@ -163,7 +175,7 @@ export class DateTimeColumnFormatter extends TableColumnFormatter< DateTimeColumnFormatter.DEFAULT_TIME_ZONE_ID ); } - + this.dh = dh; this.defaultDateTimeFormatString = defaultDateTimeFormatString; this.showTimeZone = showTimeZone; this.showTSeparator = showTSeparator; @@ -187,7 +199,7 @@ export class DateTimeColumnFormatter extends TableColumnFormatter< : this.defaultDateTimeFormatString; const formatString = this.getEffectiveFormatString(baseFormatString); try { - return dh.i18n.DateTimeFormat.format( + return this.dh.i18n.DateTimeFormat.format( formatString, value, this.dhTimeZone diff --git a/packages/jsapi-utils/src/formatters/DecimalColumnFormatter.test.ts b/packages/jsapi-utils/src/formatters/DecimalColumnFormatter.test.ts index e256989436..30c3be312a 100644 --- a/packages/jsapi-utils/src/formatters/DecimalColumnFormatter.test.ts +++ b/packages/jsapi-utils/src/formatters/DecimalColumnFormatter.test.ts @@ -1,7 +1,8 @@ +import dh from '@deephaven/jsapi-shim'; import DecimalColumnFormatter from './DecimalColumnFormatter'; describe('multiplier tests', () => { - const formatter = new DecimalColumnFormatter(); + const formatter = new DecimalColumnFormatter(dh); const value = 10.4; it('handles null multiplier correctly', () => { expect( diff --git a/packages/jsapi-utils/src/formatters/DecimalColumnFormatter.ts b/packages/jsapi-utils/src/formatters/DecimalColumnFormatter.ts index 42136f53bc..6f5ce4bf3f 100644 --- a/packages/jsapi-utils/src/formatters/DecimalColumnFormatter.ts +++ b/packages/jsapi-utils/src/formatters/DecimalColumnFormatter.ts @@ -1,5 +1,5 @@ /* eslint class-methods-use-this: "off" */ -import dh from '@deephaven/jsapi-shim'; +import type { dh as DhType } from '@deephaven/jsapi-types'; import Log from '@deephaven/log'; import TableColumnFormatter, { TableColumnFormat, @@ -19,10 +19,14 @@ export type DecimalColumnFormatterOptions = { export class DecimalColumnFormatter extends TableColumnFormatter { /** * Validates format object + * @param dh JSAPI instance * @param format Format object * @returns true for valid object */ - static isValid(format: Pick): boolean { + static isValid( + dh: DhType, + format: Pick + ): boolean { try { dh.i18n.NumberFormat.format(format.formatString, 0); return true; @@ -152,11 +156,17 @@ export class DecimalColumnFormatter extends TableColumnFormatter { defaultFormatString: string; - constructor({ - defaultFormatString = DecimalColumnFormatter.DEFAULT_FORMAT_STRING, - }: DecimalColumnFormatterOptions = {}) { + dh: DhType; + + constructor( + dh: DhType, + { + defaultFormatString = DecimalColumnFormatter.DEFAULT_FORMAT_STRING, + }: DecimalColumnFormatterOptions = {} + ) { super(); + this.dh = dh; this.defaultFormatString = defaultFormatString; } @@ -179,7 +189,7 @@ export class DecimalColumnFormatter extends TableColumnFormatter { ? valueParam * format.multiplier : valueParam; try { - return dh.i18n.NumberFormat.format(formatString, value); + return this.dh.i18n.NumberFormat.format(formatString, value); } catch (e) { log.error('Invalid format arguments'); } diff --git a/packages/jsapi-utils/src/formatters/IntegerColumnFormatter.test.ts b/packages/jsapi-utils/src/formatters/IntegerColumnFormatter.test.ts index 717e236348..66fab93f79 100644 --- a/packages/jsapi-utils/src/formatters/IntegerColumnFormatter.test.ts +++ b/packages/jsapi-utils/src/formatters/IntegerColumnFormatter.test.ts @@ -1,7 +1,8 @@ +import dh from '@deephaven/jsapi-shim'; import IntegerColumnFormatter from './IntegerColumnFormatter'; describe('multiplier tests', () => { - const formatter = new IntegerColumnFormatter(); + const formatter = new IntegerColumnFormatter(dh); const value = 10; it('handles null multiplier correctly', () => { expect( diff --git a/packages/jsapi-utils/src/formatters/IntegerColumnFormatter.ts b/packages/jsapi-utils/src/formatters/IntegerColumnFormatter.ts index 58053bc197..e9d0fe4889 100644 --- a/packages/jsapi-utils/src/formatters/IntegerColumnFormatter.ts +++ b/packages/jsapi-utils/src/formatters/IntegerColumnFormatter.ts @@ -1,5 +1,5 @@ /* eslint class-methods-use-this: "off" */ -import dh from '@deephaven/jsapi-shim'; +import type { dh as DhType } from '@deephaven/jsapi-types'; import Log from '@deephaven/log'; import TableColumnFormatter, { TableColumnFormat, @@ -20,10 +20,14 @@ export type IntegerColumnFormatterOptions = { export class IntegerColumnFormatter extends TableColumnFormatter { /** * Validates format object + * @param dh JSAPI instance * @param format Format object * @returns true for valid object */ - static isValid(format: Pick): boolean { + static isValid( + dh: DhType, + format: Pick + ): boolean { try { dh.i18n.NumberFormat.format(format.formatString, 0); return true; @@ -125,13 +129,18 @@ export class IntegerColumnFormatter extends TableColumnFormatter { '0.0000E0' ); + dh: DhType; + defaultFormatString: string; - constructor({ - defaultFormatString = IntegerColumnFormatter.DEFAULT_FORMAT_STRING, - }: IntegerColumnFormatterOptions = {}) { + constructor( + dh: DhType, + { + defaultFormatString = IntegerColumnFormatter.DEFAULT_FORMAT_STRING, + }: IntegerColumnFormatterOptions = {} + ) { super(); - + this.dh = dh; this.defaultFormatString = defaultFormatString; } @@ -154,7 +163,7 @@ export class IntegerColumnFormatter extends TableColumnFormatter { ? valueParam * format.multiplier : valueParam; try { - return dh.i18n.NumberFormat.format(formatString, value); + return this.dh.i18n.NumberFormat.format(formatString, value); } catch (e) { log.error('Invalid format arguments'); } diff --git a/packages/jsapi-utils/src/formatters/NumberColumnFormatter.test.ts b/packages/jsapi-utils/src/formatters/NumberColumnFormatter.test.ts index 7378817bdd..44f2076aac 100644 --- a/packages/jsapi-utils/src/formatters/NumberColumnFormatter.test.ts +++ b/packages/jsapi-utils/src/formatters/NumberColumnFormatter.test.ts @@ -1,3 +1,4 @@ +import dh from '@deephaven/jsapi-shim'; import DecimalColumnFormatter from './DecimalColumnFormatter'; import IntegerColumnFormatter from './IntegerColumnFormatter'; import { TableColumnFormat } from './TableColumnFormatter'; @@ -24,25 +25,27 @@ numberColumnFormatters.forEach(({ name, formatter: NumberColumnFormatter }) => { 'Valid Format', '0.0' ); - expect(NumberColumnFormatter.isValid(validFormat)).toBe(true); + expect(NumberColumnFormatter.isValid(dh, validFormat)).toBe(true); }); it('returns true for custom format with valid formatString', () => { const validFormat = NumberColumnFormatter.makeCustomFormat('0.0'); - expect(NumberColumnFormatter.isValid(validFormat)).toBe(true); + expect(NumberColumnFormatter.isValid(dh, validFormat)).toBe(true); }); it('does not throw exceptions for invalid format', () => { - expect(() => NumberColumnFormatter.isValid(INVALID_FORMAT)).not.toThrow(); + expect(() => + NumberColumnFormatter.isValid(dh, INVALID_FORMAT) + ).not.toThrow(); }); it('returns false for invalid format', () => { - expect(NumberColumnFormatter.isValid(INVALID_FORMAT)).toBe(false); + expect(NumberColumnFormatter.isValid(dh, INVALID_FORMAT)).toBe(false); }); }); describe(`${name} instance format method`, () => { - const formatter = new NumberColumnFormatter(); + const formatter = new NumberColumnFormatter(dh); it('returns empty string for invalid formatString in format argument', () => { expect(formatter.format(0, INVALID_FORMAT)).toBe(''); }); diff --git a/packages/jsapi-utils/src/formatters/TableColumnFormatter.ts b/packages/jsapi-utils/src/formatters/TableColumnFormatter.ts index e8b5547ade..58b3e16135 100644 --- a/packages/jsapi-utils/src/formatters/TableColumnFormatter.ts +++ b/packages/jsapi-utils/src/formatters/TableColumnFormatter.ts @@ -4,6 +4,8 @@ * Extend this class and register with TableUtils to make use of it. */ +import type { dh as DhType } from '@deephaven/jsapi-types'; + export type TableColumnFormatType = | 'type-global' | 'type-context-preset' @@ -24,10 +26,11 @@ export class TableColumnFormatter { /** * Validates format object + * @param dh JSAPI instance * @param format Format object * @returns true for valid object */ - static isValid(format: TableColumnFormat): boolean { + static isValid(dh: DhType, format: TableColumnFormat): boolean { return true; } diff --git a/packages/redux/package.json b/packages/redux/package.json index 187e09af1f..8b1e4a12e3 100644 --- a/packages/redux/package.json +++ b/packages/redux/package.json @@ -22,6 +22,7 @@ "build:babel": "babel ./src --out-dir ./dist --extensions \".ts,.tsx,.js,.jsx\" --source-maps --root-mode upward" }, "dependencies": { + "@deephaven/jsapi-types": "file:../jsapi-types", "@deephaven/jsapi-utils": "file:../jsapi-utils", "@deephaven/log": "file:../log", "deep-equal": "^2.0.5", diff --git a/packages/redux/src/actionTypes.ts b/packages/redux/src/actionTypes.ts index e4251a98c7..145c85b8f4 100644 --- a/packages/redux/src/actionTypes.ts +++ b/packages/redux/src/actionTypes.ts @@ -2,6 +2,8 @@ export const SAVE_WORKSPACE = 'SAVE_WORKSPACE'; export const SET_ACTIVE_TOOL = 'SET_ACTIVE_TOOL'; +export const SET_API = 'SET_API'; + export const SET_COMMAND_HISTORY_STORAGE = 'SET_COMMAND_HISTORY_STORAGE'; export const SET_DASHBOARD_STORAGE = 'SET_DASHBOARD_STORAGE'; diff --git a/packages/redux/src/actions.ts b/packages/redux/src/actions.ts index b6bd98802e..0985e47bef 100644 --- a/packages/redux/src/actions.ts +++ b/packages/redux/src/actions.ts @@ -2,6 +2,7 @@ import type { Action } from 'redux'; import type { ThunkAction } from 'redux-thunk'; import type { CommandHistoryStorage } from '@deephaven/console'; import type { FileStorage } from '@deephaven/file-explorer'; +import type { dh as DhType } from '@deephaven/jsapi-types'; import { SET_PLUGINS, SET_USER, @@ -11,6 +12,7 @@ import { SET_ACTIVE_TOOL, SET_FILE_STORAGE, SET_SERVER_CONFIG_VALUES, + SET_API, } from './actionTypes'; import type { DeephavenPluginModuleMap, @@ -34,6 +36,11 @@ export const setUser: PayloadActionCreator = user => ({ payload: user, }); +export const setApi: PayloadActionCreator = api => ({ + type: SET_API, + payload: api, +}); + export const setWorkspace: PayloadActionCreator = workspace => ({ type: SET_WORKSPACE, payload: workspace, diff --git a/packages/redux/src/selectors.ts b/packages/redux/src/selectors.ts index ea6ac17ada..8a1cda733c 100644 --- a/packages/redux/src/selectors.ts +++ b/packages/redux/src/selectors.ts @@ -8,6 +8,9 @@ export type Selector = (store: State) => R; type Settings = State['workspace']['data']['settings']; +export const getApi = (store: State): State['api'] => + store.api; + // User export const getUser = (store: State): State['user'] => store.user; diff --git a/packages/redux/src/store.ts b/packages/redux/src/store.ts index b2ea0e99ef..4afdc9f307 100644 --- a/packages/redux/src/store.ts +++ b/packages/redux/src/store.ts @@ -1,6 +1,7 @@ import { applyMiddleware, createStore, compose, combineReducers } from 'redux'; import type { FileStorage } from '@deephaven/file-explorer'; import type { ValidKeyState } from '@deephaven/components'; +import type { dh as DhType } from '@deephaven/jsapi-types'; import type { FormattingRule } from '@deephaven/jsapi-utils'; import type { PayloadAction } from './actions'; import rootMiddleware from './middleware'; @@ -91,6 +92,7 @@ export interface WorkspaceStorage { } export type RootState = { + api: DhType; activeTool: string; plugins: DeephavenPluginModuleMap; storage: Storage; diff --git a/packages/redux/tsconfig.json b/packages/redux/tsconfig.json index f1f3671a39..c02df6c89e 100644 --- a/packages/redux/tsconfig.json +++ b/packages/redux/tsconfig.json @@ -10,6 +10,7 @@ { "path": "../components" }, { "path": "../console" }, { "path": "../file-explorer" }, + { "path": "../jsapi-types" }, { "path": "../jsapi-utils" }, { "path": "../log" } ]