Skip to content

Commit

Permalink
feat: Use useWidget hook to load widgets (#502)
Browse files Browse the repository at this point in the history
- Needs deephaven/web-client-ui#2030
- Uses the `useWidget` hook defined in
deephaven/web-client-ui#2030 to load widgets
- Allows for the errors/fetch implementation to be defined at the top
level
- Needed for DH-16737
- Tested in Core with a bunch of widgets, works as expected
- Unit tests added
  • Loading branch information
mofojed committed Jun 14, 2024
1 parent e4b7137 commit d9d1e5e
Show file tree
Hide file tree
Showing 13 changed files with 3,439 additions and 12,765 deletions.
15,732 changes: 3,189 additions & 12,543 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions plugins/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@
"plugins": [
{
"name": "matplotlib",
"version": "0.1.0",
"version": "0.0.0",
"main": "src/js/dist/index.js"
},
{
"name": "plotly-express",
"version": "0.1.0",
"version": "0.0.0",
"main": "src/js/dist/bundle/index.js"
},
{
"name": "auth-keycloak",
"version": "0.1.0",
"version": "0.0.0",
"main": "src/js/dist/index.js"
},
{ "name": "ui", "version": "0.1.0", "main": "src/js/dist/index.js" },
{ "name": "ui", "version": "0.0.0", "main": "src/js/dist/index.js" },
{
"name": "example-theme",
"version": "0.1.0",
"version": "0.0.0",
"main": "src/js/dist/index.js"
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ def _send_document_error(self, error: Exception, stack_trace: str) -> None:
json.dumps(
{
"message": str(error),
"type": type(error).__name__,
"name": type(error).__name__,
"stack": stack_trace,
"code": ErrorCode.DOCUMENT_ERROR.value,
}
Expand Down
26 changes: 5 additions & 21 deletions plugins/ui/src/js/src/DashboardPlugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,14 @@ import {
useDashboardPanel,
} from '@deephaven/dashboard';
import Log from '@deephaven/log';
import {
DeferredApiBootstrap,
useObjectFetcher,
} from '@deephaven/jsapi-bootstrap';
import { DeferredApiBootstrap } from '@deephaven/jsapi-bootstrap';
import { dh } from '@deephaven/jsapi-types';
import { ErrorBoundary } from '@deephaven/components';
import { useDebouncedCallback } from '@deephaven/react-hooks';
import styles from './styles.scss?inline';
import {
ReadonlyWidgetData,
WidgetDataUpdate,
WidgetFetch,
WidgetId,
} from './widget/WidgetTypes';
import PortalPanel from './layout/PortalPanel';
Expand Down Expand Up @@ -54,9 +50,6 @@ interface DashboardPluginData {
}

interface WidgetWrapper {
/** Function to fetch the widget instance from the server */
fetch: WidgetFetch;

/** ID of this widget */
id: WidgetId;

Expand All @@ -77,20 +70,16 @@ export function DashboardPlugin(
) as unknown as [DashboardPluginData, (data: DashboardPluginData) => void];
const [initialPluginData] = useState(pluginData);

const objectFetcher = useObjectFetcher();

// Keep track of the widgets we've got opened.
const [widgetMap, setWidgetMap] = useState<
ReadonlyMap<WidgetId, WidgetWrapper>
>(new Map());

const handleWidgetOpen = useCallback(
({
fetch,
widgetId = shortid.generate(),
widget,
}: {
fetch: () => Promise<dh.Widget>;
widgetId: string;
widget: WidgetDescriptor;
}) => {
Expand All @@ -99,7 +88,6 @@ export function DashboardPlugin(
const newWidgetMap = new Map(prevWidgetMap);
const oldWidget = newWidgetMap.get(widgetId);
newWidgetMap.set(widgetId, {
fetch,
id: widgetId,
widget,
data: getPreservedData(oldWidget?.data),
Expand Down Expand Up @@ -131,16 +119,14 @@ export function DashboardPlugin(

const handlePanelOpen = useCallback(
({
fetch,
panelId: widgetId = shortid.generate(),
widget,
}: PanelOpenEventDetail<dh.Widget>) => {
const { type } = widget;

switch (type) {
case NAME_ELEMENT: {
const widgetFetch = fetch ?? (() => objectFetcher(widget));
handleWidgetOpen({ fetch: widgetFetch, widgetId, widget });
handleWidgetOpen({ widgetId, widget });
break;
}
case DASHBOARD_ELEMENT: {
Expand All @@ -152,7 +138,7 @@ export function DashboardPlugin(
}
}
},
[handleDashboardOpen, handleWidgetOpen, objectFetcher]
[handleDashboardOpen, handleWidgetOpen]
);

useEffect(
Expand All @@ -171,7 +157,6 @@ export function DashboardPlugin(
Object.entries(openWidgets).forEach(
([widgetId, { descriptor, data }]) => {
newWidgetMap.set(widgetId, {
fetch: () => objectFetcher(descriptor),
id: widgetId,
widget: descriptor,
data,
Expand All @@ -182,7 +167,7 @@ export function DashboardPlugin(
return newWidgetMap;
});
},
[objectFetcher, initialPluginData, id]
[initialPluginData, id]
);

const handlePanelClose = useCallback(
Expand Down Expand Up @@ -295,10 +280,9 @@ export function DashboardPlugin(
>
<DeferredApiBootstrap widget={wrapper.widget}>
<DashboardWidgetHandler
widget={wrapper.widget}
widgetDescriptor={wrapper.widget}
id={wrapper.id}
initialData={wrapper.data}
fetch={wrapper.fetch}
onDataChange={handleWidgetDataChange}
onClose={handleWidgetClose}
/>
Expand Down
42 changes: 11 additions & 31 deletions plugins/ui/src/js/src/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,36 +31,6 @@
}
}

.ui-widget-error-view {
display: flex;
flex-direction: column;
gap: $spacer-1;
flex-grow: 1;
max-height: 100%;

.widget-error-view-content {
position: relative;
flex-shrink: 1;
overflow: hidden;
display: flex;
flex-direction: column;
flex-grow: 1;
}

.widget-error-view-footer {
display: flex;
justify-content: flex-end;
align-items: center;
gap: $spacer-1;
flex-wrap: wrap;
flex-shrink: 0;
}

.error-view {
flex-grow: 1;
}
}

.dh-react-panel-overlay {
background-color: bg-opacity(80);
backdrop-filter: blur(5px);
Expand All @@ -77,6 +47,16 @@
z-index: 1000;

.ui-widget-error-view {
max-width: 100%;
width: 100%;
overflow: auto;
}
}

.ui-text-wrap-balance {
text-wrap: balance;
}

.ui-monospace-text {
font-family: $font-family-monospace;
white-space: pre;
}
18 changes: 4 additions & 14 deletions plugins/ui/src/js/src/widget/DashboardWidgetHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,17 @@
* Handles document events for one widget.
*/
import React, { useCallback } from 'react';
import { WidgetDescriptor } from '@deephaven/dashboard';
import type { dh } from '@deephaven/jsapi-types';
import Log from '@deephaven/log';
import { ReadonlyWidgetData, WidgetDataUpdate, WidgetId } from './WidgetTypes';
import WidgetHandler from './WidgetHandler';
import { WidgetDataUpdate, WidgetId } from './WidgetTypes';
import WidgetHandler, { WidgetHandlerProps } from './WidgetHandler';

const log = Log.module('@deephaven/js-plugin-ui/DashboardWidgetHandler');

export interface DashboardWidgetHandlerProps {
export interface DashboardWidgetHandlerProps
extends Omit<WidgetHandlerProps, 'onClose' | 'onDataChange'> {
/** ID of this widget instance */
id: WidgetId;

/** Widget for this to handle */
widget: WidgetDescriptor;

/** Fetch the widget instance */
fetch: () => Promise<dh.Widget>;

/** Widget data to display */
initialData?: ReadonlyWidgetData;

/** Triggered when all panels opened from this widget have closed */
onClose?: (widgetId: WidgetId) => void;

Expand Down
79 changes: 59 additions & 20 deletions plugins/ui/src/js/src/widget/WidgetErrorView.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,69 @@
import React from 'react';
import { Button, ErrorView } from '@deephaven/components';
import { vsRefresh } from '@deephaven/icons';
import { WidgetError } from './WidgetTypes';
import {
Button,
Content,
ContextualHelp,
CopyButton,
Flex,
Heading,
Icon,
IllustratedMessage,
Text,
} from '@deephaven/components';
import { vsWarning } from '@deephaven/icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
getErrorAction,
getErrorMessage,
getErrorName,
getErrorShortMessage,
getErrorStack,
} from './WidgetUtils';

/** Component that takes a WidgetError and displays the contents in an ErrorView, and has a button to reload the widget from a fresh state. */
/** Component that display an error message. Will automatically show a button for more info and an action button if the error has an Action defined */
function WidgetErrorView({
error,
onReload: onReset,
}: {
error: WidgetError;
onReload: () => void;
error: NonNullable<unknown>;
}): JSX.Element {
const displayMessage = `${error.message.trim()}\n\n${
error.stack ?? ''
}`.trim();
const name = getErrorName(error);
const shortMessage = getErrorShortMessage(error);
const message = getErrorMessage(error);
const stack = getErrorStack(error);
const action = getErrorAction(error);

return (
<div className="ui-widget-error-view">
<div className="widget-error-view-content">
<ErrorView message={displayMessage} type={error.type} isExpanded />
</div>
<div className="widget-error-view-footer">
<Button kind="tertiary" icon={vsRefresh} onClick={onReset}>
Reload
</Button>
</div>
</div>
<IllustratedMessage UNSAFE_className="ui-widget-error-view">
<Icon size="XXL" marginBottom="size-100">
<FontAwesomeIcon icon={vsWarning} />
</Icon>
<Heading UNSAFE_className="ui-text-wrap-balance">{name}</Heading>
<Content>
<Flex direction="column" gap="size-150">
<Text UNSAFE_className="ui-text-wrap-balance">
{shortMessage}
<ContextualHelp variant="info">
<Heading>
{name}{' '}
<CopyButton
copy={() => `${name}\n\n${message}\n\n${stack}`.trim()}
/>
</Heading>
<Content>
<Text UNSAFE_className="ui-monospace-text">
{`${message}\n\n${stack}`.trim()}
</Text>
</Content>
</ContextualHelp>
</Text>
{action != null && (
<Button kind="tertiary" onClick={action.action}>
{action.title}
</Button>
)}
</Flex>
</Content>
</IllustratedMessage>
);
}

Expand Down
Loading

0 comments on commit d9d1e5e

Please sign in to comment.