Skip to content

Commit

Permalink
feat(components): dispatch gs-error event when an error occurs
Browse files Browse the repository at this point in the history
closes #366
  • Loading branch information
fengelniederhammer committed Aug 20, 2024
1 parent 9b7dd39 commit 1483a10
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 6 deletions.
8 changes: 8 additions & 0 deletions components/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
export * from './web-components';

export { type ErrorEvent, UserFacingError } from './preact/components/error-display';

declare global {
interface HTMLElementEventMap {
'gs-error': ErrorEvent;
}
}
28 changes: 24 additions & 4 deletions components/src/preact/components/error-boundary.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import { withActions } from '@storybook/addon-actions/decorator';
import { type Meta, type StoryObj } from '@storybook/preact';
import { expect, waitFor, within } from '@storybook/test';

import { ErrorBoundary } from './error-boundary';
import { GS_ERROR_EVENT_TYPE, UserFacingError } from './error-display';

const meta: Meta = {
title: 'Component/Error boundary',
component: ErrorBoundary,
parameters: { fetchMock: {} },
parameters: {
fetchMock: {},
actions: { handles: [GS_ERROR_EVENT_TYPE] },
},
argTypes: {
size: { control: 'object' },
defaultSize: { control: 'object' },
},
args: {
size: { height: '600px', width: '100%' },
},
decorators: [withActions],
};

export default meta;
Expand All @@ -34,7 +40,21 @@ export const ErrorBoundaryWithoutErrorStory: StoryObj = {
export const ErrorBoundaryWithErrorStory: StoryObj = {
render: (args) => (
<ErrorBoundary size={args.size}>
<ContentThatThrowsError />
<ContentThatThrowsError error={() => new Error('Some error')} />
</ErrorBoundary>
),
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const content = canvas.queryByText('Some content.', { exact: false });
await waitFor(() => expect(content).not.toBeInTheDocument());
await waitFor(() => expect(canvas.getByText('Error')).toBeInTheDocument());
},
};

export const ErrorBoundaryWithUserFacingErrorStory: StoryObj = {
render: (args) => (
<ErrorBoundary size={args.size}>
<ContentThatThrowsError error={() => new UserFacingError('Error Headline', 'Some error')} />
</ErrorBoundary>
),
play: async ({ canvasElement }) => {
Expand All @@ -45,6 +65,6 @@ export const ErrorBoundaryWithErrorStory: StoryObj = {
},
};

const ContentThatThrowsError = () => {
throw new Error('Some error');
const ContentThatThrowsError = (props: { error: () => Error }) => {
throw props.error();
};
24 changes: 22 additions & 2 deletions components/src/preact/components/error-display.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { type FunctionComponent } from 'preact';
import { useRef } from 'preact/hooks';
import { useEffect, useRef } from 'preact/hooks';

export const GS_ERROR_EVENT_TYPE = 'gs-error';

export interface ErrorEvent extends Event {
readonly error: Error;
}

export class UserFacingError extends Error {
constructor(
Expand All @@ -14,10 +20,24 @@ export class UserFacingError extends Error {
export const ErrorDisplay: FunctionComponent<{ error: Error }> = ({ error }) => {
console.error(error);

const containerRef = useRef<HTMLInputElement>(null);
const ref = useRef<HTMLDialogElement>(null);

useEffect(() => {
containerRef.current?.dispatchEvent(
new ErrorEvent(GS_ERROR_EVENT_TYPE, {
error,
bubbles: true,
composed: true,
}),
);
});

return (
<div className='h-full w-full rounded-md border-2 border-gray-100 p-2 flex items-center justify-center flex-col'>
<div
ref={containerRef}
className='h-full w-full rounded-md border-2 border-gray-100 p-2 flex items-center justify-center flex-col'
>
<div className='text-red-700 font-bold'>Error</div>
<div>
Oops! Something went wrong.
Expand Down
8 changes: 8 additions & 0 deletions components/src/web-components/errorHandling.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Meta } from '@storybook/blocks';

<Meta title='Components/Error Handling' />

# Error Handling

All components dispatch a `gs-error` event of type `ErrorEvent` when an error occurs.
The event contains an `error` property that holds the `Error` object.

0 comments on commit 1483a10

Please sign in to comment.