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: Use useWidget hook to load widgets #502

Merged
merged 11 commits into from
Jun 14, 2024
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
Loading