Skip to content

Commit

Permalink
TELESTION-443: Error Boundaries
Browse files Browse the repository at this point in the history
  • Loading branch information
pklaschka committed Jan 12, 2024
1 parent 8e11049 commit 10a3901
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 6 deletions.
1 change: 1 addition & 0 deletions frontend-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"nats.ws": "^1.19.1",
"react-bootstrap": "^2.9.1",
"react-dom": "^18.2.0",
"react-error-boundary": "^4.0.12",
"react-router-dom": "^6.19.0",
"zod": "^3.22.4"
},
Expand Down
12 changes: 12 additions & 0 deletions frontend-react/pnpm-lock.yaml

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

9 changes: 7 additions & 2 deletions frontend-react/src/app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { initTelestion, registerWidgets, UserData } from '../lib';
import { simpleWidget } from './widgets/simple-widget';
import { errorWidget } from './widgets/error-widget';

const defaultUserData: UserData = {
version: '0.0.1',
Expand All @@ -8,7 +9,7 @@ const defaultUserData: UserData = {
title: 'Default Dashboard',
layout: [
['.', '8fj2o4', '.'],
['.', '.', '.']
['.', '9fj2o4', '9fj2o4']
]
}
},
Expand All @@ -18,11 +19,15 @@ const defaultUserData: UserData = {
configuration: {
text: 'Hello World!'
}
},
'9fj2o4': {
type: 'error-widget',
configuration: {}
}
}
};

registerWidgets(simpleWidget);
registerWidgets(simpleWidget, errorWidget);

await initTelestion({
version: '0.0.1',
Expand Down
24 changes: 24 additions & 0 deletions frontend-react/src/app/widgets/error-widget/error-widget.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useEffect, useState } from 'react';

export function ErrorWidget() {
const [showError, setShowError] = useState(false);
useEffect(() => {
const timeout = setTimeout(() => {
setShowError(true);
}, 3_000);
return () => clearTimeout(timeout);
}, []);

if (showError) {
throw new Error(
`Test error thrown by the error widget at ${new Date().toISOString()}`
);
}

return (
<div>
<h1>This widget will throw an error after three seconds</h1>
<p>It is used to test the error handling of the dashboard.</p>
</div>
);
}
14 changes: 14 additions & 0 deletions frontend-react/src/app/widgets/error-widget/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ErrorWidget } from './error-widget.tsx';
import { Widget } from '../../../lib';

export const errorWidget: Widget = {
id: 'error-widget',
label: 'Error Widget',

createConfig() {
return {};
},

element: <ErrorWidget />,
configElement: <div>Config</div>
};
7 changes: 4 additions & 3 deletions frontend-react/src/lib/application/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Import styles first to allow overriding bootstrap styles in CSS modules
import 'bootstrap-icons/font/bootstrap-icons.min.css';
import './index.scss';

import React from 'react';
import ReactDOM from 'react-dom/client';
import { RouterProvider } from 'react-router-dom';
Expand All @@ -6,9 +10,6 @@ import { registerWidgets } from '../widget';
import { TelestionOptions } from './model.ts';
import { createTelestionRouter } from './telestion-router.tsx';

import 'bootstrap-icons/font/bootstrap-icons.min.css';
import './index.scss';

export * from './model.ts';
export * from './hooks';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.alert {
height: 100%;
margin: 0;
overflow-y: auto;
}
31 changes: 31 additions & 0 deletions frontend-react/src/lib/widget/component/error-fallback.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Alert, AlertHeading, Button } from 'react-bootstrap';
import styles from './error-fallback.module.css';

export function ErrorFallback({
error,
resetErrorBoundary
}: {
error: Error;
resetErrorBoundary: () => void;
}) {
return (
<Alert variant="danger" className={styles.alert}>
<AlertHeading>Widget Error</AlertHeading>
<p>
Unfortunately, the widget encountered an error and cannot be displayed.
</p>
<p>Please try again or contact your developer if the problem persists.</p>
<details>
<summary>Details</summary>
<pre>{error.stack}</pre>
</details>
<Button
className={'mt-4'}
variant={'outline-light'}
onClick={resetErrorBoundary}
>
Reload the widget
</Button>
</Alert>
);
}
6 changes: 5 additions & 1 deletion frontend-react/src/lib/widget/component/widget-renderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { getWidgetById } from '../state.ts';
import { getUserData } from '../../user-data';

import styles from './widget-renderer.module.css';
import { ErrorBoundary } from 'react-error-boundary';
import { ErrorFallback } from './error-fallback.tsx';

export interface WidgetRendererProps {
widgetInstanceId: string;
Expand Down Expand Up @@ -44,7 +46,9 @@ registerWidget({
key={`renderer-${widgetInstanceId}`}
value={widgetInstance.configuration}
>
{widget.element}
<ErrorBoundary FallbackComponent={ErrorFallback}>
{widget.element}
</ErrorBoundary>
</widgetConfigContext.Provider>
) : (
<div>
Expand Down

0 comments on commit 10a3901

Please sign in to comment.