Skip to content

Commit 5ee98cd

Browse files
authored
fix: Panels not reinitializing if makeModel changes (#1633)
Mostly needed for Deephaven UI w/ widget plugin loading which will recreate the `makeModel` method if a new widget is created
1 parent 9a960b3 commit 5ee98cd

10 files changed

Lines changed: 125 additions & 110 deletions

File tree

packages/app-utils/src/plugins/remote-component.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/* eslint-disable global-require */
21
/**
32
* remote-component.config.js
43
*
@@ -20,6 +19,7 @@ import * as DeephavenJsapiComponents from '@deephaven/jsapi-components';
2019
import * as DeephavenJsapiUtils from '@deephaven/jsapi-utils';
2120
import DeephavenLog from '@deephaven/log';
2221
import * as DeephavenReactHooks from '@deephaven/react-hooks';
22+
import * as DeephavenPlugin from '@deephaven/plugin';
2323

2424
// eslint-disable-next-line import/prefer-default-export
2525
export const resolve = {
@@ -38,5 +38,6 @@ export const resolve = {
3838
'@deephaven/jsapi-components': DeephavenJsapiComponents,
3939
'@deephaven/jsapi-utils': DeephavenJsapiUtils,
4040
'@deephaven/log': DeephavenLog,
41+
'@deephaven/plugin': DeephavenPlugin,
4142
'@deephaven/react-hooks': DeephavenReactHooks,
4243
};

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

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
11
import { forwardRef, useMemo } from 'react';
22
import { useApi } from '@deephaven/jsapi-bootstrap';
33
import { useConnection } from '@deephaven/jsapi-components';
4-
import { assertNotNull } from '@deephaven/utils';
54
import {
65
ChartModel,
76
ChartModelFactory,
87
ChartTheme,
98
useChartTheme,
109
} from '@deephaven/chart';
11-
import type { dh as DhType, IdeConnection } from '@deephaven/jsapi-types';
10+
import type {
11+
dh as DhType,
12+
Figure,
13+
IdeConnection,
14+
} from '@deephaven/jsapi-types';
1215
import { IrisGridUtils } from '@deephaven/iris-grid';
1316
import { getTimeZone, store } from '@deephaven/redux';
1417
import { type WidgetComponentProps } from '@deephaven/plugin';
1518
import {
1619
ChartPanelMetadata,
1720
GLChartPanelState,
1821
isChartPanelDehydratedProps,
22+
isChartPanelFigureMetadata,
1923
isChartPanelTableMetadata,
2024
} from './panels';
2125
import ConnectedChartPanel, {
@@ -28,6 +32,7 @@ async function createChartModel(
2832
chartTheme: ChartTheme,
2933
connection: IdeConnection,
3034
metadata: ChartPanelMetadata,
35+
fetch: () => Promise<Figure>,
3136
panelState?: GLChartPanelState
3237
): Promise<ChartModel> {
3338
let settings;
@@ -43,7 +48,9 @@ async function createChartModel(
4348
} else {
4449
settings = {};
4550
tableName = '';
46-
figureName = metadata.name ?? metadata.figure;
51+
figureName = isChartPanelFigureMetadata(metadata)
52+
? metadata.figure
53+
: metadata.name;
4754
tableSettings = {};
4855
}
4956
if (panelState != null) {
@@ -64,26 +71,38 @@ async function createChartModel(
6471
}
6572
}
6673

74+
if (figureName == null && tableName == null) {
75+
const figure = await fetch();
76+
77+
return ChartModelFactory.makeModel(dh, settings, figure, chartTheme);
78+
}
79+
6780
if (figureName != null) {
68-
const definition = {
69-
title: figureName,
70-
name: figureName,
71-
type: dh.VariableType.FIGURE,
72-
};
73-
const figure = await connection.getObject(definition);
81+
let figure: Figure;
82+
83+
if (metadata.type === dh.VariableType.FIGURE) {
84+
const definition = {
85+
name: figureName,
86+
type: dh.VariableType.FIGURE,
87+
};
88+
figure = await connection.getObject(definition);
89+
} else {
90+
figure = await fetch();
91+
}
7492

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

7896
const definition = {
79-
title: figureName,
8097
name: tableName,
8198
type: dh.VariableType.TABLE,
8299
};
83100
const table = await connection.getObject(definition);
84-
const timeZone = getTimeZone(store.getState());
85-
assertNotNull(timeZone);
86-
new IrisGridUtils(dh).applyTableSettings(table, tableSettings, timeZone);
101+
new IrisGridUtils(dh).applyTableSettings(
102+
table,
103+
tableSettings,
104+
getTimeZone(store.getState())
105+
);
87106

88107
return ChartModelFactory.makeModelFromSettings(
89108
dh,
@@ -100,18 +119,17 @@ export const ChartPlugin = forwardRef(
100119
const chartTheme = useChartTheme();
101120
const connection = useConnection();
102121

122+
const panelState = isChartPanelDehydratedProps(props)
123+
? (props as unknown as ChartPanelProps).panelState
124+
: undefined;
125+
126+
const { fetch, metadata, localDashboardId } = props;
127+
103128
const hydratedProps = useMemo(
104129
() => ({
105-
...(props as unknown as ChartPanelProps),
106-
metadata: props.metadata as ChartPanelMetadata,
107-
localDashboardId: props.localDashboardId,
130+
metadata: metadata as ChartPanelMetadata,
131+
localDashboardId,
108132
makeModel: () => {
109-
const { metadata } = props;
110-
111-
const panelState = isChartPanelDehydratedProps(props)
112-
? (props as unknown as ChartPanelProps).panelState
113-
: undefined;
114-
115133
if (metadata == null) {
116134
throw new Error('Metadata is required for chart panel');
117135
}
@@ -121,15 +139,24 @@ export const ChartPlugin = forwardRef(
121139
chartTheme,
122140
connection,
123141
metadata as ChartPanelMetadata,
142+
fetch as unknown as () => Promise<Figure>,
124143
panelState
125144
);
126145
},
127146
}),
128-
[props, dh, chartTheme, connection]
147+
[
148+
dh,
149+
connection,
150+
fetch,
151+
panelState,
152+
metadata,
153+
localDashboardId,
154+
chartTheme,
155+
]
129156
);
130157

131158
// eslint-disable-next-line react/jsx-props-no-spreading
132-
return <ConnectedChartPanel ref={ref} {...hydratedProps} />;
159+
return <ConnectedChartPanel ref={ref} {...props} {...hydratedProps} />;
133160
}
134161
);
135162

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

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,21 @@
1-
import { forwardRef, useMemo } from 'react';
1+
import { forwardRef } from 'react';
22
import { type WidgetComponentProps } from '@deephaven/plugin';
3-
import { type DashboardPanelProps } from '@deephaven/dashboard';
3+
import { type Table } from '@deephaven/jsapi-types';
44
import useHydrateGrid from './useHydrateGrid';
55
import ConnectedIrisGridPanel, {
6-
IrisGridPanelProps,
76
type IrisGridPanel,
87
} from './panels/IrisGridPanel';
98

109
export const GridPlugin = forwardRef(
1110
(props: WidgetComponentProps, ref: React.Ref<IrisGridPanel>) => {
12-
const hydrate = useHydrateGrid<
13-
DashboardPanelProps & Pick<IrisGridPanelProps, 'panelState'>
14-
>();
15-
const { localDashboardId } = props;
16-
const hydratedProps = useMemo(
17-
() =>
18-
hydrate(
19-
props as WidgetComponentProps &
20-
Pick<IrisGridPanelProps, 'panelState'>,
21-
localDashboardId
22-
),
23-
[hydrate, props, localDashboardId]
11+
const { localDashboardId, fetch } = props;
12+
const hydratedProps = useHydrateGrid(
13+
fetch as unknown as () => Promise<Table>,
14+
localDashboardId
2415
);
2516

2617
// eslint-disable-next-line react/jsx-props-no-spreading
27-
return <ConnectedIrisGridPanel ref={ref} {...hydratedProps} />;
18+
return <ConnectedIrisGridPanel ref={ref} {...props} {...hydratedProps} />;
2819
}
2920
);
3021

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

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
import { DashboardPanelProps } from '@deephaven/dashboard';
1+
import { forwardRef } from 'react';
22
import { WidgetComponentProps } from '@deephaven/plugin';
3-
import { forwardRef, useMemo } from 'react';
3+
import { type Table } from '@deephaven/jsapi-types';
44
import { PandasPanel } from './panels';
55
import useHydrateGrid from './useHydrateGrid';
66

77
export const PandasPlugin = forwardRef(
88
(props: WidgetComponentProps, ref: React.Ref<PandasPanel>) => {
9-
const hydrate = useHydrateGrid<DashboardPanelProps>();
10-
const { localDashboardId } = props;
11-
const hydratedProps = useMemo(
12-
() => hydrate(props, localDashboardId),
13-
[hydrate, props, localDashboardId]
9+
const { localDashboardId, fetch } = props;
10+
const hydratedProps = useHydrateGrid(
11+
fetch as unknown as () => Promise<Table>,
12+
localDashboardId
1413
);
1514

1615
// eslint-disable-next-line react/jsx-props-no-spreading
17-
return <PandasPanel ref={ref} {...hydratedProps} />;
16+
return <PandasPanel ref={ref} {...props} {...hydratedProps} />;
1817
}
1918
);
2019

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

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,10 @@ import ChartColumnSelectorOverlay, {
6565
import './ChartPanel.scss';
6666
import { Link, LinkFilterMap } from '../linker/LinkerUtils';
6767
import { PanelState as IrisGridPanelState } from './IrisGridPanel';
68-
import { isChartPanelTableMetadata } from './ChartPanelUtils';
68+
import {
69+
isChartPanelFigureMetadata,
70+
isChartPanelTableMetadata,
71+
} from './ChartPanelUtils';
6972
import { ColumnSelectionValidator } from '../linker/ColumnSelectionValidator';
7073

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

8789
export interface ChartPanelTableMetadata extends PanelMetadata {
@@ -98,6 +100,7 @@ export interface ChartPanelTableMetadata extends PanelMetadata {
98100
}
99101

100102
export type ChartPanelMetadata =
103+
| PanelMetadata
101104
| ChartPanelFigureMetadata
102105
| ChartPanelTableMetadata;
103106

@@ -106,7 +109,7 @@ type Settings = Record<string, unknown>;
106109
export interface GLChartPanelState {
107110
filterValueMap?: [string, unknown][];
108111
settings: Partial<ChartModelSettings>;
109-
tableSettings: TableSettings;
112+
tableSettings?: TableSettings;
110113
irisGridState?: {
111114
advancedFilters: unknown;
112115
quickFilters: unknown;
@@ -128,7 +131,7 @@ interface OwnProps extends DashboardPanelProps {
128131
/** The panel container div */
129132
containerRef?: RefObject<HTMLDivElement>;
130133

131-
panelState: GLChartPanelState;
134+
panelState?: GLChartPanelState;
132135
}
133136

134137
interface StateProps {
@@ -168,7 +171,7 @@ interface ChartPanelState {
168171
columnMap: ColumnMap;
169172

170173
// eslint-disable-next-line react/no-unused-state
171-
panelState: GLChartPanelState;
174+
panelState?: GLChartPanelState;
172175
}
173176

174177
function hasInputFilter(
@@ -276,14 +279,18 @@ export class ChartPanel extends Component<ChartPanelProps, ChartPanelState> {
276279
prevProps: ChartPanelProps,
277280
prevState: ChartPanelState
278281
): void {
279-
const { inputFilters, source } = this.props;
282+
const { inputFilters, source, makeModel } = this.props;
280283
const { columnMap, model, filterMap, filterValueMap, isLinked, settings } =
281284
this.state;
282285

283286
if (!model) {
284287
return;
285288
}
286289

290+
if (makeModel !== prevProps.makeModel) {
291+
this.initModel();
292+
}
293+
287294
if (columnMap !== prevState.columnMap) {
288295
this.pruneFilterMaps();
289296
}
@@ -1042,8 +1049,10 @@ export class ChartPanel extends Component<ChartPanelProps, ChartPanelState> {
10421049
let name;
10431050
if (isChartPanelTableMetadata(metadata)) {
10441051
name = metadata.table;
1052+
} else if (isChartPanelFigureMetadata(metadata)) {
1053+
name = metadata.figure;
10451054
} else {
1046-
name = metadata.name ?? metadata.figure;
1055+
name = metadata.name;
10471056
}
10481057
const inputFilterMap = this.getInputFilterColumnMap(
10491058
columnMap,
@@ -1156,7 +1165,7 @@ const mapStateToProps = (state: RootState, ownProps: OwnProps): StateProps => {
11561165
const { localDashboardId, metadata } = ownProps;
11571166

11581167
let sourcePanelId: string | undefined;
1159-
if (metadata != null) {
1168+
if (metadata != null && isChartPanelTableMetadata(metadata)) {
11601169
sourcePanelId = metadata.sourcePanelId;
11611170
}
11621171
const panelTableMap = getTableMapForDashboard(state, localDashboardId);

packages/dashboard-core-plugins/src/panels/ChartPanelUtils.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { DehydratedDashboardPanelProps } from '@deephaven/dashboard';
22
import type {
33
ChartPanelMetadata,
44
ChartPanelTableMetadata,
5+
ChartPanelFigureMetadata,
56
GLChartPanelState,
67
} from './ChartPanel';
78

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

15+
export function isChartPanelFigureMetadata(
16+
metadata: ChartPanelMetadata
17+
): metadata is ChartPanelFigureMetadata {
18+
return (metadata as ChartPanelFigureMetadata).figure !== undefined;
19+
}
20+
1421
export type DehydratedChartPanelProps = DehydratedDashboardPanelProps & {
1522
panelState?: GLChartPanelState;
1623
};

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ type LoadedPanelState = PanelState & {
134134

135135
export interface OwnProps extends DashboardPanelProps {
136136
children?: ReactNode;
137-
panelState: LoadedPanelState | null;
137+
panelState?: LoadedPanelState | null;
138138
makeModel: () => IrisGridModel | Promise<IrisGridModel>;
139139

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

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

327-
componentDidUpdate(_: never, prevState: IrisGridPanelState): void {
327+
componentDidUpdate(
328+
prevProps: IrisGridPanelProps,
329+
prevState: IrisGridPanelState
330+
): void {
328331
const { model } = this.state;
332+
const { makeModel } = this.props;
329333
if (model !== prevState.model) {
330334
if (prevState.model != null) {
331335
this.stopModelListening(prevState.model);
@@ -335,6 +339,10 @@ export class IrisGridPanel extends PureComponent<
335339
this.startModelListening(model);
336340
}
337341
}
342+
343+
if (makeModel !== prevProps.makeModel) {
344+
this.initModel();
345+
}
338346
}
339347

340348
componentWillUnmount(): void {

0 commit comments

Comments
 (0)