Skip to content

Commit 1f077ba

Browse files
authored
Display custom error page on crash (#5880)
1 parent 29d43b5 commit 1f077ba

File tree

8 files changed

+38
-26
lines changed

8 files changed

+38
-26
lines changed

frontend/app/src/app/app.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { AuthProvider } from "@/entities/authentication/ui/useAuth";
1313
import { ConfigProvider } from "@/entities/config/config-provider";
1414
import graphqlClient from "@/shared/api/graphql/graphqlClientApollo";
1515
import { queryClient } from "@/shared/api/rest/client";
16-
import ErrorFallback from "@/shared/components/errors/error-fallback";
16+
import { ErrorBoundaryApp } from "@/shared/components/errors/error-boundary-app";
1717
import { store } from "@/shared/stores";
1818

1919
import "@/app/styles/index.css";
@@ -23,7 +23,7 @@ addCollection(mdiIcons);
2323

2424
export function App() {
2525
return (
26-
<ErrorBoundary FallbackComponent={ErrorFallback}>
26+
<ErrorBoundary FallbackComponent={ErrorBoundaryApp}>
2727
<Provider store={store}>
2828
<AuthProvider>
2929
<QueryClientProvider client={queryClient}>

frontend/app/src/app/router.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { constructPathForIpam } from "@/entities/ipam/common/utils";
55
import { IPAM_ROUTE, IP_ADDRESS_GENERIC, IP_PREFIX_GENERIC } from "@/entities/ipam/constants";
66
import { RESOURCE_GENERIC_KIND } from "@/entities/resource-manager/constants";
77
import { constructPath } from "@/shared/api/rest/fetch";
8+
import { ErrorBoundaryRouter } from "@/shared/components/errors/error-boundary-router";
89
import { ReactRouter7Adapter } from "@/shared/lib/use-query-params";
910
import queryString from "query-string";
1011
import { Navigate, Outlet, UIMatch, createBrowserRouter } from "react-router";
@@ -13,6 +14,7 @@ import { QueryParamProvider } from "use-query-params";
1314
export const router = createBrowserRouter([
1415
{
1516
path: "",
17+
errorElement: <ErrorBoundaryRouter />,
1618
element: (
1719
<QueryParamProvider
1820
adapter={ReactRouter7Adapter}

frontend/app/src/entities/events/ui/global-events.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { FilterResetButton } from "@/entities/nodes/object/ui/filters/filter-reset-button";
22
import { Button } from "@/shared/components/buttons/button-primitive";
3-
import ErrorFallback from "@/shared/components/errors/error-fallback";
3+
import ErrorScreen from "@/shared/components/errors/error-screen";
44
import NoDataFound from "@/shared/components/errors/no-data-found";
55
import Content from "@/shared/components/layout/content";
66
import { Spinner } from "@/shared/components/ui/spinner";
@@ -37,7 +37,7 @@ export const GlobalEvents = () => {
3737
const flatData = React.useMemo(() => data?.pages?.flat() ?? [], [data]);
3838

3939
if (error) {
40-
return <ErrorFallback error={error} />;
40+
return <ErrorScreen message={error.message} />;
4141
}
4242

4343
return (

frontend/app/src/entities/events/ui/node-events.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { QSP } from "@/config/qsp";
22
import { useNodeLabel } from "@/entities/nodes/object/api/get-display-label.query";
33
import { constructPath } from "@/shared/api/rest/fetch";
4-
import ErrorFallback from "@/shared/components/errors/error-fallback";
4+
import ErrorScreen from "@/shared/components/errors/error-screen";
55
import NoDataFound from "@/shared/components/errors/no-data-found";
66
import { Link } from "@/shared/components/ui/link";
77
import { Spinner } from "@/shared/components/ui/spinner";
@@ -44,7 +44,7 @@ export const NodeEvents = ({ parentId }: { parentId?: string }) => {
4444
}
4545

4646
if (error || displayLabelError) {
47-
return <ErrorFallback error={error} />;
47+
return <ErrorScreen message={error?.message || displayLabelError?.message} />;
4848
}
4949

5050
if (!flatData?.length) {
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import ErrorFallback from "@/shared/components/errors/error-fallback";
2+
import { FallbackProps } from "react-error-boundary";
3+
4+
export const ErrorBoundaryApp = ({ error, resetErrorBoundary }: FallbackProps) => {
5+
return <ErrorFallback error={error} onReset={resetErrorBoundary} />;
6+
};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import ErrorFallback from "@/shared/components/errors/error-fallback";
2+
import { useRouteError } from "react-router";
3+
4+
export function ErrorBoundaryRouter() {
5+
const error = useRouteError();
6+
7+
return <ErrorFallback error={error as Error} onReset={() => window.location.reload()} />;
8+
}

frontend/app/src/shared/components/errors/error-fallback.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@ import { Card } from "@/shared/components/ui/card";
44
import Kbd from "@/shared/components/ui/kbd";
55
import { Icon } from "@iconify-icon/react";
66
import { useEffect, useState } from "react";
7-
import { useErrorBoundary } from "react-error-boundary";
87

9-
type ErrorFallbackProps = {
8+
interface ErrorFallbackProps {
109
error: Error;
11-
};
12-
function ErrorFallback({ error }: ErrorFallbackProps) {
13-
const { resetBoundary } = useErrorBoundary();
10+
onReset: () => void;
11+
}
12+
13+
function ErrorFallback({ error, onReset }: ErrorFallbackProps) {
1414
const [bugPosition, setBugPosition] = useState({ top: 0, left: 0 });
1515

1616
useEffect(() => {
1717
const onKeydown = (event: KeyboardEvent) => {
1818
if (event.key.toLowerCase() === "enter") {
19-
resetBoundary();
19+
onReset();
2020
}
21-
console.error(event.key);
21+
2222
if (event.key.toLowerCase() === "backspace") {
2323
window.location.href = window.location.origin;
2424
}
@@ -57,11 +57,11 @@ function ErrorFallback({ error }: ErrorFallbackProps) {
5757
</p>
5858

5959
<div>
60-
<Button className="mr-2" onClick={resetBoundary}>
60+
<Button className="mr-2" onClick={onReset}>
6161
Refresh
6262
</Button>
6363
<a href={window.location.origin}>
64-
<Button variant="outline" className="my-4" onClick={resetBoundary}>
64+
<Button variant="outline" className="my-4">
6565
Homepage
6666
</Button>
6767
</a>
@@ -104,7 +104,7 @@ function ErrorFallback({ error }: ErrorFallbackProps) {
104104
</p>
105105
</Card>
106106

107-
{error.stack && (
107+
{error?.stack && (
108108
<Accordion className="text-sm text-gray-600" title="View error stack">
109109
<pre className="p-2 rounded bg-red-50 text-red-800">{error.stack}</pre>
110110
</Accordion>

frontend/app/tests/integrations/screens/object-details.cy.tsx

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22

33
import { gql } from "@apollo/client";
44
import { MockedProvider } from "@apollo/client/testing";
5-
import { ErrorBoundary } from "react-error-boundary";
65
import { Route, Routes } from "react-router";
76
import { BranchContext } from "../../../src/entities/branches/ui/branches-provider";
87
import { ObjectDetailsPage } from "../../../src/pages/objects/object-details";
9-
import ErrorFallback from "../../../src/shared/components/errors/error-fallback";
108
import { generateBranch } from "../../fake/branch";
119
import {
1210
deviceDetailsMocksASNName,
@@ -75,14 +73,12 @@ describe("List screen", () => {
7573
cy.mount(
7674
<BranchContext value={{ currentBranch: generateBranch(), setCurrentBranch: () => {} }}>
7775
<MockedProvider mocks={mocks} addTypename={false}>
78-
<ErrorBoundary FallbackComponent={ErrorFallback}>
79-
<Routes>
80-
<Route
81-
element={<ObjectDetailsPage schema={deviceDetailsMocksSchema[0]} />}
82-
path={graphqlQueryItemsPath}
83-
/>
84-
</Routes>
85-
</ErrorBoundary>
76+
<Routes>
77+
<Route
78+
element={<ObjectDetailsPage schema={deviceDetailsMocksSchema[0]} />}
79+
path={graphqlQueryItemsPath}
80+
/>
81+
</Routes>
8682
</MockedProvider>
8783
</BranchContext>,
8884
{

0 commit comments

Comments
 (0)