From 6e4b81ee0e7cd88cb3741b3dc16e63ef768509a1 Mon Sep 17 00:00:00 2001 From: Gil Barbara Date: Sun, 27 Mar 2022 22:33:39 -0300 Subject: [PATCH] Implement react-error-boundary --- package-lock.json | 24 ++++ package.json | 1 + src/components/ErrorHandler.tsx | 26 ++++ src/containers/ErrorHandler.tsx | 56 -------- src/index.tsx | 7 +- test/components/ErrorHandler.spec.tsx | 18 +++ .../__snapshots__/ErrorHandler.spec.tsx.snap | 121 ++++++++++++++++++ test/containers/ErrorHandler.spec.tsx | 30 ----- .../__snapshots__/ErrorHandler.spec.tsx.snap | 78 ----------- 9 files changed, 194 insertions(+), 167 deletions(-) create mode 100644 src/components/ErrorHandler.tsx delete mode 100644 src/containers/ErrorHandler.tsx create mode 100644 test/components/ErrorHandler.spec.tsx create mode 100644 test/components/__snapshots__/ErrorHandler.spec.tsx.snap delete mode 100644 test/containers/ErrorHandler.spec.tsx delete mode 100644 test/containers/__snapshots__/ErrorHandler.spec.tsx.snap diff --git a/package-lock.json b/package-lock.json index 03bbbc7..f193fe1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "is-lite": "^0.8.1", "react": "^17.0.2", "react-dom": "^17.0.2", + "react-error-boundary": "^3.1.4", "react-helmet-async": "^1.2.3", "react-inlinesvg": "^2.3.0", "react-redux": "^7.2.6", @@ -21385,6 +21386,21 @@ "react": "17.0.2" } }, + "node_modules/react-error-boundary": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz", + "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, "node_modules/react-error-overlay": { "version": "6.0.10", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz", @@ -42389,6 +42405,14 @@ "scheduler": "^0.20.2" } }, + "react-error-boundary": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz", + "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==", + "requires": { + "@babel/runtime": "^7.12.5" + } + }, "react-error-overlay": { "version": "6.0.10", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz", diff --git a/package.json b/package.json index 145e9af..0691659 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "is-lite": "^0.8.1", "react": "^17.0.2", "react-dom": "^17.0.2", + "react-error-boundary": "^3.1.4", "react-helmet-async": "^1.2.3", "react-inlinesvg": "^2.3.0", "react-redux": "^7.2.6", diff --git a/src/components/ErrorHandler.tsx b/src/components/ErrorHandler.tsx new file mode 100644 index 0000000..1bbce2e --- /dev/null +++ b/src/components/ErrorHandler.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { Button, Container, Paragraph } from 'styled-minimal'; + +import Icon from 'components/Icon'; + +interface Props { + error: Error; + resetErrorBoundary: () => void; +} + +export default function ErrorHandler({ error, resetErrorBoundary }: Props) { + const handleClickReset = () => { + resetErrorBoundary(); + }; + + return ( + + + {error.message} + + + + ); +} diff --git a/src/containers/ErrorHandler.tsx b/src/containers/ErrorHandler.tsx deleted file mode 100644 index 36b184d..0000000 --- a/src/containers/ErrorHandler.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react'; -import { Container, Heading, Image } from 'styled-minimal'; - -type Props = { - children?: any; - onError?: (error: Error, componentStack: string) => void; -}; - -type ErrorInfo = { - componentStack: string; -}; - -type State = { - error: Error | null; -}; - -export default class ErrorHandler extends React.Component { - public state: State = { - error: null, - }; - - componentDidCatch(error: Error, info: ErrorInfo) { - const { onError } = this.props; - - /* istanbul ignore else */ - if (typeof onError === 'function') { - try { - onError.call(this, error, info?.componentStack); - } catch { - // ignore - } - } - - this.setState({ error }); - } - - render() { - const { children } = this.props; - const { error } = this.state; - - if (error === null) { - return children; - } - - const message = error.toString(); - - return ( - - Error - - {message} - - - ); - } -} diff --git a/src/index.tsx b/src/index.tsx index 80b9335..f20c5af 100755 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,5 +1,6 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import { ErrorBoundary } from 'react-error-boundary'; import { HelmetProvider } from 'react-helmet-async'; import { Provider } from 'react-redux'; import { PersistGate } from 'redux-persist/lib/integration/react'; @@ -7,9 +8,9 @@ import { configStore } from 'store'; import { showAlert } from 'actions'; +import ErrorHandler from 'components/ErrorHandler'; import Loader from 'components/Loader'; import Reload from 'components/Reload'; -import ErrorHandler from 'containers/ErrorHandler'; import GlobalStyles from 'containers/GlobalStyles'; import reportWebVitals from './reportWebVitals'; @@ -23,11 +24,11 @@ window.store = store; ReactDOM.render( } persistor={persistor}> - + - + , diff --git a/test/components/ErrorHandler.spec.tsx b/test/components/ErrorHandler.spec.tsx new file mode 100644 index 0000000..a585055 --- /dev/null +++ b/test/components/ErrorHandler.spec.tsx @@ -0,0 +1,18 @@ +import React from 'react'; + +import ErrorHandler from 'components/ErrorHandler'; + +import { fireEvent, render, screen } from 'test-utils'; + +const mockResetError = jest.fn(); + +describe('ErrorHandler', () => { + it('should render the error and clicks', () => { + render(); + + fireEvent.click(screen.getByRole('button')); + + expect(screen.getByTestId('ErrorHandler')).toMatchSnapshot(); + expect(mockResetError).toHaveBeenCalledTimes(1); + }); +}); diff --git a/test/components/__snapshots__/ErrorHandler.spec.tsx.snap b/test/components/__snapshots__/ErrorHandler.spec.tsx.snap new file mode 100644 index 0000000..32a93b2 --- /dev/null +++ b/test/components/__snapshots__/ErrorHandler.spec.tsx.snap @@ -0,0 +1,121 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ErrorHandler should render the error and clicks 1`] = ` +.c4 { + box-sizing: border-box; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #f44336; + border: 1px solid #f44336; + color: #fff; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + border-radius: 0px; + box-shadow: none; + cursor: pointer; + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + font-family: inherit; + font-size: 16px; + line-height: 1; + padding: 8px 14px; + -webkit-text-decoration: none; + text-decoration: none; + width: auto; +} + +.c4:disabled { + opacity: 0.7; + pointer-events: none; +} + +.c4:focus { + outline-color: #f44336; +} + +.c0 { + box-sizing: border-box; + margin-left: auto; + margin-right: auto; + padding-left: 16px; + padding-right: 16px; + position: relative; + width: 100%; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + min-height: 100vh; + vertical-align: center; +} + +.c3 { + box-sizing: border-box; + line-height: 1.4; + margin-bottom: 0; + margin-top: 0; + margin-top: 16px; + margin-bottom: 16px; +} + +.c2 + .c2 { + margin-top: 8px; +} + +.c1 { + display: inline-block; + line-height: 0; +} + +.c1 svg { + height: auto; + max-height: 100%; + width: 64px; +} + +@media screen and (min-width:1024px) { + .c0 { + padding-left: 32px; + padding-right: 32px; + } +} + +
+ +

+ Oh No! +

+ +
+`; diff --git a/test/containers/ErrorHandler.spec.tsx b/test/containers/ErrorHandler.spec.tsx deleted file mode 100644 index 0435b2a..0000000 --- a/test/containers/ErrorHandler.spec.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; - -import ErrorHandler from 'containers/ErrorHandler'; - -import { render, screen } from 'test-utils'; - -const mockOnError = jest.fn(); - -describe('ErrorHandler', () => { - const Throws = () => { - throw new Error('Oh no!'); - }; - - it('should handle the error', () => { - const spy = jest.spyOn(console, 'error'); - - spy.mockImplementation(() => undefined); - - render( - - - , - ); - - expect(screen.getByTestId('ErrorHandler')).toMatchSnapshot(); - expect(mockOnError).toHaveBeenCalledTimes(1); - - spy.mockRestore(); - }); -}); diff --git a/test/containers/__snapshots__/ErrorHandler.spec.tsx.snap b/test/containers/__snapshots__/ErrorHandler.spec.tsx.snap deleted file mode 100644 index d3cd356..0000000 --- a/test/containers/__snapshots__/ErrorHandler.spec.tsx.snap +++ /dev/null @@ -1,78 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ErrorHandler should handle the error 1`] = ` -.c0 { - box-sizing: border-box; - margin-left: auto; - margin-right: auto; - padding-left: 16px; - padding-right: 16px; - position: relative; - width: 100%; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; - -webkit-box-pack: center; - -webkit-justify-content: center; - -ms-flex-pack: center; - justify-content: center; - min-height: 100vh; - background-color: #000; - color: #fff; - text-align: center; -} - -.c2 { - box-sizing: border-box; - font-size: 32px; - font-family: inherit; - font-weight: 700; - line-height: 1; - margin-bottom: 0; - margin-top: 8px; - margin-top: 16px; - margin-bottom: 16px; -} - -.c2:first-child { - margin-top: 0; -} - -.c1 { - box-sizing: border-box; - max-width: 100%; - width: 128px; -} - -@media screen and (min-width:1024px) { - .c0 { - padding-left: 32px; - padding-right: 32px; - } -} - -
- Error -

- Error: Oh no! -

-
-`;