diff --git a/src/components/ErrorBoundary/ErrorBoundary.tsx b/src/components/ErrorBoundary/ErrorBoundary.tsx index ea10dfda..97689461 100644 --- a/src/components/ErrorBoundary/ErrorBoundary.tsx +++ b/src/components/ErrorBoundary/ErrorBoundary.tsx @@ -1,51 +1,91 @@ -import React from "react"; import * as Sentry from "@sentry/react"; -import { logger } from "@utils/logger"; +import { logger } from "@/utils/logger"; +import { getTypeSafeError } from "@/utils/typeSafety/getTypeSafeError"; +import { DefaultErrorFallback } from "./DefaultErrorFallback"; /** - * A reusable error-boundary with `Sentry` and logging integrations. - * - * **NOTE:** ErrorBoundary WON'T catch the following: - * - Event-driven errors - * - Uncaught promise rejections + * Default `onError` handler fn for {@link ErrorBoundary} */ -export class ErrorBoundary extends React.Component { - public override state: ErrorBoundaryState = { - error: null, - hasError: false, - }; - - public static getDerivedStateFromError(error: Error) { - logger.error(error, "ErrorBoundary.getDerivedStateFromError"); - return { hasError: true }; - } - - public override componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { - logger.error( - `${error} - ${errorInfo}`, - `ErrorBoundary${this.props?.identifier ? `:${this.props.identifier}` : null}` - ); - } +const defaultErrorBoundaryOnErrorHandler: ErrorBoundaryOnErrorHandler = ( + error: Error, + _componentStack: string, + _eventId: string +) => { + logger.error(error, "Sentry.ErrorBoundary"); +}; - public override render() { - const { showDialog = true, ...props } = this.props; +/** + * Default `fallback` render fn for {@link ErrorBoundary} + */ +const defaultErrorBoundaryFallback: ErrorBoundaryFallbackFn = ({ error }) => ( + +); - return ( - - {this.props.children} - - ); - } +/** + ;* A reusable [`Sentry ErrorBoundary`][sentry-eb-docs] component. + * + * > **NOTE:** ErrorBoundary components DO NOT catch the following: + * > - Event-driven errors + * > - Uncaught promise rejections + * + * #### [Sentry.ErrorBoundary Options][sentry-eb-opts]: + * + * - `showDialog` - If a [Sentry User Feedback Widget][sentry-feedback-widget] should be rendered + * when the ErrorBoundary catches an error. + * + * - _**Default:**_ `true` + * + * - `dialogOptions` - Options that are passed into the Sentry User Feedback Widget. See all + * possible customization options [here][sentry-feedback-widget-opts]. + * + * - `fallback` - JSX to render when the ErrorBoundary catches an error. Can be JSX or a fn that + * returns JSX. If you provide a fn, Sentry will call it with additional info and helpers. + * + * - _**Default:**_ {@link DefaultErrorFallback|`DefaultErrorFallback`} + * + * - `onError` - A fn that gets called when the ErrorBoundary encounters an error. `onError` is + * useful if you want to propagate the error into a state management library like Redux, or if + * you want to check any side effects that could have occurred due to the error. + * + * - _**Default:**_ {@link logger.error|`logger.error`} + * + * - `onMount` - A fn that gets called in `componentDidMount()`. + * + * - `onUnmount` - A fn that gets called in `componentWillUnmount()`. + * + * - `beforeCapture` - A fn that gets called before an error is sent to Sentry, allowing for extra + * tags or context to be added to the error. + * + * Click [here to view source code][sentry-eb-src] for `Sentry.ErrorBoundary`. + * + * @docs + * - [React docs: Error Boundaries][react-eb-docs] + * - [Sentry.ErrorBoundary][sentry-eb-docs] + * - [Sentry.ErrorBoundary Options][sentry-eb-opts] + * + * [react-eb-docs]: https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary + * [sentry-eb-src]: https://github.com/getsentry/sentry-javascript/blob/develop/packages/react/src/errorboundary.tsx + * [sentry-eb-docs]: https://docs.sentry.io/platforms/javascript/guides/react/features/error-boundary/ + * [sentry-eb-opts]: https://docs.sentry.io/platforms/javascript/guides/react/features/error-boundary/#options + * [sentry-feedback-widget]: https://docs.sentry.io/platforms/javascript/guides/react/enriching-events/user-feedback/ + * [sentry-feedback-widget-opts]: https://docs.sentry.io/platforms/javascript/guides/react/enriching-events/user-feedback/#customizing-the-widget + */ +export class ErrorBoundary extends Sentry.ErrorBoundary { + public static readonly defaultProps = { + showDialog: true, + onError: defaultErrorBoundaryOnErrorHandler, + fallback: defaultErrorBoundaryFallback, + } satisfies Partial; } -export type ErrorBoundaryProps = React.ComponentProps & { - showDialog?: boolean; - identifier?: string; - children?: React.ReactNode; -}; +/** + * `onError` handler fn for {@link ErrorBoundary} + * @note Available `onError` fn args: `error`, `componentStack`, `eventId` + */ +export type ErrorBoundaryOnErrorHandler = NonNullable; -interface ErrorBoundaryState { - error: Error | string | null; - hasError: boolean; -} +/** + * `fallback` render fn for {@link ErrorBoundary} + * @note Available `fallback` fn args: { `error`, `componentStack`, `eventId`, `resetError` } + */ +export type ErrorBoundaryFallbackFn = NonNullable; diff --git a/src/components/ErrorBoundary/classNames.ts b/src/components/ErrorBoundary/classNames.ts new file mode 100644 index 00000000..34f1ca2a --- /dev/null +++ b/src/components/ErrorBoundary/classNames.ts @@ -0,0 +1,6 @@ +/** + * Class names for `ErrorBoundary` components (src/components/ErrorBoundary/). + */ +export const errorBoundaryClassNames = { + defaultErrorFallbackRoot: "error-boundary__default-error-fallback-root", +} as const;