Skip to content

Commit

Permalink
refactor: Plugin cleanup (#1409)
Browse files Browse the repository at this point in the history
- Clean up a bunch of the typings around plugins
- Add hooks for panel registration, like `useComponent`
  - Removes a lot of the boilerplate code currently used in plugins
- One hydrate method now is used for new panels and rehydrating
dehydrated panels alike
- Makes the examples for
deephaven/deephaven-js-plugins#12 much cleaner
  • Loading branch information
mofojed authored Aug 14, 2023
1 parent 9fd5c27 commit 3445586
Show file tree
Hide file tree
Showing 29 changed files with 431 additions and 383 deletions.
2 changes: 1 addition & 1 deletion packages/code-studio/src/main/AppMainContainer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ describe('hydrates widgets correctly', () => {

expect(
screen.getByText(
'{"localDashboardId":"default","metadata":{"type":"TestType","name":"TestName"}}'
'{"metadata":{"type":"TestType","name":"TestName"},"localDashboardId":"default"}'
)
).toBeTruthy();
expect(connection.getObject).toHaveBeenCalled();
Expand Down
98 changes: 53 additions & 45 deletions packages/code-studio/src/main/AppMainContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { CSSTransition } from 'react-transition-group';
import { connect } from 'react-redux';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { ChartModel } from '@deephaven/chart';
import {
ContextActions,
ThemeExport,
Expand All @@ -31,12 +32,11 @@ import {
ClosedPanels,
Dashboard,
DashboardLayoutConfig,
DashboardPanelProps,
DashboardUtils,
DEFAULT_DASHBOARD_ID,
DehydratedDashboardPanelProps,
getDashboardData,
PanelEvent,
PanelProps,
updateDashboardData as updateDashboardDataAction,
} from '@deephaven/dashboard';
import {
Expand All @@ -56,12 +56,13 @@ import {
ChartBuilderPlugin,
FilterSet,
Link,
ChartPanelProps,
PandasPanelProps,
IrisGridPanelProps,
ColumnSelectionValidator,
getDashboardConnection,
TablePlugin,
IrisGridPanelMetadata,
isIrisGridPanelMetadata,
isLegacyIrisGridPanelMetadata,
isChartPanelDehydratedProps,
} from '@deephaven/dashboard-core-plugins';
import {
vsGear,
Expand Down Expand Up @@ -102,11 +103,7 @@ import { getLayoutStorage, getServerConfigValues } from '../redux';
import Logo from '../settings/community-wordmark-app.svg';
import './AppMainContainer.scss';
import WidgetList, { WindowMouseEvent } from './WidgetList';
import {
createChartModel,
createGridModel,
GridPanelMetadata,
} from './WidgetUtils';
import { createChartModel, createGridModel } from './WidgetUtils';
import EmptyDashboard from './EmptyDashboard';
import UserLayoutUtils from './UserLayoutUtils';
import DownloadServiceWorkerUtils from '../DownloadServiceWorkerUtils';
Expand Down Expand Up @@ -170,7 +167,10 @@ export class AppMainContainer extends Component<
event.returnValue = '';
}

static hydrateConsole(props: PanelProps, id: string): DashboardPanelProps {
static hydrateConsole(
props: DehydratedDashboardPanelProps,
id: string
): DehydratedDashboardPanelProps {
return DashboardUtils.hydrate(
{
...props,
Expand Down Expand Up @@ -208,8 +208,7 @@ export class AppMainContainer extends Component<
this.handleWidgetSelect = this.handleWidgetSelect.bind(this);
this.handlePaste = this.handlePaste.bind(this);
this.hydrateChart = this.hydrateChart.bind(this);
this.hydrateGrid = this.hydrateGrid.bind(this);
this.hydratePandas = this.hydratePandas.bind(this);
this.hydrateTable = this.hydrateTable.bind(this);
this.hydrateDefault = this.hydrateDefault.bind(this);
this.openNotebookFromURL = this.openNotebookFromURL.bind(this);
this.handleDisconnect = this.handleDisconnect.bind(this);
Expand Down Expand Up @@ -687,11 +686,9 @@ export class AppMainContainer extends Component<
}

hydrateDefault(
props: {
metadata?: { type?: string; id?: string; name?: string };
} & PanelProps,
props: DehydratedDashboardPanelProps,
id: string
): DashboardPanelProps & { fetch?: () => Promise<unknown> } {
): DehydratedDashboardPanelProps & { fetch?: () => Promise<unknown> } {
const { connection } = this.props;
const { metadata } = props;
if (
Expand All @@ -705,55 +702,70 @@ export class AppMainContainer extends Component<
type: metadata.type,
id: metadata.id,
}
: { type: metadata.type, name: metadata.name, title: metadata.name };
: {
type: metadata.type,
name: metadata.name,
title: metadata.name,
};
return {
fetch: () => connection.getObject(widget),
localDashboardId: id,
...props,
localDashboardId: id,
};
}
return DashboardUtils.hydrate(props, id);
}

hydrateGrid(props: IrisGridPanelProps, id: string): IrisGridPanelProps {
return this.hydrateTable(
props,
id,
props.metadata.type ?? dh.VariableType.TABLE
);
}

hydratePandas(props: PandasPanelProps, id: string): PandasPanelProps {
return this.hydrateTable(props, id, dh.VariableType.PANDAS);
}

hydrateTable<T extends { metadata: GridPanelMetadata }>(
props: T,
id: string,
type: string = dh.VariableType.TABLE
): T & {
hydrateTable(
props: DehydratedDashboardPanelProps,
id: string
): DehydratedDashboardPanelProps & {
getDownloadWorker: () => Promise<ServiceWorker>;
loadPlugin: (pluginName: string) => TablePlugin;
localDashboardId: string;
makeModel: () => Promise<IrisGridModel>;
} {
const { connection } = this.props;
let metadata: IrisGridPanelMetadata;
if (isIrisGridPanelMetadata(props.metadata)) {
metadata = props.metadata;
} else if (isLegacyIrisGridPanelMetadata(props.metadata)) {
metadata = {
name: props.metadata.table,
type: props.metadata.type ?? dh.VariableType.TABLE,
};
} else {
throw new Error('Metadata is required for table panel');
}

return {
...props,
getDownloadWorker: DownloadServiceWorkerUtils.getServiceWorker,
loadPlugin: this.handleLoadTablePlugin,
localDashboardId: id,
makeModel: () => createGridModel(dh, connection, props.metadata, type),
makeModel: () => createGridModel(dh, connection, metadata),
};
}

hydrateChart(props: ChartPanelProps, id: string): ChartPanelProps {
hydrateChart(
props: DehydratedDashboardPanelProps,
id: string
): DehydratedDashboardPanelProps & {
makeModel: () => Promise<ChartModel>;
} {
const { connection } = this.props;
return {
...props,
localDashboardId: id,
makeModel: () => {
const { metadata, panelState } = props;
const { metadata } = props;
const panelState = isChartPanelDehydratedProps(props)
? props.panelState
: undefined;
if (metadata == null) {
throw new Error('Metadata is required for chart panel');
}

return createChartModel(dh, connection, metadata, panelState);
},
};
Expand Down Expand Up @@ -914,11 +926,7 @@ export class AppMainContainer extends Component<
onLayoutInitialized={this.openNotebookFromURL}
hydrate={this.hydrateDefault}
>
<GridPlugin
hydrate={this.hydrateGrid}
getDownloadWorker={DownloadServiceWorkerUtils.getServiceWorker}
loadPlugin={this.handleLoadTablePlugin}
/>
<GridPlugin hydrate={this.hydrateTable} />
<ChartPlugin hydrate={this.hydrateChart} />
<ChartBuilderPlugin />
<ConsolePlugin
Expand All @@ -931,7 +939,7 @@ export class AppMainContainer extends Component<
}
/>
<FilterPlugin />
<PandasPlugin hydrate={this.hydratePandas} />
<PandasPlugin hydrate={this.hydrateTable} />
<MarkdownPlugin />
<LinkerPlugin />
{dashboardPlugins}
Expand Down
28 changes: 14 additions & 14 deletions packages/code-studio/src/main/WidgetUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,16 @@ import { getTimeZone, store } from '@deephaven/redux';
import {
ChartPanelMetadata,
GLChartPanelState,
IrisGridPanelMetadata,
isChartPanelTableMetadata,
} from '@deephaven/dashboard-core-plugins';

export type GridPanelMetadata = {
table: string;
};

export const createChartModel = async (
export async function createChartModel(
dh: DhType,
connection: IdeConnection,
metadata: ChartPanelMetadata,
panelState?: GLChartPanelState
): Promise<ChartModel> => {
): Promise<ChartModel> {
let settings;
let tableName;
let figureName;
Expand All @@ -39,10 +36,10 @@ export const createChartModel = async (
} else {
settings = {};
tableName = '';
figureName = metadata.figure;
figureName = metadata.name ?? metadata.figure;
tableSettings = {};
}
if (panelState !== undefined) {
if (panelState != null) {
if (panelState.tableSettings != null) {
tableSettings = panelState.tableSettings;
}
Expand All @@ -60,7 +57,7 @@ export const createChartModel = async (
}
}

if (figureName !== undefined) {
if (figureName != null) {
const definition = {
title: figureName,
name: figureName,
Expand All @@ -85,16 +82,19 @@ export const createChartModel = async (

// eslint-disable-next-line @typescript-eslint/no-explicit-any
return ChartModelFactory.makeModelFromSettings(dh, settings as any, table);
};
}

export const createGridModel = async (
dh: DhType,
connection: IdeConnection,
metadata: GridPanelMetadata,
type: string = dh.VariableType.TABLE
metadata: IrisGridPanelMetadata
): Promise<IrisGridModel> => {
const { table: tableName } = metadata;
const definition = { title: tableName, name: tableName, type };
const { name: tableName, type } = metadata;
const definition = {
title: tableName,
name: tableName,
type,
};
const table = (await connection.getObject(definition)) as Table;
return IrisGridModelFactory.makeModel(dh, table);
};
Expand Down
81 changes: 11 additions & 70 deletions packages/dashboard-core-plugins/src/ChartPlugin.tsx
Original file line number Diff line number Diff line change
@@ -1,86 +1,27 @@
import { DragEvent, useCallback, useEffect } from 'react';
import { ChartModelFactory } from '@deephaven/chart';
import {
assertIsDashboardPluginProps,
DashboardPluginComponentProps,
LayoutUtils,
PanelEvent,
PanelHydrateFunction,
useListener,
useDashboardPanel,
} from '@deephaven/dashboard';
import type { Figure, VariableDefinition } from '@deephaven/jsapi-types';
import { useApi } from '@deephaven/jsapi-bootstrap';
import shortid from 'shortid';
import { ChartPanel, ChartPanelProps } from './panels';
import { ChartPanel } from './panels';

export type ChartPluginProps = Partial<DashboardPluginComponentProps> & {
hydrate: PanelHydrateFunction<ChartPanelProps>;
hydrate: PanelHydrateFunction;
};

export function ChartPlugin(props: ChartPluginProps): JSX.Element | null {
assertIsDashboardPluginProps(props);
const { id, layout, registerComponent, hydrate } = props;

const { hydrate } = props;
const dh = useApi();

const handlePanelOpen = useCallback(
({
dragEvent,
fetch,
panelId = shortid.generate(),
widget,
}: {
dragEvent?: DragEvent;
fetch: () => Promise<Figure>;
panelId?: string;
widget: VariableDefinition;
}) => {
const { name, type } = widget;
if (type !== dh.VariableType.FIGURE) {
return;
}

const metadata = { name, figure: name };
const makeModel = () =>
fetch().then((figure: Figure) =>
ChartModelFactory.makeModel(dh, undefined, figure)
);
const config = {
type: 'react-component' as const,
component: ChartPanel.COMPONENT,
props: {
localDashboardId: id,
id: panelId,
metadata,
makeModel,
},
title: name,
id: panelId,
};

const { root } = layout;
LayoutUtils.openComponent({ root, config, dragEvent });
},
[dh, id, layout]
);

useEffect(
function registerComponentsAndReturnCleanup() {
const cleanups = [
registerComponent(
ChartPanel.COMPONENT,
ChartPanel,
hydrate as PanelHydrateFunction
),
];
return () => {
cleanups.forEach(cleanup => cleanup());
};
},
[hydrate, registerComponent]
);

useListener(layout.eventHub, PanelEvent.OPEN, handlePanelOpen);
useDashboardPanel({
dashboardProps: props,
componentName: ChartPanel.COMPONENT,
component: ChartPanel,
supportedTypes: dh.VariableType.FIGURE,
hydrate,
});

return null;
}
Expand Down
Loading

0 comments on commit 3445586

Please sign in to comment.