From 6212bd5d84737f0638a813598ac3ea1af1663598 Mon Sep 17 00:00:00 2001 From: Vlad Babich Date: Tue, 5 Mar 2024 13:41:58 -0700 Subject: [PATCH] feat: Export plotly-express as a dashboard plugin (#329) Tested with DHE V+ (1.20231218.176) Core 0.32.1 Closes #308 BREAKING CHANGE: - `widget` type in the `PanelEvent.OPEN` event arguments has changed from `VariableDefinition` to `VariableDescriptor` --- .../src/js/src/DashboardPlugin.tsx | 77 +++++++++++++++++++ .../src/js/src/PlotlyExpressChartUtils.ts | 11 ++- plugins/plotly-express/src/js/src/index.ts | 2 + 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 plugins/plotly-express/src/js/src/DashboardPlugin.tsx diff --git a/plugins/plotly-express/src/js/src/DashboardPlugin.tsx b/plugins/plotly-express/src/js/src/DashboardPlugin.tsx new file mode 100644 index 000000000..53b335620 --- /dev/null +++ b/plugins/plotly-express/src/js/src/DashboardPlugin.tsx @@ -0,0 +1,77 @@ +import { useCallback, DragEvent, useEffect } from 'react'; +import shortid from 'shortid'; +import { + DashboardPluginComponentProps, + LayoutUtils, + PanelEvent, + useListener, +} from '@deephaven/dashboard'; +import type { VariableDescriptor } from '@deephaven/jsapi-types'; +import PlotlyExpressChartPanel from './PlotlyExpressChartPanel.js'; +import type { PlotlyChartWidget } from './PlotlyExpressChartUtils.js'; + +export function DashboardPlugin( + props: DashboardPluginComponentProps +): JSX.Element | null { + const { id, layout, registerComponent } = props; + + const handlePanelOpen = useCallback( + async ({ + dragEvent, + fetch, + metadata = {}, + panelId = shortid.generate(), + widget, + }: { + dragEvent?: DragEvent; + fetch: () => Promise; + metadata?: Record; + panelId?: string; + widget: VariableDescriptor; + }) => { + const { type, name } = widget; + if (type !== 'deephaven.plot.express.DeephavenFigure') { + return; + } + + const config = { + type: 'react-component' as const, + component: 'PlotlyPanel', + props: { + localDashboardId: id, + id: panelId, + metadata: { + ...metadata, + ...widget, + figure: name, + }, + fetch, + }, + title: name, + id: panelId, + }; + + const { root } = layout; + LayoutUtils.openComponent({ root, config, dragEvent }); + }, + [id, layout] + ); + + useEffect( + function registerComponentsAndReturnCleanup() { + const cleanups = [ + registerComponent('PlotlyPanel', PlotlyExpressChartPanel), + ]; + return () => { + cleanups.forEach(cleanup => cleanup()); + }; + }, + [registerComponent] + ); + + useListener(layout.eventHub, PanelEvent.OPEN, handlePanelOpen); + + return null; +} + +export default DashboardPlugin; diff --git a/plugins/plotly-express/src/js/src/PlotlyExpressChartUtils.ts b/plugins/plotly-express/src/js/src/PlotlyExpressChartUtils.ts index c2b193f98..c91c66f82 100644 --- a/plugins/plotly-express/src/js/src/PlotlyExpressChartUtils.ts +++ b/plugins/plotly-express/src/js/src/PlotlyExpressChartUtils.ts @@ -1,5 +1,14 @@ import type { Data, PlotlyDataLayoutConfig } from 'plotly.js'; -import type { Widget } from '@deephaven/jsapi-types'; +import type { Table, Widget } from '@deephaven/jsapi-types'; + +export interface PlotlyChartWidget { + getDataAsBase64(): string; + exportedObjects: { fetch(): Promise }[]; + addEventListener( + type: string, + fn: (event: CustomEvent) => () => void + ): void; +} export interface PlotlyChartWidgetData { type: string; diff --git a/plugins/plotly-express/src/js/src/index.ts b/plugins/plotly-express/src/js/src/index.ts index 9d83e0884..312a2bc95 100644 --- a/plugins/plotly-express/src/js/src/index.ts +++ b/plugins/plotly-express/src/js/src/index.ts @@ -1,5 +1,7 @@ import { PlotlyExpressPlugin } from './PlotlyExpressPlugin.js'; +// Export legacy dashboard plugin as named export for backwards compatibility +export * from './DashboardPlugin.js'; export * from './PlotlyExpressChartModel.js'; export * from './PlotlyExpressChartUtils.js';