first commit
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import type { ErrorInfo } from "react";
|
||||
import React from "react";
|
||||
|
||||
class ErrorBoundary extends React.Component<
|
||||
{ children: React.ReactNode; message?: string },
|
||||
{ error: Error | null; errorInfo: ErrorInfo | null }
|
||||
> {
|
||||
constructor(props: { children: React.ReactNode } | Readonly<{ children: React.ReactNode }>) {
|
||||
super(props);
|
||||
this.state = { error: null, errorInfo: null };
|
||||
}
|
||||
|
||||
componentDidCatch?(error: Error, errorInfo: ErrorInfo) {
|
||||
// Catch errors in any components below and re-render with error message
|
||||
this.setState({ error, errorInfo });
|
||||
// You can also log error messages to an error reporting service here
|
||||
}
|
||||
|
||||
render() {
|
||||
// do not intercept next-not-found error, allow displaying not-found.tsx page when notFound() is thrown on server side
|
||||
if (
|
||||
this.state.error !== null &&
|
||||
"digest" in this.state.error &&
|
||||
this.state.error.digest === "NEXT_NOT_FOUND"
|
||||
) {
|
||||
return this.props.children;
|
||||
}
|
||||
|
||||
if (this.state.errorInfo) {
|
||||
// Error path
|
||||
return (
|
||||
<div>
|
||||
<h2>{this.props.message || "Something went wrong."}</h2>
|
||||
<details style={{ whiteSpace: "pre-wrap" }}>
|
||||
{this.state.error && this.state.error.toString()}
|
||||
</details>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// Normally, just render children
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
export default ErrorBoundary;
|
||||
@@ -0,0 +1,39 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { useEffect } from "react";
|
||||
|
||||
import ErrorBoundary from "./ErrorBoundary";
|
||||
|
||||
describe("ErrorBoundary", () => {
|
||||
test("should render children when no error occurs", () => {
|
||||
const { container } = render(
|
||||
<ErrorBoundary>
|
||||
<div>Child Component</div>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
|
||||
const childElement = container.querySelector("div");
|
||||
expect(childElement).toBeInTheDocument();
|
||||
expect(childElement?.textContent).toBe("Child Component");
|
||||
});
|
||||
|
||||
test("should render error message and error details when an error occurs", () => {
|
||||
const ErrorThrowingComponent = () => {
|
||||
useEffect(() => {
|
||||
throw new Error("Test Error");
|
||||
}, []);
|
||||
|
||||
return <div>Error Throwing Component</div>;
|
||||
};
|
||||
|
||||
render(
|
||||
<ErrorBoundary message="Error Message">
|
||||
<ErrorThrowingComponent />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
|
||||
const errorMessage = screen.getByText("Error Message");
|
||||
const errorDetails = screen.getByText("Error: Test Error");
|
||||
expect(errorMessage).toBeInTheDocument();
|
||||
expect(errorDetails).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,64 @@
|
||||
import { Canvas, Meta, Story } from "@storybook/addon-docs";
|
||||
|
||||
import {
|
||||
Examples,
|
||||
Example,
|
||||
Title,
|
||||
VariantsTable,
|
||||
CustomArgsTable,
|
||||
VariantRow,
|
||||
} from "@calcom/storybook/components";
|
||||
|
||||
import ErrorBoundary from "./ErrorBoundary";
|
||||
import { Tooltip } from "../tooltip";
|
||||
|
||||
<Meta title="UI/ErrorBoundary" component={ErrorBoundary} />
|
||||
|
||||
<Title title="ErrorBoundary" suffix="Brief" subtitle="Version 1.0 — Last Update: 17 Aug 2023" />
|
||||
|
||||
## Definition
|
||||
|
||||
ErrorBoundary is an element that catches JavaScript errors in their child component tree, log those errors and display a fallback UI.
|
||||
|
||||
## Structure
|
||||
|
||||
ErrorBoundary offers a flexible component capable of catching JavaScript errors and displaying it in the UI.
|
||||
|
||||
<CustomArgsTable of={ErrorBoundary} />
|
||||
|
||||
<Examples title="ErrorBoundary">
|
||||
<Example title="Default">
|
||||
<ErrorBoundary message="There is a problem with the App">
|
||||
<p>Child Component</p>
|
||||
</ErrorBoundary>
|
||||
</Example>
|
||||
<Example title="With Error">
|
||||
<ErrorBoundary message="There is a problem with the App">
|
||||
<Tooltip />
|
||||
</ErrorBoundary>
|
||||
</Example>
|
||||
</Examples>
|
||||
|
||||
<Title offset title="ErrorBoundary" suffix="Variants" />
|
||||
|
||||
<Canvas>
|
||||
<Story
|
||||
name="ErrorBoundary"
|
||||
args={{
|
||||
children: <p class="text-default">Child Component</p>,
|
||||
message: "There is a problem with the App",
|
||||
}}>
|
||||
{({ children, message }) => (
|
||||
<VariantsTable titles={["Default"]} columnMinWidth={150}>
|
||||
<VariantRow variant="Default">
|
||||
<ErrorBoundary message={message}>{children}</ErrorBoundary>
|
||||
</VariantRow>
|
||||
<VariantRow variant="With Error">
|
||||
<ErrorBoundary message={message}>
|
||||
<Tooltip />
|
||||
</ErrorBoundary>
|
||||
</VariantRow>
|
||||
</VariantsTable>
|
||||
)}
|
||||
</Story>
|
||||
</Canvas>
|
||||
1
calcom/packages/ui/components/errorBoundary/index.ts
Normal file
1
calcom/packages/ui/components/errorBoundary/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as ErrorBoundary } from "./ErrorBoundary";
|
||||
Reference in New Issue
Block a user