Skip to content

Commit f5ad224

Browse files
feat: add ErrorBoundary (#549)
1 parent 27c4094 commit f5ad224

File tree

13 files changed

+234
-8
lines changed

13 files changed

+234
-8
lines changed

package-lock.json

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"numeral": "2.0.6",
3131
"path-to-regexp": "3.0.0",
3232
"qs": "^6.11.0",
33+
"react-error-boundary": "^4.0.12",
3334
"react-json-inspector": "7.1.1",
3435
"react-list": "0.8.11",
3536
"react-monaco-editor": "0.30.1",
Lines changed: 32 additions & 0 deletions
Loading
Lines changed: 32 additions & 0 deletions
Loading
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
@import '../../styles/mixins.scss';
2+
3+
.ydb-error-boundary {
4+
display: flex;
5+
flex-direction: row;
6+
align-items: flex-start;
7+
8+
padding: 20px;
9+
10+
@include body-2-typography();
11+
12+
&__illustration {
13+
width: 230px;
14+
height: 230px;
15+
margin-right: 20px;
16+
}
17+
&__error-title {
18+
margin-top: 44px;
19+
@include lead-typography();
20+
}
21+
&__error-description {
22+
margin-top: 12px;
23+
}
24+
&__show-details {
25+
margin-top: 8px;
26+
}
27+
&__error-details {
28+
padding: 13px 18px;
29+
30+
border: 1px solid var(--g-color-line-generic);
31+
background-color: var(--g-color-base-generic-ultralight);
32+
}
33+
&__actions {
34+
display: flex;
35+
flex-direction: row;
36+
gap: 10px;
37+
38+
margin-top: 20px;
39+
}
40+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import type {ReactNode} from 'react';
2+
import {ErrorBoundary as ErrorBoundaryBase} from 'react-error-boundary';
3+
import cn from 'bem-cn-lite';
4+
5+
import {Button, Disclosure} from '@gravity-ui/uikit';
6+
7+
import {registerError} from '../../utils/registerError';
8+
import {Illustration} from '../Illustration';
9+
import i18n from './i18n';
10+
import './ErrorBoundary.scss';
11+
12+
const b = cn('ydb-error-boundary');
13+
14+
interface ErrorBoundaryProps {
15+
children?: ReactNode;
16+
useRetry?: boolean;
17+
onReportProblem?: (error?: Error) => void;
18+
}
19+
20+
export const ErrorBoundary = ({children, useRetry = true, onReportProblem}: ErrorBoundaryProps) => {
21+
return (
22+
<ErrorBoundaryBase
23+
onError={(error, info) => {
24+
registerError(error, info.componentStack, 'error-boundary');
25+
}}
26+
fallbackRender={({error, resetErrorBoundary}) => {
27+
return (
28+
<div className={b(null)}>
29+
<Illustration name="error" className={b('illustration')} />
30+
<div className={b('content')}>
31+
<h2 className={b('error-title')}>{i18n('error-title')}</h2>
32+
<div className={b('error-description')}>
33+
{i18n('error-description')}
34+
</div>
35+
<Disclosure
36+
summary={i18n('show-details')}
37+
className={b('show-details')}
38+
size="m"
39+
>
40+
<pre className={b('error-details')}>{error.stack}</pre>
41+
</Disclosure>
42+
<div className={b('actions')}>
43+
{useRetry && (
44+
<Button view="outlined" onClick={resetErrorBoundary}>
45+
{i18n('button-reset')}
46+
</Button>
47+
)}
48+
{onReportProblem && (
49+
<Button view="outlined" onClick={() => onReportProblem(error)}>
50+
{i18n('report-problem')}
51+
</Button>
52+
)}
53+
</div>
54+
</div>
55+
</div>
56+
);
57+
}}
58+
>
59+
{children}
60+
</ErrorBoundaryBase>
61+
);
62+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"error-title": "Something went wrong",
3+
"error-description": "We have something broken, but don't worry, it won't last long",
4+
"show-details": "Show details",
5+
"report-problem": "Report a problem",
6+
"button-reset": "Try again"
7+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {i18n, Lang} from '../../../utils/i18n';
2+
3+
import en from './en.json';
4+
import ru from './ru.json';
5+
6+
const COMPONENT = 'ydb-error-boundary';
7+
8+
i18n.registerKeyset(Lang.En, COMPONENT, en);
9+
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
10+
11+
export default i18n.keyset(COMPONENT);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"error-title": "Что-то пошло не так",
3+
"error-description": "У нас что-то сломалось, но не переживайте, это ненадолго",
4+
"show-details": "Показать детали",
5+
"report-problem": "Сообщить о проблеме",
6+
"button-reset": "Попробовать снова"
7+
}

src/components/Illustration/Illustration.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ const store: IllustrationStore = {
1313
light: {
1414
403: () => import('../../assets/illustrations/light/403.svg'),
1515
thumbsUp: () => import('../../assets/illustrations/light/thumbsUp.svg'),
16+
error: () => import('../../assets/illustrations/light/error.svg'),
1617
},
1718
dark: {
1819
403: () => import('../../assets/illustrations/dark/403.svg'),
19-
thumbsUp: () => import('../../assets/illustrations/light/thumbsUp.svg'),
20+
thumbsUp: () => import('../../assets/illustrations/dark/thumbsUp.svg'),
21+
error: () => import('../../assets/illustrations/dark/error.svg'),
2022
},
2123
};
2224

0 commit comments

Comments
 (0)