Skip to content

Commit

Permalink
feat: Widget plugins (#1564)
Browse files Browse the repository at this point in the history
Fixes #1455
Fixes #1167 by allowing widget plugins to advertise their supported
types
  • Loading branch information
mattrunyon authored Oct 19, 2023
1 parent 876a6ac commit 94cc82c
Show file tree
Hide file tree
Showing 33 changed files with 953 additions and 114 deletions.
47 changes: 45 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
"@types/react": "^17.0.2",
"@types/react-beautiful-dnd": "^13.1.2",
"@types/react-dom": "^17.0.9",
"@types/react-is": "^17.0.2",
"@types/react-plotly.js": "^2.6.0",
"@types/react-router-dom": "^5.1.2",
"@types/react-test-renderer": "^17.0.1",
Expand Down
4 changes: 2 additions & 2 deletions packages/app-utils/src/components/AppBootstrap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
RefreshTokenBootstrap,
useBroadcastLoginListener,
} from '@deephaven/jsapi-components';
import { type DashboardPlugin } from '@deephaven/plugin';
import { type Plugin } from '@deephaven/plugin';
import FontBootstrap from './FontBootstrap';
import PluginsBootstrap from './PluginsBootstrap';
import AuthBootstrap from './AuthBootstrap';
Expand All @@ -24,7 +24,7 @@ export type AppBootstrapProps = {
pluginsUrl: string;

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

/** Font class names to load. */
fontClassNames?: string[];
Expand Down
4 changes: 2 additions & 2 deletions packages/app-utils/src/components/PluginsBootstrap.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type DashboardPlugin } from '@deephaven/plugin';
import { type Plugin } from '@deephaven/plugin';
import React, { createContext, useEffect, useState } from 'react';
import { PluginModuleMap, loadModulePlugins } from '../plugins';

Expand All @@ -11,7 +11,7 @@ export type PluginsBootstrapProps = {
pluginsUrl: string;

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

/**
* The children to render wrapped with the PluginsContext.
Expand Down
2 changes: 2 additions & 0 deletions packages/code-studio/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ async function getCorePlugins() {
FilterPluginConfig,
MarkdownPluginConfig,
LinkerPluginConfig,
WidgetLoaderPluginConfig,
} = dashboardCorePlugins;
return [
GridPluginConfig,
Expand All @@ -51,6 +52,7 @@ async function getCorePlugins() {
FilterPluginConfig,
MarkdownPluginConfig,
LinkerPluginConfig,
WidgetLoaderPluginConfig,
];
}

Expand Down
18 changes: 18 additions & 0 deletions packages/console/src/Console.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ interface ConsoleProps {
* (file:File) => Promise<File[]>
*/
unzip: (file: File) => Promise<JSZipObject[]>;
supportsType(type: string): boolean;
iconForType(type: string): ReactElement;
}

interface ConsoleState {
Expand All @@ -105,6 +107,16 @@ interface ConsoleState {
isPrintStdOutEnabled: boolean;
isClosePanelsOnDisconnectEnabled: boolean;
}

function defaultSupportsType(): boolean {
return true;
}

function defaultIconForType(type: string): ReactElement {
// eslint-disable-next-line react/jsx-no-useless-fragment
return <></>;

This comment has been minimized.

Copy link
@bmingles

bmingles Oct 26, 2023

Contributor

@mattrunyon Any reason not to return <ObjectIcon type={type} /> here?

This comment has been minimized.

Copy link
@mattrunyon

mattrunyon Oct 26, 2023

Author Collaborator

I think I initially wanted to remove ObjectIcon in the future since it wouldn't be used in DHC at least. Does DHE use this Console component?

This comment has been minimized.

Copy link
@bmingles

bmingles Oct 26, 2023

Contributor

Yes. I ended up adding to DHE in order to do the version bump, but it would probably be safer to have it as the default in DHC.

https://github.com/deephaven-ent/iris/pull/967/files#diff-d7a31b4b499d1d929d4e1865cdcfc899912c14a53a788ad420064e4fe8f34166R40-R42

}

export class Console extends PureComponent<ConsoleProps, ConsoleState> {
static defaultProps = {
statusBarChildren: null,
Expand All @@ -117,6 +129,8 @@ export class Console extends PureComponent<ConsoleProps, ConsoleState> {
objectMap: new Map(),
disabled: false,
unzip: null,
supportsType: defaultSupportsType,
iconForType: defaultIconForType,
};

static LOG_THROTTLE = 500;
Expand Down Expand Up @@ -951,6 +965,8 @@ export class Console extends PureComponent<ConsoleProps, ConsoleState> {
timeZone,
disabled,
unzip,
supportsType,
iconForType,
} = this.props;
const {
consoleHeight,
Expand Down Expand Up @@ -1013,6 +1029,8 @@ export class Console extends PureComponent<ConsoleProps, ConsoleState> {
openObject={openObject}
language={language}
disabled={disabled}
supportsType={supportsType}
iconForType={iconForType}
/>
{historyChildren}
</div>
Expand Down
68 changes: 35 additions & 33 deletions packages/console/src/console-history/ConsoleHistory.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Console display for use in the Iris environment.
*/
import React, { Component, ReactElement } from 'react';
import { type ReactElement } from 'react';
import type { VariableDefinition } from '@deephaven/jsapi-types';
import ConsoleHistoryItem from './ConsoleHistoryItem';

Expand All @@ -13,43 +13,45 @@ interface ConsoleHistoryProps {
language: string;
openObject: (object: VariableDefinition) => void;
disabled?: boolean;
supportsType(type: string): boolean;
iconForType(type: string): ReactElement;
}

class ConsoleHistory extends Component<
ConsoleHistoryProps,
Record<string, never>
> {
static defaultProps = {
disabled: false,
};

static itemKey(i: number, item: ConsoleHistoryActionItem): string {
return `${i}.${item.command}.${item.result && item.result.message}.${
item.result && item.result.error
}`;
}

render(): ReactElement {
const { disabled, items, language, openObject } = this.props;
const historyElements = [];
for (let i = 0; i < items.length; i += 1) {
const item = items[i];
const historyElement = (
<ConsoleHistoryItem
key={ConsoleHistory.itemKey(i, item)}
disabled={disabled}
item={item}
openObject={openObject}
language={language}
/>
);
historyElements.push(historyElement);
}
function itemKey(i: number, item: ConsoleHistoryActionItem): string {
return `${i}.${item.command}.${item.result && item.result.message}.${
item.result && item.result.error
}`;
}

return (
<div className="container-fluid console-history">{historyElements}</div>
function ConsoleHistory(props: ConsoleHistoryProps): ReactElement {
const {
disabled = false,
items,
language,
openObject,
supportsType,
iconForType,
} = props;
const historyElements = [];
for (let i = 0; i < items.length; i += 1) {
const item = items[i];
const historyElement = (
<ConsoleHistoryItem
key={itemKey(i, item)}
disabled={disabled}
item={item}
openObject={openObject}
language={language}
supportsType={supportsType}
iconForType={iconForType}
/>
);
historyElements.push(historyElement);
}

return (
<div className="container-fluid console-history">{historyElements}</div>
);
}

export default ConsoleHistory;
13 changes: 10 additions & 3 deletions packages/console/src/console-history/ConsoleHistoryItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Button } from '@deephaven/components';
import Log from '@deephaven/log';
import type { VariableDefinition } from '@deephaven/jsapi-types';
import classNames from 'classnames';
import { Code, ObjectIcon } from '../common';
import { Code } from '../common';
import ConsoleHistoryItemResult from './ConsoleHistoryItemResult';
import ConsoleHistoryResultInProgress from './ConsoleHistoryResultInProgress';
import ConsoleHistoryResultErrorMessage from './ConsoleHistoryResultErrorMessage';
Expand All @@ -20,6 +20,10 @@ interface ConsoleHistoryItemProps {
language: string;
openObject: (object: VariableDefinition) => void;
disabled?: boolean;
// TODO: #1573 Remove this eslint disable
// eslint-disable-next-line react/no-unused-prop-types
supportsType: (type: string) => boolean;
iconForType: (type: string) => ReactElement;
}

class ConsoleHistoryItem extends PureComponent<
Expand Down Expand Up @@ -53,7 +57,7 @@ class ConsoleHistoryItem extends PureComponent<
}

render(): ReactElement {
const { disabled, item, language } = this.props;
const { disabled, item, language, iconForType } = this.props;
const { disabledObjects, result } = item;
const hasCommand = item.command != null && item.command !== '';

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

if (changes) {
const { created, updated } = changes;
// TODO: #1573 filter for supported types or change button kind
// based on if type is supported. Possibly a warn state for widgets
// that the UI doesn't have anything registered to support.
[...created, ...updated].forEach(object => {
hasButtons = true;
const { title } = object;
Expand All @@ -92,7 +99,7 @@ class ConsoleHistoryItem extends PureComponent<
onClick={() => this.handleObjectClick(object)}
className="btn-console-object"
disabled={btnDisabled}
icon={<ObjectIcon type={object.type} />}
icon={iconForType(object.type)}
>
{title}
</Button>
Expand Down
37 changes: 16 additions & 21 deletions packages/dashboard-core-plugins/src/PandasPlugin.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,23 @@
import {
assertIsDashboardPluginProps,
DashboardPluginComponentProps,
useDashboardPanel,
} from '@deephaven/dashboard';
import { useApi } from '@deephaven/jsapi-bootstrap';
import { DashboardPanelProps } from '@deephaven/dashboard';
import { WidgetComponentProps } from '@deephaven/plugin';
import { forwardRef, useMemo } from 'react';
import { PandasPanel } from './panels';
import useHydrateGrid from './useHydrateGrid';

export function PandasPlugin(
props: DashboardPluginComponentProps
): JSX.Element | null {
assertIsDashboardPluginProps(props);
const dh = useApi();
const hydrate = useHydrateGrid();
export const PandasPlugin = forwardRef(
(props: WidgetComponentProps, ref: React.Ref<PandasPanel>) => {
const hydrate = useHydrateGrid<DashboardPanelProps>();
const { localDashboardId } = props;
const hydratedProps = useMemo(
() => hydrate(props, localDashboardId),
[hydrate, props, localDashboardId]
);

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

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

export default PandasPlugin;
Loading

0 comments on commit 94cc82c

Please sign in to comment.