Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Convert DashboardPlugins to WidgetPlugins #1598

Merged
merged 5 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 33 additions & 40 deletions packages/dashboard-core-plugins/src/ChartPlugin.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
import { useCallback } from 'react';
import {
assertIsDashboardPluginProps,
DashboardPluginComponentProps,
DehydratedDashboardPanelProps,
useDashboardPanel,
} from '@deephaven/dashboard';
import { forwardRef, useMemo } from 'react';
import { useApi } from '@deephaven/jsapi-bootstrap';
import { useConnection } from '@deephaven/jsapi-components';
import { ChartModel, ChartModelFactory } from '@deephaven/chart';
import type { dh as DhType, IdeConnection } from '@deephaven/jsapi-types';
import { IrisGridUtils } from '@deephaven/iris-grid';
import { getTimeZone, store } from '@deephaven/redux';
import { type WidgetComponentProps } from '@deephaven/plugin';
import {
ChartPanel,
ChartPanelMetadata,
GLChartPanelState,
isChartPanelDehydratedProps,
isChartPanelTableMetadata,
} from './panels';
import ConnectedChartPanel, {
type ChartPanel,
type ChartPanelProps,
} from './panels/ChartPanel';

async function createChartModel(
dh: DhType,
Expand Down Expand Up @@ -86,41 +84,36 @@ async function createChartModel(
return ChartModelFactory.makeModelFromSettings(dh, settings as any, table);
}

export function ChartPlugin(
props: DashboardPluginComponentProps
): JSX.Element | null {
assertIsDashboardPluginProps(props);
const dh = useApi();
const connection = useConnection();
export const ChartPlugin = forwardRef(
(props: WidgetComponentProps, ref: React.Ref<ChartPanel>) => {
const dh = useApi();
const connection = useConnection();

const hydrate = useCallback(
(hydrateProps: DehydratedDashboardPanelProps, id: string) => ({
...hydrateProps,
localDashboardId: id,
makeModel: () => {
const { metadata } = hydrateProps;
const panelState = isChartPanelDehydratedProps(hydrateProps)
? hydrateProps.panelState
: undefined;
if (metadata == null) {
throw new Error('Metadata is required for chart panel');
}
const hydratedProps = useMemo(
() => ({
...props,
metadata: props.metadata as ChartPanelMetadata,
localDashboardId: props.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');
}

return createChartModel(dh, connection, metadata, panelState);
},
}),
[dh, connection]
);
return createChartModel(dh, connection, metadata, panelState);
},
}),
[dh, connection, props]
);

useDashboardPanel({
dashboardProps: props,
componentName: ChartPanel.COMPONENT,
component: ChartPanel,
supportedTypes: dh.VariableType.FIGURE,
hydrate,
});
// eslint-disable-next-line react/jsx-props-no-spreading
return <ConnectedChartPanel ref={ref} {...hydratedProps} />;
}
);

return null;
}
ChartPlugin.displayName = 'ChartPlugin';

export default ChartPlugin;
13 changes: 9 additions & 4 deletions packages/dashboard-core-plugins/src/ChartPluginConfig.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { PluginType, DashboardPlugin } from '@deephaven/plugin';
import { PluginType, type WidgetPlugin } from '@deephaven/plugin';
import { vsGraph } from '@deephaven/icons';
import ChartPlugin from './ChartPlugin';

const ChartPluginConfig: DashboardPlugin = {
name: 'ChartPlugin',
type: PluginType.DASHBOARD_PLUGIN,
const ChartPluginConfig: WidgetPlugin = {
name: 'ChartPanel',
title: 'Chart',
type: PluginType.WIDGET_PLUGIN,
component: ChartPlugin,
panelComponent: ChartPlugin,
supportedTypes: 'Figure',
icon: vsGraph,
mofojed marked this conversation as resolved.
Show resolved Hide resolved
};

export default ChartPluginConfig;
59 changes: 27 additions & 32 deletions packages/dashboard-core-plugins/src/GridPlugin.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,33 @@
import { useMemo } from 'react';
import {
assertIsDashboardPluginProps,
DashboardPluginComponentProps,
useDashboardPanel,
} from '@deephaven/dashboard';
import { useApi } from '@deephaven/jsapi-bootstrap';
import { IrisGridPanel } from './panels';
import { forwardRef, useMemo } from 'react';
import { type WidgetComponentProps } from '@deephaven/plugin';
import { type DashboardPanelProps } from '@deephaven/dashboard';
import useHydrateGrid from './useHydrateGrid';
import ConnectedIrisGridPanel, {
IrisGridPanelProps,
type IrisGridPanel,
} from './panels/IrisGridPanel';

export function GridPlugin(
props: DashboardPluginComponentProps
): JSX.Element | null {
assertIsDashboardPluginProps(props);
const dh = useApi();
const hydrate = useHydrateGrid();
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 supportedTypes = useMemo(
() => [
dh.VariableType.TABLE,
dh.VariableType.TREETABLE,
dh.VariableType.HIERARCHICALTABLE,
],
[dh]
);
// eslint-disable-next-line react/jsx-props-no-spreading
return <ConnectedIrisGridPanel ref={ref} {...hydratedProps} />;
}
);

useDashboardPanel({
dashboardProps: props,
componentName: IrisGridPanel.COMPONENT,
component: IrisGridPanel,
supportedTypes,
hydrate,
});

return null;
}
GridPlugin.displayName = 'GridPlugin';

export default GridPlugin;
13 changes: 9 additions & 4 deletions packages/dashboard-core-plugins/src/GridPluginConfig.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { PluginType, DashboardPlugin } from '@deephaven/plugin';
import { PluginType, type WidgetPlugin } from '@deephaven/plugin';
import { dhTable } from '@deephaven/icons';
import GridPlugin from './GridPlugin';

const GridPluginConfig: DashboardPlugin = {
name: 'GridPlugin',
type: PluginType.DASHBOARD_PLUGIN,
const GridPluginConfig: WidgetPlugin = {
name: 'IrisGridPanel',
title: 'Table',
type: PluginType.WIDGET_PLUGIN,
component: GridPlugin,
panelComponent: GridPlugin,
supportedTypes: ['Table', 'TreeTable', 'HierarchicalTable'],
icon: dhTable,
};

export default GridPluginConfig;
1 change: 0 additions & 1 deletion packages/dashboard-core-plugins/src/WidgetLoaderPlugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ export function WrapWidgetPlugin(
{...props}
/>
)}
)
mattrunyon marked this conversation as resolved.
Show resolved Hide resolved
</WidgetPanel>
);
}
Expand Down
4 changes: 0 additions & 4 deletions packages/dashboard-core-plugins/src/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ import { type IdeConnection } from '@deephaven/jsapi-types';
import { ConnectionContext } from '@deephaven/jsapi-components';
import { PluginsContext } from '@deephaven/plugin';
import {
ChartPlugin,
ConsolePlugin,
FilterPlugin,
GridPlugin,
LinkerPlugin,
MarkdownPlugin,
WidgetLoaderPlugin,
Expand All @@ -36,8 +34,6 @@ it('handles mounting and unmount core plugins properly', () => {
<Provider store={store}>
<Dashboard>
<FilterPlugin />
<GridPlugin hydrate={() => undefined} />
<ChartPlugin hydrate={() => undefined} />
<ConsolePlugin />
<LinkerPlugin />
<MarkdownPlugin />
Expand Down
44 changes: 22 additions & 22 deletions packages/dashboard-core-plugins/src/panels/ChartPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,18 @@ export interface ChartPanelProps extends DashboardPanelProps {
metadata: ChartPanelMetadata;
/** Function to build the ChartModel used by this ChartPanel. Can return a promise. */
makeModel: () => Promise<ChartModel>;
localDashboardId: string;
Plotly?: typeof PlotlyType;
/** The panel container div */
containerRef?: RefObject<HTMLDivElement>;

panelState: GLChartPanelState;
settings: Partial<WorkspaceSettings>;
}

interface PropsFromRedux {
mattrunyon marked this conversation as resolved.
Show resolved Hide resolved
inputFilters: InputFilter[];
links: Link[];
localDashboardId: string;
isLinkerActive: boolean;
source?: TableTemplate;
sourcePanel?: PanelComponent;
Expand All @@ -134,12 +143,6 @@ export interface ChartPanelProps extends DashboardPanelProps {
id: string,
secondParam: undefined
) => void;
Plotly?: typeof PlotlyType;
/** The panel container div */
containerRef?: RefObject<HTMLDivElement>;

panelState: GLChartPanelState;
settings: Partial<WorkspaceSettings>;
}

interface ChartPanelState {
Expand Down Expand Up @@ -178,7 +181,14 @@ function hasPanelState(
return (panel as { panelState: IrisGridPanelState }).panelState != null;
}

export class ChartPanel extends Component<ChartPanelProps, ChartPanelState> {
type ChartPanelPropsWithRedux = ChartPanelProps &
PropsFromRedux &
React.RefAttributes<ChartPanel>;

export class ChartPanel extends Component<
ChartPanelPropsWithRedux,
ChartPanelState
> {
static defaultProps = {
columnSelectionValidator: null,
isLinkerActive: false,
Expand All @@ -193,7 +203,7 @@ export class ChartPanel extends Component<ChartPanelProps, ChartPanelState> {

static COMPONENT = 'ChartPanel';

constructor(props: ChartPanelProps) {
constructor(props: ChartPanelPropsWithRedux) {
super(props);

this.handleColumnSelected = this.handleColumnSelected.bind(this);
Expand Down Expand Up @@ -263,7 +273,7 @@ export class ChartPanel extends Component<ChartPanelProps, ChartPanelState> {
}

componentDidUpdate(
prevProps: ChartPanelProps,
prevProps: ChartPanelPropsWithRedux,
prevState: ChartPanelState
): void {
const { inputFilters, source } = this.props;
Expand Down Expand Up @@ -1046,7 +1056,7 @@ export class ChartPanel extends Component<ChartPanelProps, ChartPanelState> {
if (isChartPanelTableMetadata(metadata)) {
name = metadata.table;
} else {
name = metadata.figure;
name = metadata.name ?? metadata.figure;
}
const inputFilterMap = this.getInputFilterColumnMap(
columnMap,
Expand Down Expand Up @@ -1160,17 +1170,7 @@ export class ChartPanel extends Component<ChartPanelProps, ChartPanelState> {
const mapStateToProps = (
state: RootState,
ownProps: { localDashboardId: string; metadata: { sourcePanelId?: string } }
): Omit<
ChartPanelProps,
| 'glContainer'
| 'glEventHub'
| 'localDashboardId'
| 'makeModel'
| 'metadata'
| 'panelState'
| 'setActiveTool'
| 'setDashboardIsolatedLinkerPanelId'
> => {
) => {
const { localDashboardId, metadata } = ownProps;

let sourcePanelId;
Expand Down
17 changes: 15 additions & 2 deletions packages/dashboard-core-plugins/src/panels/Panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ interface PanelProps {
interface PanelState {
title?: string | null;
showRenameDialog: boolean;
isWithinPanel: boolean;
}
/**
* Generic panel component that emits mount/unmount/focus events.
Expand Down Expand Up @@ -118,10 +119,13 @@ class Panel extends PureComponent<PanelProps, PanelState> {
this.handleTabClicked = this.handleTabClicked.bind(this);
this.handleTab = this.handleTab.bind(this);

this.ref = React.createRef<HTMLDivElement>();

const { glContainer } = this.props;
this.state = {
title: LayoutUtils.getTitleFromContainer(glContainer),
showRenameDialog: false,
isWithinPanel: false,
mattrunyon marked this conversation as resolved.
Show resolved Hide resolved
};
}

Expand Down Expand Up @@ -151,6 +155,11 @@ class Panel extends PureComponent<PanelProps, PanelState> {
if (prevProps.componentPanel == null && componentPanel != null) {
glEventHub.emit(PanelEvent.MOUNT, componentPanel);
}

this.setState({
mattrunyon marked this conversation as resolved.
Show resolved Hide resolved
isWithinPanel:
this.ref.current?.parentElement?.closest('.iris-panel') != null,
mattrunyon marked this conversation as resolved.
Show resolved Hide resolved
});
}

componentWillUnmount(): void {
Expand All @@ -176,6 +185,8 @@ class Panel extends PureComponent<PanelProps, PanelState> {
}
}

ref: React.RefObject<HTMLDivElement>;

handleTab(tab: Tab): void {
if (tab != null) {
this.setState({
Expand Down Expand Up @@ -335,21 +346,23 @@ class Panel extends PureComponent<PanelProps, PanelState> {
isRenamable,
} = this.props;
const { tab: glTab } = glContainer;
const { showRenameDialog, title } = this.state;
const { showRenameDialog, title, isWithinPanel } = this.state;

return (
<div
className={classNames('h-100 w-100 iris-panel', className)}
onFocusCapture={this.handleFocus}
onBlurCapture={this.handleBlur}
ref={this.ref}
>
{children}
<LoadingOverlay
errorMessage={errorMessage}
isLoaded={isLoaded}
isLoading={isLoading}
/>
{glTab != null &&
{!isWithinPanel &&
glTab != null &&
mattrunyon marked this conversation as resolved.
Show resolved Hide resolved
ReactDOM.createPortal(
<>
<PanelContextMenu
Expand Down
Loading
Loading