Skip to content

Commit

Permalink
fix: Panels not reinitializing if makeModel changes (#1633)
Browse files Browse the repository at this point in the history
Mostly needed for Deephaven UI w/ widget plugin loading which will
recreate the `makeModel` method if a new widget is created
  • Loading branch information
mattrunyon authored Nov 10, 2023
1 parent 9a960b3 commit 5ee98cd
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 110 deletions.
3 changes: 2 additions & 1 deletion packages/app-utils/src/plugins/remote-component.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable global-require */
/**
* remote-component.config.js
*
Expand All @@ -20,6 +19,7 @@ import * as DeephavenJsapiComponents from '@deephaven/jsapi-components';
import * as DeephavenJsapiUtils from '@deephaven/jsapi-utils';
import DeephavenLog from '@deephaven/log';
import * as DeephavenReactHooks from '@deephaven/react-hooks';
import * as DeephavenPlugin from '@deephaven/plugin';

// eslint-disable-next-line import/prefer-default-export
export const resolve = {
Expand All @@ -38,5 +38,6 @@ export const resolve = {
'@deephaven/jsapi-components': DeephavenJsapiComponents,
'@deephaven/jsapi-utils': DeephavenJsapiUtils,
'@deephaven/log': DeephavenLog,
'@deephaven/plugin': DeephavenPlugin,
'@deephaven/react-hooks': DeephavenReactHooks,
};
75 changes: 51 additions & 24 deletions packages/dashboard-core-plugins/src/ChartPlugin.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import { forwardRef, useMemo } from 'react';
import { useApi } from '@deephaven/jsapi-bootstrap';
import { useConnection } from '@deephaven/jsapi-components';
import { assertNotNull } from '@deephaven/utils';
import {
ChartModel,
ChartModelFactory,
ChartTheme,
useChartTheme,
} from '@deephaven/chart';
import type { dh as DhType, IdeConnection } from '@deephaven/jsapi-types';
import type {
dh as DhType,
Figure,
IdeConnection,
} from '@deephaven/jsapi-types';
import { IrisGridUtils } from '@deephaven/iris-grid';
import { getTimeZone, store } from '@deephaven/redux';
import { type WidgetComponentProps } from '@deephaven/plugin';
import {
ChartPanelMetadata,
GLChartPanelState,
isChartPanelDehydratedProps,
isChartPanelFigureMetadata,
isChartPanelTableMetadata,
} from './panels';
import ConnectedChartPanel, {
Expand All @@ -28,6 +32,7 @@ async function createChartModel(
chartTheme: ChartTheme,
connection: IdeConnection,
metadata: ChartPanelMetadata,
fetch: () => Promise<Figure>,
panelState?: GLChartPanelState
): Promise<ChartModel> {
let settings;
Expand All @@ -43,7 +48,9 @@ async function createChartModel(
} else {
settings = {};
tableName = '';
figureName = metadata.name ?? metadata.figure;
figureName = isChartPanelFigureMetadata(metadata)
? metadata.figure
: metadata.name;
tableSettings = {};
}
if (panelState != null) {
Expand All @@ -64,26 +71,38 @@ async function createChartModel(
}
}

if (figureName == null && tableName == null) {
const figure = await fetch();

return ChartModelFactory.makeModel(dh, settings, figure, chartTheme);
}

if (figureName != null) {
const definition = {
title: figureName,
name: figureName,
type: dh.VariableType.FIGURE,
};
const figure = await connection.getObject(definition);
let figure: Figure;

if (metadata.type === dh.VariableType.FIGURE) {
const definition = {
name: figureName,
type: dh.VariableType.FIGURE,
};
figure = await connection.getObject(definition);
} else {
figure = await fetch();
}

return ChartModelFactory.makeModel(dh, settings, figure, chartTheme);
}

const definition = {
title: figureName,
name: tableName,
type: dh.VariableType.TABLE,
};
const table = await connection.getObject(definition);
const timeZone = getTimeZone(store.getState());
assertNotNull(timeZone);
new IrisGridUtils(dh).applyTableSettings(table, tableSettings, timeZone);
new IrisGridUtils(dh).applyTableSettings(
table,
tableSettings,
getTimeZone(store.getState())
);

return ChartModelFactory.makeModelFromSettings(
dh,
Expand All @@ -100,18 +119,17 @@ export const ChartPlugin = forwardRef(
const chartTheme = useChartTheme();
const connection = useConnection();

const panelState = isChartPanelDehydratedProps(props)
? (props as unknown as ChartPanelProps).panelState
: undefined;

const { fetch, metadata, localDashboardId } = props;

const hydratedProps = useMemo(
() => ({
...(props as unknown as ChartPanelProps),
metadata: props.metadata as ChartPanelMetadata,
localDashboardId: props.localDashboardId,
metadata: metadata as ChartPanelMetadata,
localDashboardId,
makeModel: () => {
const { metadata } = props;

const panelState = isChartPanelDehydratedProps(props)
? (props as unknown as ChartPanelProps).panelState
: undefined;

if (metadata == null) {
throw new Error('Metadata is required for chart panel');
}
Expand All @@ -121,15 +139,24 @@ export const ChartPlugin = forwardRef(
chartTheme,
connection,
metadata as ChartPanelMetadata,
fetch as unknown as () => Promise<Figure>,
panelState
);
},
}),
[props, dh, chartTheme, connection]
[
dh,
connection,
fetch,
panelState,
metadata,
localDashboardId,
chartTheme,
]
);

// eslint-disable-next-line react/jsx-props-no-spreading
return <ConnectedChartPanel ref={ref} {...hydratedProps} />;
return <ConnectedChartPanel ref={ref} {...props} {...hydratedProps} />;
}
);

Expand Down
23 changes: 7 additions & 16 deletions packages/dashboard-core-plugins/src/GridPlugin.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,21 @@
import { forwardRef, useMemo } from 'react';
import { forwardRef } from 'react';
import { type WidgetComponentProps } from '@deephaven/plugin';
import { type DashboardPanelProps } from '@deephaven/dashboard';
import { type Table } from '@deephaven/jsapi-types';
import useHydrateGrid from './useHydrateGrid';
import ConnectedIrisGridPanel, {
IrisGridPanelProps,
type IrisGridPanel,
} from './panels/IrisGridPanel';

export const GridPlugin = forwardRef(
(props: WidgetComponentProps, ref: React.Ref<IrisGridPanel>) => {
const hydrate = useHydrateGrid<
DashboardPanelProps & Pick<IrisGridPanelProps, 'panelState'>
>();
const { localDashboardId } = props;
const hydratedProps = useMemo(
() =>
hydrate(
props as WidgetComponentProps &
Pick<IrisGridPanelProps, 'panelState'>,
localDashboardId
),
[hydrate, props, localDashboardId]
const { localDashboardId, fetch } = props;
const hydratedProps = useHydrateGrid(
fetch as unknown as () => Promise<Table>,
localDashboardId
);

// eslint-disable-next-line react/jsx-props-no-spreading
return <ConnectedIrisGridPanel ref={ref} {...hydratedProps} />;
return <ConnectedIrisGridPanel ref={ref} {...props} {...hydratedProps} />;
}
);

Expand Down
15 changes: 7 additions & 8 deletions packages/dashboard-core-plugins/src/PandasPlugin.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import { DashboardPanelProps } from '@deephaven/dashboard';
import { forwardRef } from 'react';
import { WidgetComponentProps } from '@deephaven/plugin';
import { forwardRef, useMemo } from 'react';
import { type Table } from '@deephaven/jsapi-types';
import { PandasPanel } from './panels';
import useHydrateGrid from './useHydrateGrid';

export const PandasPlugin = forwardRef(
(props: WidgetComponentProps, ref: React.Ref<PandasPanel>) => {
const hydrate = useHydrateGrid<DashboardPanelProps>();
const { localDashboardId } = props;
const hydratedProps = useMemo(
() => hydrate(props, localDashboardId),
[hydrate, props, localDashboardId]
const { localDashboardId, fetch } = props;
const hydratedProps = useHydrateGrid(
fetch as unknown as () => Promise<Table>,
localDashboardId
);

// eslint-disable-next-line react/jsx-props-no-spreading
return <PandasPanel ref={ref} {...hydratedProps} />;
return <PandasPanel ref={ref} {...props} {...hydratedProps} />;
}
);

Expand Down
25 changes: 17 additions & 8 deletions packages/dashboard-core-plugins/src/panels/ChartPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ import ChartColumnSelectorOverlay, {
import './ChartPanel.scss';
import { Link, LinkFilterMap } from '../linker/LinkerUtils';
import { PanelState as IrisGridPanelState } from './IrisGridPanel';
import { isChartPanelTableMetadata } from './ChartPanelUtils';
import {
isChartPanelFigureMetadata,
isChartPanelTableMetadata,
} from './ChartPanelUtils';
import { ColumnSelectionValidator } from '../linker/ColumnSelectionValidator';

const log = Log.module('ChartPanel');
Expand All @@ -81,7 +84,6 @@ export interface ChartPanelFigureMetadata extends PanelMetadata {
* @deprecated use `name` instead
*/
figure?: string;
sourcePanelId: never;
}

export interface ChartPanelTableMetadata extends PanelMetadata {
Expand All @@ -98,6 +100,7 @@ export interface ChartPanelTableMetadata extends PanelMetadata {
}

export type ChartPanelMetadata =
| PanelMetadata
| ChartPanelFigureMetadata
| ChartPanelTableMetadata;

Expand All @@ -106,7 +109,7 @@ type Settings = Record<string, unknown>;
export interface GLChartPanelState {
filterValueMap?: [string, unknown][];
settings: Partial<ChartModelSettings>;
tableSettings: TableSettings;
tableSettings?: TableSettings;
irisGridState?: {
advancedFilters: unknown;
quickFilters: unknown;
Expand All @@ -128,7 +131,7 @@ interface OwnProps extends DashboardPanelProps {
/** The panel container div */
containerRef?: RefObject<HTMLDivElement>;

panelState: GLChartPanelState;
panelState?: GLChartPanelState;
}

interface StateProps {
Expand Down Expand Up @@ -168,7 +171,7 @@ interface ChartPanelState {
columnMap: ColumnMap;

// eslint-disable-next-line react/no-unused-state
panelState: GLChartPanelState;
panelState?: GLChartPanelState;
}

function hasInputFilter(
Expand Down Expand Up @@ -276,14 +279,18 @@ export class ChartPanel extends Component<ChartPanelProps, ChartPanelState> {
prevProps: ChartPanelProps,
prevState: ChartPanelState
): void {
const { inputFilters, source } = this.props;
const { inputFilters, source, makeModel } = this.props;
const { columnMap, model, filterMap, filterValueMap, isLinked, settings } =
this.state;

if (!model) {
return;
}

if (makeModel !== prevProps.makeModel) {
this.initModel();
}

if (columnMap !== prevState.columnMap) {
this.pruneFilterMaps();
}
Expand Down Expand Up @@ -1042,8 +1049,10 @@ export class ChartPanel extends Component<ChartPanelProps, ChartPanelState> {
let name;
if (isChartPanelTableMetadata(metadata)) {
name = metadata.table;
} else if (isChartPanelFigureMetadata(metadata)) {
name = metadata.figure;
} else {
name = metadata.name ?? metadata.figure;
name = metadata.name;
}
const inputFilterMap = this.getInputFilterColumnMap(
columnMap,
Expand Down Expand Up @@ -1156,7 +1165,7 @@ const mapStateToProps = (state: RootState, ownProps: OwnProps): StateProps => {
const { localDashboardId, metadata } = ownProps;

let sourcePanelId: string | undefined;
if (metadata != null) {
if (metadata != null && isChartPanelTableMetadata(metadata)) {
sourcePanelId = metadata.sourcePanelId;
}
const panelTableMap = getTableMapForDashboard(state, localDashboardId);
Expand Down
7 changes: 7 additions & 0 deletions packages/dashboard-core-plugins/src/panels/ChartPanelUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { DehydratedDashboardPanelProps } from '@deephaven/dashboard';
import type {
ChartPanelMetadata,
ChartPanelTableMetadata,
ChartPanelFigureMetadata,
GLChartPanelState,
} from './ChartPanel';

Expand All @@ -11,6 +12,12 @@ export function isChartPanelTableMetadata(
return (metadata as ChartPanelTableMetadata).settings !== undefined;
}

export function isChartPanelFigureMetadata(
metadata: ChartPanelMetadata
): metadata is ChartPanelFigureMetadata {
return (metadata as ChartPanelFigureMetadata).figure !== undefined;
}

export type DehydratedChartPanelProps = DehydratedDashboardPanelProps & {
panelState?: GLChartPanelState;
};
Expand Down
14 changes: 11 additions & 3 deletions packages/dashboard-core-plugins/src/panels/IrisGridPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ type LoadedPanelState = PanelState & {

export interface OwnProps extends DashboardPanelProps {
children?: ReactNode;
panelState: LoadedPanelState | null;
panelState?: LoadedPanelState | null;
makeModel: () => IrisGridModel | Promise<IrisGridModel>;

onStateChange?: (irisGridState: IrisGridState, gridState: GridState) => void;
Expand Down Expand Up @@ -205,7 +205,7 @@ interface IrisGridPanelState {
columnHeaderGroups?: readonly ColumnHeaderGroup[];

// eslint-disable-next-line react/no-unused-state
panelState: PanelState | null; // Dehydrated panel state that can load this panel
panelState?: PanelState | null; // Dehydrated panel state that can load this panel
irisGridStateOverrides: Partial<DehydratedIrisGridState>;
gridStateOverrides: Partial<GridState>;
}
Expand Down Expand Up @@ -324,8 +324,12 @@ export class IrisGridPanel extends PureComponent<
this.initModel();
}

componentDidUpdate(_: never, prevState: IrisGridPanelState): void {
componentDidUpdate(
prevProps: IrisGridPanelProps,
prevState: IrisGridPanelState
): void {
const { model } = this.state;
const { makeModel } = this.props;
if (model !== prevState.model) {
if (prevState.model != null) {
this.stopModelListening(prevState.model);
Expand All @@ -335,6 +339,10 @@ export class IrisGridPanel extends PureComponent<
this.startModelListening(model);
}
}

if (makeModel !== prevProps.makeModel) {
this.initModel();
}
}

componentWillUnmount(): void {
Expand Down
Loading

0 comments on commit 5ee98cd

Please sign in to comment.