Skip to content

Commit 6b5cc27

Browse files
authored
feat: add ErrorBoundary component
* feat: add ErrorBoundary component * feat: add error logging to ErrorBoundary * chore: add changeset file
1 parent 4e09c15 commit 6b5cc27

File tree

7 files changed

+98
-2
lines changed

7 files changed

+98
-2
lines changed

.changeset/empty-bottles-fold.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'react-starter-boilerplate': minor
3+
---
4+
5+
add ErrorBoundary component

package-lock.json

Lines changed: 14 additions & 2 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
@@ -61,6 +61,7 @@
6161
"qs": "6.12.1",
6262
"react": "18.2.0",
6363
"react-dom": "18.2.0",
64+
"react-error-boundary": "4.0.13",
6465
"react-intl": "6.6.5",
6566
"react-router-dom": "6.22.3",
6667
"typescript": "5.4.3"
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { render, screen } from '@testing-library/react';
2+
3+
import { logger } from 'integrations/logger';
4+
5+
import { ErrorBoundary } from './ErrorBoundary';
6+
7+
const logErrorSpy = vitest.spyOn(logger, 'error');
8+
9+
const ErrorComponent = ({ shouldError = true }: { shouldError?: boolean }) => {
10+
if (shouldError) {
11+
throw new Error('error');
12+
}
13+
return <div>no error</div>;
14+
};
15+
16+
describe('ErrorBoundary', () => {
17+
it('should show fallback component and log error via logger', () => {
18+
render(
19+
<ErrorBoundary fallback={<div>error</div>}>
20+
<ErrorComponent />
21+
</ErrorBoundary>,
22+
);
23+
24+
expect(screen.getByText('error')).toBeInTheDocument();
25+
expect(logErrorSpy).toHaveBeenCalledTimes(1);
26+
});
27+
28+
it('should not log error when shouldLog = false', () => {
29+
render(
30+
<ErrorBoundary shouldLog={false} fallback={<div>error</div>}>
31+
<ErrorComponent />
32+
</ErrorBoundary>,
33+
);
34+
35+
expect(screen.getByText('error')).toBeInTheDocument();
36+
expect(logErrorSpy).not.toBeCalled();
37+
});
38+
39+
it('should show children content when there is no error', () => {
40+
render(
41+
<ErrorBoundary fallback={<div>error</div>}>
42+
<ErrorComponent shouldError={false} />
43+
</ErrorBoundary>,
44+
);
45+
46+
expect(screen.getByText('no error')).toBeInTheDocument();
47+
expect(logErrorSpy).not.toBeCalled();
48+
});
49+
});
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { ErrorBoundary as ReactErrorBoundary } from 'react-error-boundary';
2+
import { ErrorInfo } from 'react';
3+
4+
import { logger } from 'integrations/logger';
5+
6+
import { ErrorBoundaryProps } from './ErrorBoundary.types';
7+
8+
export const ErrorBoundary = ({ shouldLog = true, onError, ...props }: ErrorBoundaryProps) => {
9+
const handleError = (error: Error, errorInfo: ErrorInfo) => {
10+
if (shouldLog) {
11+
logger.error(error);
12+
}
13+
onError?.(error, errorInfo);
14+
};
15+
16+
// eslint-disable-next-line react/jsx-props-no-spreading
17+
return <ReactErrorBoundary onError={handleError} {...props} />;
18+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import {
2+
ErrorBoundaryProps as ReactErrorBoundaryProps,
3+
FallbackProps as ErrorBoundaryFallbackProps,
4+
} from 'react-error-boundary';
5+
6+
export type ErrorBoundaryProps = ReactErrorBoundaryProps & {
7+
shouldLog?: boolean;
8+
};
9+
10+
export type FallbackProps = ErrorBoundaryFallbackProps;

vite.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export default defineConfig({
2424
globals: true,
2525
environment: 'jsdom',
2626
setupFiles: 'src/setupTests.ts',
27+
clearMocks: true,
2728
exclude: [...configDefaults.exclude, 'e2e/**/*', 'e2e-playwright/**/*'],
2829
coverage: {
2930
reporter: ['text', 'json', 'html'],

0 commit comments

Comments
 (0)