From 34ef5ceb85096c1cadb23ded9d2be2429f6b3f19 Mon Sep 17 00:00:00 2001 From: Matthew Runyon Date: Wed, 15 Nov 2023 15:10:19 -0600 Subject: [PATCH 1/5] fix: Changes for Deephaven UI embedding widget plugins --- .../src/ChartPlugin.tsx | 46 +++++++++-- .../src/ChartPluginConfig.ts | 4 +- .../dashboard-core-plugins/src/GridPlugin.tsx | 45 +++++++++-- .../src/GridPluginConfig.ts | 4 +- .../src/PandasPlugin.tsx | 77 ++++++++++++++++--- .../src/PandasPluginConfig.ts | 5 +- packages/dashboard-core-plugins/src/index.ts | 6 +- .../src/panels/PandasPanel.tsx | 70 +++++++++++------ packages/plugin/src/PluginTypes.ts | 6 +- 9 files changed, 202 insertions(+), 61 deletions(-) diff --git a/packages/dashboard-core-plugins/src/ChartPlugin.tsx b/packages/dashboard-core-plugins/src/ChartPlugin.tsx index d335d54191..7693ceb278 100644 --- a/packages/dashboard-core-plugins/src/ChartPlugin.tsx +++ b/packages/dashboard-core-plugins/src/ChartPlugin.tsx @@ -1,7 +1,8 @@ -import { forwardRef, useMemo } from 'react'; +import { forwardRef, useEffect, useMemo, useState } from 'react'; import { useApi } from '@deephaven/jsapi-bootstrap'; import { useConnection } from '@deephaven/jsapi-components'; import { + Chart, ChartModel, ChartModelFactory, ChartTheme, @@ -14,7 +15,7 @@ import type { } from '@deephaven/jsapi-types'; import { IrisGridUtils } from '@deephaven/iris-grid'; import { getTimeZone, store } from '@deephaven/redux'; -import { type WidgetComponentProps } from '@deephaven/plugin'; +import { WidgetPanelProps, type WidgetComponentProps } from '@deephaven/plugin'; import { ChartPanelMetadata, GLChartPanelState, @@ -113,8 +114,41 @@ async function createChartModel( ); } -export const ChartPlugin = forwardRef( - (props: WidgetComponentProps, ref: React.Ref) => { +export function ChartPlugin(props: WidgetComponentProps): JSX.Element | null { + const dh = useApi(); + const chartTheme = useChartTheme(); + const [model, setModel] = useState(); + + const { fetch } = props; + + useEffect(() => { + let cancelled = false; + async function init() { + const figure = (await fetch()) as unknown as Figure; + const newModel = await ChartModelFactory.makeModel( + dh, + undefined, + figure, + chartTheme + ); + + if (!cancelled) { + setModel(newModel); + } + } + + init(); + + return () => { + cancelled = true; + }; + }, [dh, fetch, chartTheme]); + + return model ? : null; +} + +export const ChartPanelPlugin = forwardRef( + (props: WidgetPanelProps, ref: React.Ref) => { const dh = useApi(); const chartTheme = useChartTheme(); const connection = useConnection(); @@ -160,6 +194,4 @@ export const ChartPlugin = forwardRef( } ); -ChartPlugin.displayName = 'ChartPlugin'; - -export default ChartPlugin; +ChartPanelPlugin.displayName = 'ChartPanelPlugin'; diff --git a/packages/dashboard-core-plugins/src/ChartPluginConfig.ts b/packages/dashboard-core-plugins/src/ChartPluginConfig.ts index db712f8809..dff9c32f08 100644 --- a/packages/dashboard-core-plugins/src/ChartPluginConfig.ts +++ b/packages/dashboard-core-plugins/src/ChartPluginConfig.ts @@ -1,13 +1,13 @@ import { PluginType, type WidgetPlugin } from '@deephaven/plugin'; import { vsGraph } from '@deephaven/icons'; -import ChartPlugin from './ChartPlugin'; +import { ChartPlugin, ChartPanelPlugin } from './ChartPlugin'; const ChartPluginConfig: WidgetPlugin = { name: 'ChartPanel', title: 'Chart', type: PluginType.WIDGET_PLUGIN, component: ChartPlugin, - panelComponent: ChartPlugin, + panelComponent: ChartPanelPlugin, supportedTypes: 'Figure', icon: vsGraph, }; diff --git a/packages/dashboard-core-plugins/src/GridPlugin.tsx b/packages/dashboard-core-plugins/src/GridPlugin.tsx index 72601da32a..988e5aa504 100644 --- a/packages/dashboard-core-plugins/src/GridPlugin.tsx +++ b/packages/dashboard-core-plugins/src/GridPlugin.tsx @@ -1,13 +1,46 @@ -import { forwardRef } from 'react'; -import { type WidgetComponentProps } from '@deephaven/plugin'; +import { useEffect, useState, forwardRef } from 'react'; +import { WidgetPanelProps, type WidgetComponentProps } from '@deephaven/plugin'; import { type Table } from '@deephaven/jsapi-types'; +import { useApi } from '@deephaven/jsapi-bootstrap'; +import { + IrisGrid, + IrisGridModelFactory, + type IrisGridModel, +} from '@deephaven/iris-grid'; import useHydrateGrid from './useHydrateGrid'; import ConnectedIrisGridPanel, { type IrisGridPanel, } from './panels/IrisGridPanel'; -export const GridPlugin = forwardRef( - (props: WidgetComponentProps, ref: React.Ref) => { +export function GridPlugin(props: WidgetComponentProps): JSX.Element | null { + const dh = useApi(); + const [model, setModel] = useState(); + + const { fetch } = props; + + useEffect(() => { + let cancelled = false; + async function init() { + const table = (await fetch()) as unknown as Table; + const newModel = await IrisGridModelFactory.makeModel(dh, table); + if (!cancelled) { + setModel(newModel); + } + } + + init(); + + return () => { + cancelled = true; + }; + }, [dh, fetch]); + + // eslint-disable-next-line react/jsx-props-no-spreading + return model ? : null; +} + +export const GridPanelPlugin = forwardRef( + (props: WidgetPanelProps, ref: React.Ref) => { const { localDashboardId, fetch } = props; const hydratedProps = useHydrateGrid( fetch as unknown as () => Promise, @@ -19,6 +52,4 @@ export const GridPlugin = forwardRef( } ); -GridPlugin.displayName = 'GridPlugin'; - -export default GridPlugin; +GridPanelPlugin.displayName = 'GridPanelPlugin'; diff --git a/packages/dashboard-core-plugins/src/GridPluginConfig.ts b/packages/dashboard-core-plugins/src/GridPluginConfig.ts index 9b17fbc708..ef0a26c54f 100644 --- a/packages/dashboard-core-plugins/src/GridPluginConfig.ts +++ b/packages/dashboard-core-plugins/src/GridPluginConfig.ts @@ -1,13 +1,13 @@ import { PluginType, type WidgetPlugin } from '@deephaven/plugin'; import { dhTable } from '@deephaven/icons'; -import GridPlugin from './GridPlugin'; +import { GridPlugin, GridPanelPlugin } from './GridPlugin'; const GridPluginConfig: WidgetPlugin = { name: 'IrisGridPanel', title: 'Table', type: PluginType.WIDGET_PLUGIN, component: GridPlugin, - panelComponent: GridPlugin, + panelComponent: GridPanelPlugin, supportedTypes: ['Table', 'TreeTable', 'HierarchicalTable'], icon: dhTable, }; diff --git a/packages/dashboard-core-plugins/src/PandasPlugin.tsx b/packages/dashboard-core-plugins/src/PandasPlugin.tsx index 0b5ce7a4e9..b268c200c3 100644 --- a/packages/dashboard-core-plugins/src/PandasPlugin.tsx +++ b/packages/dashboard-core-plugins/src/PandasPlugin.tsx @@ -1,22 +1,79 @@ -import { forwardRef } from 'react'; -import { WidgetComponentProps } from '@deephaven/plugin'; +import { forwardRef, useCallback, useEffect, useState } from 'react'; +import { WidgetComponentProps, WidgetPanelProps } from '@deephaven/plugin'; import { type Table } from '@deephaven/jsapi-types'; -import { PandasPanel } from './panels'; +import IrisGrid, { + IrisGridModelFactory, + type IrisGridModel, +} from '@deephaven/iris-grid'; +import { useApi } from '@deephaven/jsapi-bootstrap'; +import { LoadingOverlay } from '@deephaven/components'; +import { PandasPanel, PandasReloadButton } from './panels'; import useHydrateGrid from './useHydrateGrid'; -export const PandasPlugin = forwardRef( - (props: WidgetComponentProps, ref: React.Ref) => { +export function PandasPlugin(props: WidgetComponentProps): JSX.Element | null { + const dh = useApi(); + const [model, setModel] = useState(); + const [isLoading, setIsLoading] = useState(true); + const [isLoaded, setIsLoaded] = useState(false); + + const { fetch } = props; + + const makeModel = useCallback(async () => { + const table = (await fetch()) as unknown as Table; + return IrisGridModelFactory.makeModel(dh, table); + }, [dh, fetch]); + + const handleReload = useCallback(async () => { + setIsLoading(true); + const newModel = await makeModel(); + setModel(newModel); + setIsLoading(false); + }, [makeModel]); + + useEffect(() => { + let cancelled = false; + async function init() { + const newModel = await makeModel(); + if (!cancelled) { + setModel(newModel); + setIsLoaded(true); + setIsLoading(false); + } + } + + init(); + setIsLoading(true); + + return () => { + cancelled = true; + }; + }, [makeModel]); + + return ( + <> + + {model && ( + + + + )} + + ); +} + +export const PandasPanelPlugin = forwardRef( + (props: WidgetPanelProps, ref: React.Ref) => { const { localDashboardId, fetch } = props; const hydratedProps = useHydrateGrid( fetch as unknown as () => Promise
, localDashboardId ); - // eslint-disable-next-line react/jsx-props-no-spreading - return ; + return ( + // eslint-disable-next-line react/jsx-props-no-spreading + + ); } ); -PandasPlugin.displayName = 'PandasPlugin'; - -export default PandasPlugin; +PandasPanelPlugin.displayName = 'PandasPanelPlugin'; diff --git a/packages/dashboard-core-plugins/src/PandasPluginConfig.ts b/packages/dashboard-core-plugins/src/PandasPluginConfig.ts index b2c9ace1e0..2497d92bc2 100644 --- a/packages/dashboard-core-plugins/src/PandasPluginConfig.ts +++ b/packages/dashboard-core-plugins/src/PandasPluginConfig.ts @@ -1,14 +1,13 @@ import { PluginType, WidgetPlugin } from '@deephaven/plugin'; import { dhPandas } from '@deephaven/icons'; -import PandasPlugin from './PandasPlugin'; +import { PandasPlugin, PandasPanelPlugin } from './PandasPlugin'; const PandasPluginConfig: WidgetPlugin = { name: 'PandasPanel', title: 'Pandas', type: PluginType.WIDGET_PLUGIN, - // TODO: #1573 Replace with actual base component and not just the panel plugin component: PandasPlugin, - panelComponent: PandasPlugin, + panelComponent: PandasPanelPlugin, supportedTypes: 'pandas.DataFrame', icon: dhPandas, }; diff --git a/packages/dashboard-core-plugins/src/index.ts b/packages/dashboard-core-plugins/src/index.ts index 7c405a4a77..2c92e2c197 100644 --- a/packages/dashboard-core-plugins/src/index.ts +++ b/packages/dashboard-core-plugins/src/index.ts @@ -1,17 +1,17 @@ -export { default as ChartPlugin } from './ChartPlugin'; +export * from './ChartPlugin'; export { default as ChartPluginConfig } from './ChartPluginConfig'; export { default as ChartBuilderPlugin } from './ChartBuilderPlugin'; export { default as ChartBuilderPluginConfig } from './ChartBuilderPluginConfig'; export { default as ConsolePlugin } from './ConsolePlugin'; export { default as FilterPlugin } from './FilterPlugin'; export { default as FilterPluginConfig } from './FilterPluginConfig'; -export { default as GridPlugin } from './GridPlugin'; +export * from './GridPlugin'; export { default as GridPluginConfig } from './GridPluginConfig'; export { default as LinkerPlugin } from './LinkerPlugin'; export { default as LinkerPluginConfig } from './LinkerPluginConfig'; export { default as MarkdownPlugin } from './MarkdownPlugin'; export { default as MarkdownPluginConfig } from './MarkdownPluginConfig'; -export { default as PandasPlugin } from './PandasPlugin'; +export * from './PandasPlugin'; export { default as PandasPluginConfig } from './PandasPluginConfig'; export { default as WidgetLoaderPlugin } from './WidgetLoaderPlugin'; export { default as WidgetLoaderPluginConfig } from './WidgetLoaderPluginConfig'; diff --git a/packages/dashboard-core-plugins/src/panels/PandasPanel.tsx b/packages/dashboard-core-plugins/src/panels/PandasPanel.tsx index 2c9388fbca..dea00d0702 100644 --- a/packages/dashboard-core-plugins/src/panels/PandasPanel.tsx +++ b/packages/dashboard-core-plugins/src/panels/PandasPanel.tsx @@ -1,6 +1,12 @@ /* eslint-disable react/jsx-props-no-spreading */ /* eslint-disable react/no-unused-state */ -import React, { Component, ReactElement, RefObject } from 'react'; +import React, { + Component, + ReactElement, + RefObject, + useCallback, + useRef, +} from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { dhRefresh } from '@deephaven/icons'; import { Button } from '@deephaven/components'; @@ -11,6 +17,42 @@ import ConnectedIrisGridPanel, { } from './IrisGridPanel'; import './PandasPanel.scss'; +export function PandasReloadButton({ + onClick, +}: { + onClick: React.MouseEventHandler; +}): JSX.Element { + const buttonRef = useRef(null); + + const handleClick = useCallback( + (e: React.MouseEvent) => { + buttonRef.current?.blur(); + onClick(e); + }, + [onClick] + ); + + return ( + + ); +} + export interface PandasPanelProps extends IrisGridPanelOwnProps { panelState: PanelState | null; } @@ -34,7 +76,6 @@ class PandasPanel extends Component { super(props); this.irisGridRef = React.createRef(); - this.buttonRef = React.createRef(); this.handleReload = this.handleReload.bind(this); this.handleGridStateChange = this.handleGridStateChange.bind(this); @@ -47,13 +88,10 @@ class PandasPanel extends Component { }; } - buttonRef: RefObject; - irisGridRef: RefObject; handleReload(): void { this.irisGridRef.current?.initModel(); - this.buttonRef.current?.blur(); this.setState({ shouldFocusGrid: true, }); @@ -76,32 +114,14 @@ class PandasPanel extends Component { } render(): ReactElement { - const { ...props } = this.props; - return ( - + ); } diff --git a/packages/plugin/src/PluginTypes.ts b/packages/plugin/src/PluginTypes.ts index eafcf755ed..27cb33861a 100644 --- a/packages/plugin/src/PluginTypes.ts +++ b/packages/plugin/src/PluginTypes.ts @@ -106,6 +106,9 @@ export function isDashboardPlugin( export interface WidgetComponentProps { fetch: () => Promise; +} + +export interface WidgetPanelProps extends WidgetComponentProps { metadata?: { id?: string; name?: string; @@ -150,8 +153,7 @@ export interface WidgetPlugin extends Plugin { * * See @deephaven/dashboard-core-plugins WidgetPanel for the component that should be used here. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - panelComponent?: React.ComponentType; + panelComponent?: React.ComponentType; /** * The icon to display next to the console button. From 8baf873d1d488b9ef1061565e621110e31367cf2 Mon Sep 17 00:00:00 2001 From: Matthew Runyon Date: Wed, 15 Nov 2023 18:17:29 -0600 Subject: [PATCH 2/5] Fix for plotly-express --- packages/chart/src/Chart.tsx | 3 ++- packages/dashboard-core-plugins/src/panels/ChartPanel.tsx | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/chart/src/Chart.tsx b/packages/chart/src/Chart.tsx index 5a403f9a02..af6af5914a 100644 --- a/packages/chart/src/Chart.tsx +++ b/packages/chart/src/Chart.tsx @@ -49,6 +49,7 @@ interface ChartProps { settings: FormatterSettings; isActive: boolean; Plotly: typeof Plotly; + containerRef?: React.RefObject; onDisconnect: () => void; onReconnect: () => void; onUpdate: (obj: { isLoading: boolean }) => void; @@ -145,7 +146,7 @@ export class Chart extends Component { this.PlotComponent = createPlotlyComponent(props.Plotly); this.plot = React.createRef(); - this.plotWrapper = React.createRef(); + this.plotWrapper = props.containerRef ?? React.createRef(); this.columnFormats = []; this.dateTimeFormatterOptions = {}; this.decimalFormatOptions = {}; diff --git a/packages/dashboard-core-plugins/src/panels/ChartPanel.tsx b/packages/dashboard-core-plugins/src/panels/ChartPanel.tsx index 2dc94c2298..a1ba969fe3 100644 --- a/packages/dashboard-core-plugins/src/panels/ChartPanel.tsx +++ b/packages/dashboard-core-plugins/src/panels/ChartPanel.tsx @@ -128,7 +128,7 @@ interface OwnProps extends DashboardPanelProps { makeModel: () => Promise; localDashboardId: string; Plotly?: typeof PlotlyType; - /** The panel container div */ + /** The plot container div */ containerRef?: RefObject; panelState?: GLChartPanelState; @@ -236,7 +236,7 @@ export class ChartPanel extends Component { ); this.handleClearAllFilters = this.handleClearAllFilters.bind(this); - this.panelContainer = props.containerRef ?? React.createRef(); + this.panelContainer = React.createRef(); this.pending = new Pending(); const { metadata, panelState } = props; @@ -1035,6 +1035,7 @@ export class ChartPanel extends Component { metadata, settings, Plotly, + containerRef, } = this.props; const { columnMap, @@ -1117,6 +1118,7 @@ export class ChartPanel extends Component { onError={this.handleError} onSettingsChanged={this.handleSettingsChanged} Plotly={Plotly} + containerRef={containerRef} /> )} From c33d928084bc5d742c05a078c2f54b735e784cae Mon Sep 17 00:00:00 2001 From: Matthew Runyon Date: Thu, 16 Nov 2023 14:58:16 -0600 Subject: [PATCH 3/5] Split into separate files --- .../src/plugins/remote-component.config.ts | 2 + .../{ChartPlugin.tsx => ChartPanelPlugin.tsx} | 40 ++------------ .../src/ChartPluginConfig.ts | 5 +- .../src/ChartWidgetPlugin.tsx | 47 ++++++++++++++++ .../src/GridPanelPlugin.tsx | 24 ++++++++ .../dashboard-core-plugins/src/GridPlugin.tsx | 55 ------------------- .../src/GridPluginConfig.ts | 5 +- .../src/GridWidgetPlugin.tsx | 40 ++++++++++++++ .../src/PandasPanelPlugin.tsx | 24 ++++++++ .../src/PandasPluginConfig.ts | 5 +- ...andasPlugin.tsx => PandasWidgetPlugin.tsx} | 28 +++------- packages/dashboard-core-plugins/src/index.ts | 9 ++- 12 files changed, 163 insertions(+), 121 deletions(-) rename packages/dashboard-core-plugins/src/{ChartPlugin.tsx => ChartPanelPlugin.tsx} (82%) create mode 100644 packages/dashboard-core-plugins/src/ChartWidgetPlugin.tsx create mode 100644 packages/dashboard-core-plugins/src/GridPanelPlugin.tsx delete mode 100644 packages/dashboard-core-plugins/src/GridPlugin.tsx create mode 100644 packages/dashboard-core-plugins/src/GridWidgetPlugin.tsx create mode 100644 packages/dashboard-core-plugins/src/PandasPanelPlugin.tsx rename packages/dashboard-core-plugins/src/{PandasPlugin.tsx => PandasWidgetPlugin.tsx} (63%) diff --git a/packages/app-utils/src/plugins/remote-component.config.ts b/packages/app-utils/src/plugins/remote-component.config.ts index d1e0523bbb..ee96f29e5d 100644 --- a/packages/app-utils/src/plugins/remote-component.config.ts +++ b/packages/app-utils/src/plugins/remote-component.config.ts @@ -20,6 +20,7 @@ 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'; +import * as DeephavenCorePlugins from '@deephaven/dashboard-core-plugins'; // eslint-disable-next-line import/prefer-default-export export const resolve = { @@ -40,4 +41,5 @@ export const resolve = { '@deephaven/log': DeephavenLog, '@deephaven/plugin': DeephavenPlugin, '@deephaven/react-hooks': DeephavenReactHooks, + '@deephaven/dashboard-core-plugins': DeephavenCorePlugins, }; diff --git a/packages/dashboard-core-plugins/src/ChartPlugin.tsx b/packages/dashboard-core-plugins/src/ChartPanelPlugin.tsx similarity index 82% rename from packages/dashboard-core-plugins/src/ChartPlugin.tsx rename to packages/dashboard-core-plugins/src/ChartPanelPlugin.tsx index 7693ceb278..5dfbe3974f 100644 --- a/packages/dashboard-core-plugins/src/ChartPlugin.tsx +++ b/packages/dashboard-core-plugins/src/ChartPanelPlugin.tsx @@ -1,8 +1,7 @@ -import { forwardRef, useEffect, useMemo, useState } from 'react'; +import { forwardRef, useMemo } from 'react'; import { useApi } from '@deephaven/jsapi-bootstrap'; import { useConnection } from '@deephaven/jsapi-components'; import { - Chart, ChartModel, ChartModelFactory, ChartTheme, @@ -15,7 +14,7 @@ import type { } from '@deephaven/jsapi-types'; import { IrisGridUtils } from '@deephaven/iris-grid'; import { getTimeZone, store } from '@deephaven/redux'; -import { WidgetPanelProps, type WidgetComponentProps } from '@deephaven/plugin'; +import { WidgetPanelProps } from '@deephaven/plugin'; import { ChartPanelMetadata, GLChartPanelState, @@ -114,39 +113,6 @@ async function createChartModel( ); } -export function ChartPlugin(props: WidgetComponentProps): JSX.Element | null { - const dh = useApi(); - const chartTheme = useChartTheme(); - const [model, setModel] = useState(); - - const { fetch } = props; - - useEffect(() => { - let cancelled = false; - async function init() { - const figure = (await fetch()) as unknown as Figure; - const newModel = await ChartModelFactory.makeModel( - dh, - undefined, - figure, - chartTheme - ); - - if (!cancelled) { - setModel(newModel); - } - } - - init(); - - return () => { - cancelled = true; - }; - }, [dh, fetch, chartTheme]); - - return model ? : null; -} - export const ChartPanelPlugin = forwardRef( (props: WidgetPanelProps, ref: React.Ref) => { const dh = useApi(); @@ -195,3 +161,5 @@ export const ChartPanelPlugin = forwardRef( ); ChartPanelPlugin.displayName = 'ChartPanelPlugin'; + +export default ChartPanelPlugin; diff --git a/packages/dashboard-core-plugins/src/ChartPluginConfig.ts b/packages/dashboard-core-plugins/src/ChartPluginConfig.ts index dff9c32f08..e63cb9ee87 100644 --- a/packages/dashboard-core-plugins/src/ChartPluginConfig.ts +++ b/packages/dashboard-core-plugins/src/ChartPluginConfig.ts @@ -1,12 +1,13 @@ import { PluginType, type WidgetPlugin } from '@deephaven/plugin'; import { vsGraph } from '@deephaven/icons'; -import { ChartPlugin, ChartPanelPlugin } from './ChartPlugin'; +import { ChartWidgetPlugin } from './ChartWidgetPlugin'; +import { ChartPanelPlugin } from './ChartPanelPlugin'; const ChartPluginConfig: WidgetPlugin = { name: 'ChartPanel', title: 'Chart', type: PluginType.WIDGET_PLUGIN, - component: ChartPlugin, + component: ChartWidgetPlugin, panelComponent: ChartPanelPlugin, supportedTypes: 'Figure', icon: vsGraph, diff --git a/packages/dashboard-core-plugins/src/ChartWidgetPlugin.tsx b/packages/dashboard-core-plugins/src/ChartWidgetPlugin.tsx new file mode 100644 index 0000000000..f55b86b071 --- /dev/null +++ b/packages/dashboard-core-plugins/src/ChartWidgetPlugin.tsx @@ -0,0 +1,47 @@ +import { useEffect, useState } from 'react'; +import { useApi } from '@deephaven/jsapi-bootstrap'; +import { + Chart, + ChartModel, + ChartModelFactory, + useChartTheme, +} from '@deephaven/chart'; +import type { Figure } from '@deephaven/jsapi-types'; +import { type WidgetComponentProps } from '@deephaven/plugin'; + +export function ChartWidgetPlugin( + props: WidgetComponentProps +): JSX.Element | null { + const dh = useApi(); + const chartTheme = useChartTheme(); + const [model, setModel] = useState(); + + const { fetch } = props; + + useEffect(() => { + let cancelled = false; + async function init() { + const figure = (await fetch()) as unknown as Figure; + const newModel = await ChartModelFactory.makeModel( + dh, + undefined, + figure, + chartTheme + ); + + if (!cancelled) { + setModel(newModel); + } + } + + init(); + + return () => { + cancelled = true; + }; + }, [dh, fetch, chartTheme]); + + return model ? : null; +} + +export default ChartWidgetPlugin; diff --git a/packages/dashboard-core-plugins/src/GridPanelPlugin.tsx b/packages/dashboard-core-plugins/src/GridPanelPlugin.tsx new file mode 100644 index 0000000000..75666bd064 --- /dev/null +++ b/packages/dashboard-core-plugins/src/GridPanelPlugin.tsx @@ -0,0 +1,24 @@ +import { forwardRef } from 'react'; +import { WidgetPanelProps } from '@deephaven/plugin'; +import { type Table } from '@deephaven/jsapi-types'; +import useHydrateGrid from './useHydrateGrid'; +import ConnectedIrisGridPanel, { + type IrisGridPanel, +} from './panels/IrisGridPanel'; + +export const GridPanelPlugin = forwardRef( + (props: WidgetPanelProps, ref: React.Ref) => { + const { localDashboardId, fetch } = props; + const hydratedProps = useHydrateGrid( + fetch as unknown as () => Promise
, + localDashboardId + ); + + // eslint-disable-next-line react/jsx-props-no-spreading + return ; + } +); + +GridPanelPlugin.displayName = 'GridPanelPlugin'; + +export default GridPanelPlugin; diff --git a/packages/dashboard-core-plugins/src/GridPlugin.tsx b/packages/dashboard-core-plugins/src/GridPlugin.tsx deleted file mode 100644 index 988e5aa504..0000000000 --- a/packages/dashboard-core-plugins/src/GridPlugin.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { useEffect, useState, forwardRef } from 'react'; -import { WidgetPanelProps, type WidgetComponentProps } from '@deephaven/plugin'; -import { type Table } from '@deephaven/jsapi-types'; -import { useApi } from '@deephaven/jsapi-bootstrap'; -import { - IrisGrid, - IrisGridModelFactory, - type IrisGridModel, -} from '@deephaven/iris-grid'; -import useHydrateGrid from './useHydrateGrid'; -import ConnectedIrisGridPanel, { - type IrisGridPanel, -} from './panels/IrisGridPanel'; - -export function GridPlugin(props: WidgetComponentProps): JSX.Element | null { - const dh = useApi(); - const [model, setModel] = useState(); - - const { fetch } = props; - - useEffect(() => { - let cancelled = false; - async function init() { - const table = (await fetch()) as unknown as Table; - const newModel = await IrisGridModelFactory.makeModel(dh, table); - if (!cancelled) { - setModel(newModel); - } - } - - init(); - - return () => { - cancelled = true; - }; - }, [dh, fetch]); - - // eslint-disable-next-line react/jsx-props-no-spreading - return model ? : null; -} - -export const GridPanelPlugin = forwardRef( - (props: WidgetPanelProps, ref: React.Ref) => { - const { localDashboardId, fetch } = props; - const hydratedProps = useHydrateGrid( - fetch as unknown as () => Promise
, - localDashboardId - ); - - // eslint-disable-next-line react/jsx-props-no-spreading - return ; - } -); - -GridPanelPlugin.displayName = 'GridPanelPlugin'; diff --git a/packages/dashboard-core-plugins/src/GridPluginConfig.ts b/packages/dashboard-core-plugins/src/GridPluginConfig.ts index ef0a26c54f..6e18aebe1b 100644 --- a/packages/dashboard-core-plugins/src/GridPluginConfig.ts +++ b/packages/dashboard-core-plugins/src/GridPluginConfig.ts @@ -1,12 +1,13 @@ import { PluginType, type WidgetPlugin } from '@deephaven/plugin'; import { dhTable } from '@deephaven/icons'; -import { GridPlugin, GridPanelPlugin } from './GridPlugin'; +import { GridWidgetPlugin } from './GridWidgetPlugin'; +import { GridPanelPlugin } from './GridPanelPlugin'; const GridPluginConfig: WidgetPlugin = { name: 'IrisGridPanel', title: 'Table', type: PluginType.WIDGET_PLUGIN, - component: GridPlugin, + component: GridWidgetPlugin, panelComponent: GridPanelPlugin, supportedTypes: ['Table', 'TreeTable', 'HierarchicalTable'], icon: dhTable, diff --git a/packages/dashboard-core-plugins/src/GridWidgetPlugin.tsx b/packages/dashboard-core-plugins/src/GridWidgetPlugin.tsx new file mode 100644 index 0000000000..f7de8e52b1 --- /dev/null +++ b/packages/dashboard-core-plugins/src/GridWidgetPlugin.tsx @@ -0,0 +1,40 @@ +import { useEffect, useState } from 'react'; +import { type WidgetComponentProps } from '@deephaven/plugin'; +import { type Table } from '@deephaven/jsapi-types'; +import { useApi } from '@deephaven/jsapi-bootstrap'; +import { + IrisGrid, + IrisGridModelFactory, + type IrisGridModel, +} from '@deephaven/iris-grid'; + +export function GridWidgetPlugin( + props: WidgetComponentProps +): JSX.Element | null { + const dh = useApi(); + const [model, setModel] = useState(); + + const { fetch } = props; + + useEffect(() => { + let cancelled = false; + async function init() { + const table = (await fetch()) as unknown as Table; + const newModel = await IrisGridModelFactory.makeModel(dh, table); + if (!cancelled) { + setModel(newModel); + } + } + + init(); + + return () => { + cancelled = true; + }; + }, [dh, fetch]); + + // eslint-disable-next-line react/jsx-props-no-spreading + return model ? : null; +} + +export default GridWidgetPlugin; diff --git a/packages/dashboard-core-plugins/src/PandasPanelPlugin.tsx b/packages/dashboard-core-plugins/src/PandasPanelPlugin.tsx new file mode 100644 index 0000000000..520130cb56 --- /dev/null +++ b/packages/dashboard-core-plugins/src/PandasPanelPlugin.tsx @@ -0,0 +1,24 @@ +import { forwardRef } from 'react'; +import { WidgetPanelProps } from '@deephaven/plugin'; +import { type Table } from '@deephaven/jsapi-types'; +import { PandasPanel } from './panels'; +import useHydrateGrid from './useHydrateGrid'; + +export const PandasPanelPlugin = forwardRef( + (props: WidgetPanelProps, ref: React.Ref) => { + const { localDashboardId, fetch } = props; + const hydratedProps = useHydrateGrid( + fetch as unknown as () => Promise
, + localDashboardId + ); + + return ( + // eslint-disable-next-line react/jsx-props-no-spreading + + ); + } +); + +PandasPanelPlugin.displayName = 'PandasPanelPlugin'; + +export default PandasPanelPlugin; diff --git a/packages/dashboard-core-plugins/src/PandasPluginConfig.ts b/packages/dashboard-core-plugins/src/PandasPluginConfig.ts index 2497d92bc2..0902e2b689 100644 --- a/packages/dashboard-core-plugins/src/PandasPluginConfig.ts +++ b/packages/dashboard-core-plugins/src/PandasPluginConfig.ts @@ -1,12 +1,13 @@ import { PluginType, WidgetPlugin } from '@deephaven/plugin'; import { dhPandas } from '@deephaven/icons'; -import { PandasPlugin, PandasPanelPlugin } from './PandasPlugin'; +import { PandasWidgetPlugin } from './PandasWidgetPlugin'; +import { PandasPanelPlugin } from './PandasPanelPlugin'; const PandasPluginConfig: WidgetPlugin = { name: 'PandasPanel', title: 'Pandas', type: PluginType.WIDGET_PLUGIN, - component: PandasPlugin, + component: PandasWidgetPlugin, panelComponent: PandasPanelPlugin, supportedTypes: 'pandas.DataFrame', icon: dhPandas, diff --git a/packages/dashboard-core-plugins/src/PandasPlugin.tsx b/packages/dashboard-core-plugins/src/PandasWidgetPlugin.tsx similarity index 63% rename from packages/dashboard-core-plugins/src/PandasPlugin.tsx rename to packages/dashboard-core-plugins/src/PandasWidgetPlugin.tsx index b268c200c3..cde5b0d5a8 100644 --- a/packages/dashboard-core-plugins/src/PandasPlugin.tsx +++ b/packages/dashboard-core-plugins/src/PandasWidgetPlugin.tsx @@ -1,5 +1,5 @@ -import { forwardRef, useCallback, useEffect, useState } from 'react'; -import { WidgetComponentProps, WidgetPanelProps } from '@deephaven/plugin'; +import { useCallback, useEffect, useState } from 'react'; +import { WidgetComponentProps } from '@deephaven/plugin'; import { type Table } from '@deephaven/jsapi-types'; import IrisGrid, { IrisGridModelFactory, @@ -7,10 +7,11 @@ import IrisGrid, { } from '@deephaven/iris-grid'; import { useApi } from '@deephaven/jsapi-bootstrap'; import { LoadingOverlay } from '@deephaven/components'; -import { PandasPanel, PandasReloadButton } from './panels'; -import useHydrateGrid from './useHydrateGrid'; +import { PandasReloadButton } from './panels'; -export function PandasPlugin(props: WidgetComponentProps): JSX.Element | null { +export function PandasWidgetPlugin( + props: WidgetComponentProps +): JSX.Element | null { const dh = useApi(); const [model, setModel] = useState(); const [isLoading, setIsLoading] = useState(true); @@ -61,19 +62,4 @@ export function PandasPlugin(props: WidgetComponentProps): JSX.Element | null { ); } -export const PandasPanelPlugin = forwardRef( - (props: WidgetPanelProps, ref: React.Ref) => { - const { localDashboardId, fetch } = props; - const hydratedProps = useHydrateGrid( - fetch as unknown as () => Promise
, - localDashboardId - ); - - return ( - // eslint-disable-next-line react/jsx-props-no-spreading - - ); - } -); - -PandasPanelPlugin.displayName = 'PandasPanelPlugin'; +export default PandasWidgetPlugin; diff --git a/packages/dashboard-core-plugins/src/index.ts b/packages/dashboard-core-plugins/src/index.ts index 2c92e2c197..476e909c01 100644 --- a/packages/dashboard-core-plugins/src/index.ts +++ b/packages/dashboard-core-plugins/src/index.ts @@ -1,17 +1,20 @@ -export * from './ChartPlugin'; +export { default as ChartPanelPlugin } from './ChartPanelPlugin'; +export { default as ChartWidgetPlugin } from './ChartWidgetPlugin'; export { default as ChartPluginConfig } from './ChartPluginConfig'; export { default as ChartBuilderPlugin } from './ChartBuilderPlugin'; export { default as ChartBuilderPluginConfig } from './ChartBuilderPluginConfig'; export { default as ConsolePlugin } from './ConsolePlugin'; export { default as FilterPlugin } from './FilterPlugin'; export { default as FilterPluginConfig } from './FilterPluginConfig'; -export * from './GridPlugin'; +export { default as GridPanelPlugin } from './GridPanelPlugin'; +export { default as GridWidgetPlugin } from './GridWidgetPlugin'; export { default as GridPluginConfig } from './GridPluginConfig'; export { default as LinkerPlugin } from './LinkerPlugin'; export { default as LinkerPluginConfig } from './LinkerPluginConfig'; export { default as MarkdownPlugin } from './MarkdownPlugin'; export { default as MarkdownPluginConfig } from './MarkdownPluginConfig'; -export * from './PandasPlugin'; +export { default as PandasPanelPlugin } from './PandasPanelPlugin'; +export { default as PandasWidgetPlugin } from './PandasWidgetPlugin'; export { default as PandasPluginConfig } from './PandasPluginConfig'; export { default as WidgetLoaderPlugin } from './WidgetLoaderPlugin'; export { default as WidgetLoaderPluginConfig } from './WidgetLoaderPluginConfig'; From e613c5bc30c68974a3ea701f74b9482c3ca10b79 Mon Sep 17 00:00:00 2001 From: Matthew Runyon Date: Thu, 16 Nov 2023 15:07:30 -0600 Subject: [PATCH 4/5] Remove dependency used for development --- packages/app-utils/src/plugins/remote-component.config.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/app-utils/src/plugins/remote-component.config.ts b/packages/app-utils/src/plugins/remote-component.config.ts index ee96f29e5d..d1e0523bbb 100644 --- a/packages/app-utils/src/plugins/remote-component.config.ts +++ b/packages/app-utils/src/plugins/remote-component.config.ts @@ -20,7 +20,6 @@ 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'; -import * as DeephavenCorePlugins from '@deephaven/dashboard-core-plugins'; // eslint-disable-next-line import/prefer-default-export export const resolve = { @@ -41,5 +40,4 @@ export const resolve = { '@deephaven/log': DeephavenLog, '@deephaven/plugin': DeephavenPlugin, '@deephaven/react-hooks': DeephavenReactHooks, - '@deephaven/dashboard-core-plugins': DeephavenCorePlugins, }; From e58e5f04ce7d21ab2ce88f7a52b94df4690921b7 Mon Sep 17 00:00:00 2001 From: Matthew Runyon Date: Fri, 17 Nov 2023 11:50:22 -0600 Subject: [PATCH 5/5] Address review comments --- .../src/GridWidgetPlugin.tsx | 1 - .../src/PandasWidgetPlugin.tsx | 2 +- .../src/panels/PandasPanel.tsx | 48 +------------------ .../src/panels/PandasReloadButton.tsx | 42 ++++++++++++++++ 4 files changed, 45 insertions(+), 48 deletions(-) create mode 100644 packages/dashboard-core-plugins/src/panels/PandasReloadButton.tsx diff --git a/packages/dashboard-core-plugins/src/GridWidgetPlugin.tsx b/packages/dashboard-core-plugins/src/GridWidgetPlugin.tsx index f7de8e52b1..f53a920160 100644 --- a/packages/dashboard-core-plugins/src/GridWidgetPlugin.tsx +++ b/packages/dashboard-core-plugins/src/GridWidgetPlugin.tsx @@ -33,7 +33,6 @@ export function GridWidgetPlugin( }; }, [dh, fetch]); - // eslint-disable-next-line react/jsx-props-no-spreading return model ? : null; } diff --git a/packages/dashboard-core-plugins/src/PandasWidgetPlugin.tsx b/packages/dashboard-core-plugins/src/PandasWidgetPlugin.tsx index cde5b0d5a8..265a1b6dc2 100644 --- a/packages/dashboard-core-plugins/src/PandasWidgetPlugin.tsx +++ b/packages/dashboard-core-plugins/src/PandasWidgetPlugin.tsx @@ -7,7 +7,7 @@ import IrisGrid, { } from '@deephaven/iris-grid'; import { useApi } from '@deephaven/jsapi-bootstrap'; import { LoadingOverlay } from '@deephaven/components'; -import { PandasReloadButton } from './panels'; +import { PandasReloadButton } from './panels/PandasReloadButton'; export function PandasWidgetPlugin( props: WidgetComponentProps diff --git a/packages/dashboard-core-plugins/src/panels/PandasPanel.tsx b/packages/dashboard-core-plugins/src/panels/PandasPanel.tsx index dea00d0702..9ca0cd34a3 100644 --- a/packages/dashboard-core-plugins/src/panels/PandasPanel.tsx +++ b/packages/dashboard-core-plugins/src/panels/PandasPanel.tsx @@ -1,58 +1,14 @@ /* eslint-disable react/jsx-props-no-spreading */ /* eslint-disable react/no-unused-state */ -import React, { - Component, - ReactElement, - RefObject, - useCallback, - useRef, -} from 'react'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { dhRefresh } from '@deephaven/icons'; -import { Button } from '@deephaven/components'; +import React, { Component, ReactElement, RefObject } from 'react'; import ConnectedIrisGridPanel, { type IrisGridPanel, type OwnProps as IrisGridPanelOwnProps, type PanelState, } from './IrisGridPanel'; +import { PandasReloadButton } from './PandasReloadButton'; import './PandasPanel.scss'; -export function PandasReloadButton({ - onClick, -}: { - onClick: React.MouseEventHandler; -}): JSX.Element { - const buttonRef = useRef(null); - - const handleClick = useCallback( - (e: React.MouseEvent) => { - buttonRef.current?.blur(); - onClick(e); - }, - [onClick] - ); - - return ( - - ); -} - export interface PandasPanelProps extends IrisGridPanelOwnProps { panelState: PanelState | null; } diff --git a/packages/dashboard-core-plugins/src/panels/PandasReloadButton.tsx b/packages/dashboard-core-plugins/src/panels/PandasReloadButton.tsx new file mode 100644 index 0000000000..cb993a515d --- /dev/null +++ b/packages/dashboard-core-plugins/src/panels/PandasReloadButton.tsx @@ -0,0 +1,42 @@ +import React, { useCallback, useRef } from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { dhRefresh } from '@deephaven/icons'; +import { Button } from '@deephaven/components'; + +export function PandasReloadButton({ + onClick, +}: { + onClick: React.MouseEventHandler; +}): JSX.Element { + const buttonRef = useRef(null); + + const handleClick = useCallback( + (e: React.MouseEvent) => { + buttonRef.current?.blur(); + onClick(e); + }, + [onClick] + ); + + return ( + + ); +} + +export default PandasReloadButton;