Skip to content

Commit

Permalink
feat: Add DashboardPlugin support to embed-widget (#1950)
Browse files Browse the repository at this point in the history
Needed for deephaven/deephaven-plugins#215

Doesn't support multiple dashboards being opened in one embed, but some
of the groundwork is there and we don't support multiple dashboards from
a single widget anyway yet.
  • Loading branch information
mattrunyon authored Apr 24, 2024
1 parent 5770557 commit 27fc8bd
Show file tree
Hide file tree
Showing 19 changed files with 339 additions and 66 deletions.
30 changes: 22 additions & 8 deletions package-lock.json

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

2 changes: 2 additions & 0 deletions packages/app-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"@deephaven/console": "file:../console",
"@deephaven/dashboard": "file:../dashboard",
"@deephaven/dashboard-core-plugins": "file:../dashboard-core-plugins",
"@deephaven/golden-layout": "file:../golden-layout",
"@deephaven/icons": "file:../icons",
"@deephaven/iris-grid": "file:../iris-grid",
"@deephaven/jsapi-bootstrap": "file:../jsapi-bootstrap",
Expand All @@ -48,6 +49,7 @@
"@deephaven/utils": "file:../utils",
"@paciolan/remote-component": "2.13.0",
"@paciolan/remote-module-loader": "^3.0.2",
"classnames": "^2.5.1",
"fira": "mozilla/fira#4.202"
},
"peerDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,40 @@ import React, { useCallback } from 'react';
import classNames from 'classnames';
import {
DashboardUtils,
DEFAULT_DASHBOARD_ID,
DehydratedDashboardPanelProps,
LazyDashboard,
} from '@deephaven/dashboard';
import {
sanitizeVariableDescriptor,
useObjectFetcher,
} from '@deephaven/jsapi-bootstrap';
import LayoutManager, { ItemConfigType } from '@deephaven/golden-layout';
import LayoutManager, {
ItemConfigType,
Settings as LayoutSettings,
} from '@deephaven/golden-layout';
import { LoadingOverlay } from '@deephaven/components';
import EmptyDashboard from './EmptyDashboard';

interface AppDashboardsProps {
dashboards: {
id: string;
layoutConfig: ItemConfigType[];
layoutSettings?: Partial<LayoutSettings>;
key?: string;
}[];
activeDashboard: string;
onLayoutInitialized?: () => void;
onGoldenLayoutChange: (goldenLayout: LayoutManager) => void;
plugins: JSX.Element[];
onAutoFillClick: (event: React.MouseEvent) => void;
emptyDashboard?: JSX.Element;
}

export function AppDashboards({
dashboards,
activeDashboard,
onLayoutInitialized,
onGoldenLayoutChange,
plugins,
onAutoFillClick,
emptyDashboard = <LoadingOverlay />,
}: AppDashboardsProps): JSX.Element {
const fetchObject = useObjectFetcher();

Expand Down Expand Up @@ -68,14 +72,10 @@ export function AppDashboards({
id={d.id}
key={d.key}
isActive={d.id === activeDashboard}
emptyDashboard={
d.id === DEFAULT_DASHBOARD_ID ? (
<EmptyDashboard onAutoFillClick={onAutoFillClick} />
) : (
<LoadingOverlay />
)
}
emptyDashboard={emptyDashboard}
layoutConfig={d.layoutConfig}
layoutSettings={d.layoutSettings}
onLayoutInitialized={onLayoutInitialized}
onGoldenLayoutChange={onGoldenLayoutChange}
hydrate={hydratePanel}
plugins={plugins}
Expand Down
1 change: 1 addition & 0 deletions packages/app-utils/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './AppBootstrap';
export * from './AppDashboards';
export * from './AuthBootstrap';
export * from './ConnectionBootstrap';
export * from './ConnectionContext';
Expand Down
39 changes: 14 additions & 25 deletions packages/code-studio/src/main/AppMainContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
NavTabList,
type NavTabItem,
SlideTransition,
LoadingOverlay,
} from '@deephaven/components';
import { SHORTCUTS as IRIS_GRID_SHORTCUTS } from '@deephaven/iris-grid';
import {
Expand Down Expand Up @@ -88,13 +89,8 @@ import {
} from '@deephaven/utils';
import GoldenLayout from '@deephaven/golden-layout';
import type { ItemConfigType } from '@deephaven/golden-layout';
import {
type DashboardPlugin,
isDashboardPlugin,
type LegacyDashboardPlugin,
isLegacyDashboardPlugin,
type PluginModuleMap,
} from '@deephaven/plugin';
import { type PluginModuleMap, getDashboardPlugins } from '@deephaven/plugin';
import { AppDashboards } from '@deephaven/app-utils';
import JSZip from 'jszip';
import SettingsMenu from '../settings/SettingsMenu';
import AppControlsMenu from './AppControlsMenu';
Expand All @@ -103,8 +99,8 @@ import './AppMainContainer.scss';
import WidgetList, { WindowMouseEvent } from './WidgetList';
import UserLayoutUtils from './UserLayoutUtils';
import LayoutStorage from '../storage/LayoutStorage';
import AppDashboards from './AppDashboards';
import { getFormattedVersionInfo } from '../settings/SettingsUtils';
import EmptyDashboard from './EmptyDashboard';

const log = Log.module('AppMainContainer');

Expand Down Expand Up @@ -783,22 +779,9 @@ export class AppMainContainer extends Component<
});
}

getDashboardPlugins = memoize((plugins: PluginModuleMap) => {
const dashboardPlugins = [...plugins.entries()].filter(
([, plugin]) =>
isDashboardPlugin(plugin) || isLegacyDashboardPlugin(plugin)
) as [string, DashboardPlugin | LegacyDashboardPlugin][];

return dashboardPlugins.map(([name, plugin]) => {
if (isLegacyDashboardPlugin(plugin)) {
const { DashboardPlugin: DPlugin } = plugin;
return <DPlugin key={name} />;
}

const { component: DPlugin } = plugin;
return <DPlugin key={name} />;
});
});
getDashboardPlugins = memoize((plugins: PluginModuleMap) =>
getDashboardPlugins(plugins)
);

handleHomeClick(): void {
this.handleTabSelect(DEFAULT_DASHBOARD_ID);
Expand Down Expand Up @@ -995,7 +978,13 @@ export class AppMainContainer extends Component<
dashboards={this.getDashboards()}
activeDashboard={activeTabKey}
onGoldenLayoutChange={this.handleGoldenLayoutChange}
onAutoFillClick={this.handleAutoFillClick}
emptyDashboard={
activeTabKey === DEFAULT_DASHBOARD_ID ? (
<EmptyDashboard onAutoFillClick={this.handleAutoFillClick} />
) : (
<LoadingOverlay />
)
}
plugins={[
<ConsolePlugin
key="ConsolePlugin"
Expand Down
6 changes: 5 additions & 1 deletion packages/dashboard/src/DashboardEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ export function stopListenForCreateDashboard<T = unknown>(
eventHub: EventHub,
handler: (p: CreateDashboardPayload<T>) => void
): void {
eventHub.off(CREATE_DASHBOARD, handler);
try {
eventHub.off(CREATE_DASHBOARD, handler);
} catch {
// golden-layout throws if the handler is not found. Instead catch it and no-op
}
}

export function listenForCreateDashboard<T = unknown>(
Expand Down
12 changes: 7 additions & 5 deletions packages/dashboard/src/DashboardLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,6 @@ export function DashboardLayout({
layout.getReactChildren()
);

// Fire only once after the layout is mounted
// This should ensure DashboardPlugins have been mounted
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => onLayoutInitialized(), []);

const hydrateMap = useMemo(() => new Map(), []);
const dehydrateMap = useMemo(() => new Map(), []);
const registerComponent = useCallback(
Expand Down Expand Up @@ -320,6 +315,13 @@ export function DashboardLayout({
]
);

// This should be the last hook called in this component
// Ensures it runs after any other effects on mount
// Fire only once after the layout is mounted
// This should ensure DashboardPlugins have been mounted
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => onLayoutInitialized(), []);

return (
<>
{isDashboardEmpty && emptyDashboard}
Expand Down
7 changes: 6 additions & 1 deletion packages/embed-widget/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,21 @@
"dependencies": {
"@deephaven/app-utils": "file:../app-utils",
"@deephaven/components": "file:../components",
"@deephaven/dashboard": "file:../dashboard",
"@deephaven/dashboard-core-plugins": "file:../dashboard-core-plugins",
"@deephaven/jsapi-bootstrap": "file:../jsapi-bootstrap",
"@deephaven/jsapi-components": "file:../jsapi-components",
"@deephaven/jsapi-types": "1.0.0-dev0.33.1",
"@deephaven/jsapi-utils": "file:../jsapi-utils",
"@deephaven/log": "file:../log",
"@deephaven/plugin": "file:../plugin",
"@deephaven/redux": "file:../redux",
"@deephaven/utils": "file:../utils",
"fira": "mozilla/fira#4.202",
"react": "^17.0.2",
"react-dom": "^17.0.2"
"react-dom": "^17.0.2",
"react-redux": "^7.2.4",
"shortid": "^2.2.16"
},
"scripts": {
"analyze": "source-map-explorer build/assets/*.js --no-border-checks",
Expand Down
37 changes: 37 additions & 0 deletions packages/embed-widget/src/App.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,43 @@
left: 0;
right: 0;
bottom: 0;
height: 100vh;
width: 100vw;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
overflow: hidden;
}

$tab-height: 32px;
$tab-font-size: 1rem;

$tab-button-side-padding: 9px;

$tab-link-disabled-color: $gray-600;

$nav-space: 4px; // give a gap around some buttons for focus area that are in nav bar

.app-main-top-nav-menus {
display: flex;
width: 100%;
justify-content: space-between;
align-items: center;
}

.tab-content {
flex-grow: 1;
height: 100%;
}

.tab-pane {
height: 100%;
width: 100%;
flex-grow: 1;
}

.app-main-tabs {
&:focus {
outline: none;
}
}
Loading

0 comments on commit 27fc8bd

Please sign in to comment.