Skip to content

Commit 94cc82c

Browse files
authored
feat: Widget plugins (#1564)
Fixes #1455 Fixes #1167 by allowing widget plugins to advertise their supported types
1 parent 876a6ac commit 94cc82c

33 files changed

Lines changed: 953 additions & 114 deletions

package-lock.json

Lines changed: 45 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
"@types/react": "^17.0.2",
9797
"@types/react-beautiful-dnd": "^13.1.2",
9898
"@types/react-dom": "^17.0.9",
99+
"@types/react-is": "^17.0.2",
99100
"@types/react-plotly.js": "^2.6.0",
100101
"@types/react-router-dom": "^5.1.2",
101102
"@types/react-test-renderer": "^17.0.1",

packages/app-utils/src/components/AppBootstrap.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
RefreshTokenBootstrap,
66
useBroadcastLoginListener,
77
} from '@deephaven/jsapi-components';
8-
import { type DashboardPlugin } from '@deephaven/plugin';
8+
import { type Plugin } from '@deephaven/plugin';
99
import FontBootstrap from './FontBootstrap';
1010
import PluginsBootstrap from './PluginsBootstrap';
1111
import AuthBootstrap from './AuthBootstrap';
@@ -24,7 +24,7 @@ export type AppBootstrapProps = {
2424
pluginsUrl: string;
2525

2626
/** The core plugins to load. */
27-
getCorePlugins?: () => Promise<DashboardPlugin[]>;
27+
getCorePlugins?: () => Promise<Plugin[]>;
2828

2929
/** Font class names to load. */
3030
fontClassNames?: string[];

packages/app-utils/src/components/PluginsBootstrap.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type DashboardPlugin } from '@deephaven/plugin';
1+
import { type Plugin } from '@deephaven/plugin';
22
import React, { createContext, useEffect, useState } from 'react';
33
import { PluginModuleMap, loadModulePlugins } from '../plugins';
44

@@ -11,7 +11,7 @@ export type PluginsBootstrapProps = {
1111
pluginsUrl: string;
1212

1313
/** The core plugins to load. */
14-
getCorePlugins?: () => Promise<DashboardPlugin[]>;
14+
getCorePlugins?: () => Promise<Plugin[]>;
1515

1616
/**
1717
* The children to render wrapped with the PluginsContext.

packages/code-studio/src/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ async function getCorePlugins() {
4242
FilterPluginConfig,
4343
MarkdownPluginConfig,
4444
LinkerPluginConfig,
45+
WidgetLoaderPluginConfig,
4546
} = dashboardCorePlugins;
4647
return [
4748
GridPluginConfig,
@@ -51,6 +52,7 @@ async function getCorePlugins() {
5152
FilterPluginConfig,
5253
MarkdownPluginConfig,
5354
LinkerPluginConfig,
55+
WidgetLoaderPluginConfig,
5456
];
5557
}
5658

packages/console/src/Console.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ interface ConsoleProps {
7979
* (file:File) => Promise<File[]>
8080
*/
8181
unzip: (file: File) => Promise<JSZipObject[]>;
82+
supportsType(type: string): boolean;
83+
iconForType(type: string): ReactElement;
8284
}
8385

8486
interface ConsoleState {
@@ -105,6 +107,16 @@ interface ConsoleState {
105107
isPrintStdOutEnabled: boolean;
106108
isClosePanelsOnDisconnectEnabled: boolean;
107109
}
110+
111+
function defaultSupportsType(): boolean {
112+
return true;
113+
}
114+
115+
function defaultIconForType(type: string): ReactElement {
116+
// eslint-disable-next-line react/jsx-no-useless-fragment
117+
return <></>;
118+
}
119+
108120
export class Console extends PureComponent<ConsoleProps, ConsoleState> {
109121
static defaultProps = {
110122
statusBarChildren: null,
@@ -117,6 +129,8 @@ export class Console extends PureComponent<ConsoleProps, ConsoleState> {
117129
objectMap: new Map(),
118130
disabled: false,
119131
unzip: null,
132+
supportsType: defaultSupportsType,
133+
iconForType: defaultIconForType,
120134
};
121135

122136
static LOG_THROTTLE = 500;
@@ -951,6 +965,8 @@ export class Console extends PureComponent<ConsoleProps, ConsoleState> {
951965
timeZone,
952966
disabled,
953967
unzip,
968+
supportsType,
969+
iconForType,
954970
} = this.props;
955971
const {
956972
consoleHeight,
@@ -1013,6 +1029,8 @@ export class Console extends PureComponent<ConsoleProps, ConsoleState> {
10131029
openObject={openObject}
10141030
language={language}
10151031
disabled={disabled}
1032+
supportsType={supportsType}
1033+
iconForType={iconForType}
10161034
/>
10171035
{historyChildren}
10181036
</div>
Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* Console display for use in the Iris environment.
33
*/
4-
import React, { Component, ReactElement } from 'react';
4+
import { type ReactElement } from 'react';
55
import type { VariableDefinition } from '@deephaven/jsapi-types';
66
import ConsoleHistoryItem from './ConsoleHistoryItem';
77

@@ -13,43 +13,45 @@ interface ConsoleHistoryProps {
1313
language: string;
1414
openObject: (object: VariableDefinition) => void;
1515
disabled?: boolean;
16+
supportsType(type: string): boolean;
17+
iconForType(type: string): ReactElement;
1618
}
1719

18-
class ConsoleHistory extends Component<
19-
ConsoleHistoryProps,
20-
Record<string, never>
21-
> {
22-
static defaultProps = {
23-
disabled: false,
24-
};
25-
26-
static itemKey(i: number, item: ConsoleHistoryActionItem): string {
27-
return `${i}.${item.command}.${item.result && item.result.message}.${
28-
item.result && item.result.error
29-
}`;
30-
}
31-
32-
render(): ReactElement {
33-
const { disabled, items, language, openObject } = this.props;
34-
const historyElements = [];
35-
for (let i = 0; i < items.length; i += 1) {
36-
const item = items[i];
37-
const historyElement = (
38-
<ConsoleHistoryItem
39-
key={ConsoleHistory.itemKey(i, item)}
40-
disabled={disabled}
41-
item={item}
42-
openObject={openObject}
43-
language={language}
44-
/>
45-
);
46-
historyElements.push(historyElement);
47-
}
20+
function itemKey(i: number, item: ConsoleHistoryActionItem): string {
21+
return `${i}.${item.command}.${item.result && item.result.message}.${
22+
item.result && item.result.error
23+
}`;
24+
}
4825

49-
return (
50-
<div className="container-fluid console-history">{historyElements}</div>
26+
function ConsoleHistory(props: ConsoleHistoryProps): ReactElement {
27+
const {
28+
disabled = false,
29+
items,
30+
language,
31+
openObject,
32+
supportsType,
33+
iconForType,
34+
} = props;
35+
const historyElements = [];
36+
for (let i = 0; i < items.length; i += 1) {
37+
const item = items[i];
38+
const historyElement = (
39+
<ConsoleHistoryItem
40+
key={itemKey(i, item)}
41+
disabled={disabled}
42+
item={item}
43+
openObject={openObject}
44+
language={language}
45+
supportsType={supportsType}
46+
iconForType={iconForType}
47+
/>
5148
);
49+
historyElements.push(historyElement);
5250
}
51+
52+
return (
53+
<div className="container-fluid console-history">{historyElements}</div>
54+
);
5355
}
5456

5557
export default ConsoleHistory;

packages/console/src/console-history/ConsoleHistoryItem.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Button } from '@deephaven/components';
66
import Log from '@deephaven/log';
77
import type { VariableDefinition } from '@deephaven/jsapi-types';
88
import classNames from 'classnames';
9-
import { Code, ObjectIcon } from '../common';
9+
import { Code } from '../common';
1010
import ConsoleHistoryItemResult from './ConsoleHistoryItemResult';
1111
import ConsoleHistoryResultInProgress from './ConsoleHistoryResultInProgress';
1212
import ConsoleHistoryResultErrorMessage from './ConsoleHistoryResultErrorMessage';
@@ -20,6 +20,10 @@ interface ConsoleHistoryItemProps {
2020
language: string;
2121
openObject: (object: VariableDefinition) => void;
2222
disabled?: boolean;
23+
// TODO: #1573 Remove this eslint disable
24+
// eslint-disable-next-line react/no-unused-prop-types
25+
supportsType: (type: string) => boolean;
26+
iconForType: (type: string) => ReactElement;
2327
}
2428

2529
class ConsoleHistoryItem extends PureComponent<
@@ -53,7 +57,7 @@ class ConsoleHistoryItem extends PureComponent<
5357
}
5458

5559
render(): ReactElement {
56-
const { disabled, item, language } = this.props;
60+
const { disabled, item, language, iconForType } = this.props;
5761
const { disabledObjects, result } = item;
5862
const hasCommand = item.command != null && item.command !== '';
5963

@@ -77,6 +81,9 @@ class ConsoleHistoryItem extends PureComponent<
7781

7882
if (changes) {
7983
const { created, updated } = changes;
84+
// TODO: #1573 filter for supported types or change button kind
85+
// based on if type is supported. Possibly a warn state for widgets
86+
// that the UI doesn't have anything registered to support.
8087
[...created, ...updated].forEach(object => {
8188
hasButtons = true;
8289
const { title } = object;
@@ -92,7 +99,7 @@ class ConsoleHistoryItem extends PureComponent<
9299
onClick={() => this.handleObjectClick(object)}
93100
className="btn-console-object"
94101
disabled={btnDisabled}
95-
icon={<ObjectIcon type={object.type} />}
102+
icon={iconForType(object.type)}
96103
>
97104
{title}
98105
</Button>
Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,23 @@
1-
import {
2-
assertIsDashboardPluginProps,
3-
DashboardPluginComponentProps,
4-
useDashboardPanel,
5-
} from '@deephaven/dashboard';
6-
import { useApi } from '@deephaven/jsapi-bootstrap';
1+
import { DashboardPanelProps } from '@deephaven/dashboard';
2+
import { WidgetComponentProps } from '@deephaven/plugin';
3+
import { forwardRef, useMemo } from 'react';
74
import { PandasPanel } from './panels';
85
import useHydrateGrid from './useHydrateGrid';
96

10-
export function PandasPlugin(
11-
props: DashboardPluginComponentProps
12-
): JSX.Element | null {
13-
assertIsDashboardPluginProps(props);
14-
const dh = useApi();
15-
const hydrate = useHydrateGrid();
7+
export const PandasPlugin = forwardRef(
8+
(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]
14+
);
1615

17-
useDashboardPanel({
18-
dashboardProps: props,
19-
componentName: PandasPanel.COMPONENT,
20-
component: PandasPanel,
21-
supportedTypes: dh.VariableType.PANDAS,
22-
hydrate,
23-
});
16+
// eslint-disable-next-line react/jsx-props-no-spreading
17+
return <PandasPanel ref={ref} {...hydratedProps} />;
18+
}
19+
);
2420

25-
return null;
26-
}
21+
PandasPlugin.displayName = 'PandasPlugin';
2722

2823
export default PandasPlugin;

0 commit comments

Comments
 (0)