TD-790: Add error boundary (#256)

This commit is contained in:
Ildar Galeev 2023-10-24 15:03:00 +03:00 committed by GitHub
parent d0aa41e3b8
commit 37095b3010
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 195 additions and 113 deletions

247
package-lock.json generated
View File

@ -22,6 +22,7 @@
"md5": "2.3.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-error-boundary": "4.0.11",
"react-hook-form": "7.45.1",
"react-svg": "16.1.18",
"styled-components": "6.0.4",
@ -247,15 +248,52 @@
}
},
"node_modules/@babel/code-frame": {
"version": "7.22.5",
"license": "MIT",
"version": "7.22.13",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
"integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
"dependencies": {
"@babel/highlight": "^7.22.5"
"@babel/highlight": "^7.22.13",
"chalk": "^2.4.2"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/code-frame/node_modules/ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dependencies": {
"color-convert": "^1.9.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/code-frame/node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/code-frame/node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/compat-data": {
"version": "7.22.9",
"license": "MIT",
@ -313,10 +351,11 @@
}
},
"node_modules/@babel/generator": {
"version": "7.22.9",
"license": "MIT",
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
"integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
"dependencies": {
"@babel/types": "^7.22.5",
"@babel/types": "^7.23.0",
"@jridgewell/gen-mapping": "^0.3.2",
"@jridgewell/trace-mapping": "^0.3.17",
"jsesc": "^2.5.1"
@ -444,18 +483,20 @@
}
},
"node_modules/@babel/helper-environment-visitor": {
"version": "7.22.5",
"license": "MIT",
"version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
"integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-function-name": {
"version": "7.22.5",
"license": "MIT",
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
"integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
"dependencies": {
"@babel/template": "^7.22.5",
"@babel/types": "^7.22.5"
"@babel/template": "^7.22.15",
"@babel/types": "^7.23.0"
},
"engines": {
"node": ">=6.9.0"
@ -593,8 +634,9 @@
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.22.5",
"license": "MIT",
"version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
"engines": {
"node": ">=6.9.0"
}
@ -631,11 +673,12 @@
}
},
"node_modules/@babel/highlight": {
"version": "7.22.5",
"license": "MIT",
"version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
"integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
"dependencies": {
"@babel/helper-validator-identifier": "^7.22.5",
"chalk": "^2.0.0",
"@babel/helper-validator-identifier": "^7.22.20",
"chalk": "^2.4.2",
"js-tokens": "^4.0.0"
},
"engines": {
@ -644,7 +687,8 @@
},
"node_modules/@babel/highlight/node_modules/ansi-styles": {
"version": "3.2.1",
"license": "MIT",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dependencies": {
"color-convert": "^1.9.0"
},
@ -654,7 +698,8 @@
},
"node_modules/@babel/highlight/node_modules/chalk": {
"version": "2.4.2",
"license": "MIT",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
@ -664,13 +709,10 @@
"node": ">=4"
}
},
"node_modules/@babel/highlight/node_modules/js-tokens": {
"version": "4.0.0",
"license": "MIT"
},
"node_modules/@babel/highlight/node_modules/supports-color": {
"version": "5.5.0",
"license": "MIT",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dependencies": {
"has-flag": "^3.0.0"
},
@ -679,8 +721,9 @@
}
},
"node_modules/@babel/parser": {
"version": "7.22.7",
"license": "MIT",
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
"integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
"bin": {
"parser": "bin/babel-parser.js"
},
@ -1960,29 +2003,31 @@
}
},
"node_modules/@babel/template": {
"version": "7.22.5",
"license": "MIT",
"version": "7.22.15",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
"dependencies": {
"@babel/code-frame": "^7.22.5",
"@babel/parser": "^7.22.5",
"@babel/types": "^7.22.5"
"@babel/code-frame": "^7.22.13",
"@babel/parser": "^7.22.15",
"@babel/types": "^7.22.15"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
"version": "7.22.8",
"license": "MIT",
"version": "7.23.2",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
"integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
"dependencies": {
"@babel/code-frame": "^7.22.5",
"@babel/generator": "^7.22.7",
"@babel/helper-environment-visitor": "^7.22.5",
"@babel/helper-function-name": "^7.22.5",
"@babel/code-frame": "^7.22.13",
"@babel/generator": "^7.23.0",
"@babel/helper-environment-visitor": "^7.22.20",
"@babel/helper-function-name": "^7.23.0",
"@babel/helper-hoist-variables": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6",
"@babel/parser": "^7.22.7",
"@babel/types": "^7.22.5",
"@babel/parser": "^7.23.0",
"@babel/types": "^7.23.0",
"debug": "^4.1.0",
"globals": "^11.1.0"
},
@ -1991,11 +2036,12 @@
}
},
"node_modules/@babel/types": {
"version": "7.22.5",
"license": "MIT",
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
"integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
"dependencies": {
"@babel/helper-string-parser": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.20",
"to-fast-properties": "^2.0.0"
},
"engines": {
@ -4250,14 +4296,21 @@
"license": "MIT"
},
"node_modules/color-convert": {
"version": "1.9.1",
"license": "MIT",
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dependencies": {
"color-name": "^1.1.1"
"color-name": "1.1.3"
}
},
"node_modules/color-convert/node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
},
"node_modules/color-name": {
"version": "1.1.4",
"dev": true,
"license": "MIT"
},
"node_modules/combined-stream": {
@ -4720,7 +4773,8 @@
},
"node_modules/escape-string-regexp": {
"version": "1.0.5",
"license": "MIT",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"engines": {
"node": ">=0.8.0"
}
@ -5674,7 +5728,8 @@
},
"node_modules/has-flag": {
"version": "3.0.0",
"license": "MIT",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"engines": {
"node": ">=4"
}
@ -7198,8 +7253,9 @@
}
},
"node_modules/js-tokens": {
"version": "3.0.2",
"license": "MIT"
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"node_modules/js-yaml": {
"version": "3.13.1",
@ -7926,6 +7982,33 @@
"node": ">= 6"
}
},
"node_modules/postcss": {
"version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"dependencies": {
"nanoid": "^3.3.6",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/prelude-ls": {
"version": "1.1.2",
"dev": true,
@ -8070,6 +8153,17 @@
"react": "^18.2.0"
}
},
"node_modules/react-error-boundary": {
"version": "4.0.11",
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.11.tgz",
"integrity": "sha512-U13ul67aP5DOSPNSCWQ/eO0AQEYzEFkVljULQIjMV0KlffTAhxuDoBKdO0pb/JZ8mDhMKFZ9NZi0BmLGUiNphw==",
"dependencies": {
"@babel/runtime": "^7.12.5"
},
"peerDependencies": {
"react": ">=16.13.1"
}
},
"node_modules/react-hook-form": {
"version": "7.45.1",
"license": "MIT",
@ -8711,32 +8805,6 @@
"version": "0.8.1",
"license": "MIT"
},
"node_modules/styled-components/node_modules/postcss": {
"version": "8.4.26",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.6",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/styled-components/node_modules/tslib": {
"version": "2.6.0",
"license": "0BSD"
@ -9478,33 +9546,6 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/vite/node_modules/postcss": {
"version": "8.4.27",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.6",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/w3c-xmlserializer": {
"version": "4.0.0",
"dev": true,

View File

@ -29,6 +29,7 @@
"md5": "2.3.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-error-boundary": "4.0.11",
"react-hook-form": "7.45.1",
"react-svg": "16.1.18",
"styled-components": "6.0.4",

View File

@ -1,9 +1,11 @@
import { motion } from 'framer-motion';
import { lazy, useContext, useEffect, useMemo, useState } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import styled from 'styled-components';
import { InvoiceChangeType } from 'checkout/backend';
import { ModalName, ResultFormInfo, ResultType } from 'checkout/hooks';
import { ErrorBoundaryFallback } from 'checkout/components/ui';
import { ModalName, ModalState, ResultFormInfo, ResultType } from 'checkout/hooks';
import { PayableInvoiceData, useInvoiceEvents, useModal } from 'checkout/hooks';
import isNil from 'checkout/utils/is-nil';
@ -21,6 +23,16 @@ const Container = styled.div`
position: relative;
`;
const Modals = ({ modalState }: { modalState: ModalState[] }) => {
const activeModalName = useMemo(() => modalState.find((modal) => modal.active).name, [modalState]);
return (
<ErrorBoundary fallback={<ErrorBoundaryFallback />}>
{activeModalName === ModalName.modalForms && <Modal />}
{activeModalName === ModalName.modalInteraction && <UserInteractionModal />}
</ErrorBoundary>
);
};
const ModalContainer = () => {
const {
appConfig: { capiEndpoint },
@ -113,8 +125,6 @@ const ModalContainer = () => {
toInteractionState(interactionModel);
}, [interactionModel]);
const activeModalName = useMemo(() => modalState.find((modal) => modal.active).name, [modalState]);
return (
<motion.div animate={{ opacity: 1 }} initial={{ opacity: 0 }} transition={{ duration: 1 }}>
<Container>
@ -129,8 +139,8 @@ const ModalContainer = () => {
}}
>
<PayableInvoiceContext.Provider value={{ payableInvoiceData, setPayableInvoiceData }}>
{activeModalName === ModalName.modalForms && <Modal />}
{activeModalName === ModalName.modalInteraction && <UserInteractionModal />}
{/* eslint-disable-next-line react/jsx-max-depth */}
<Modals modalState={modalState}></Modals>
</PayableInvoiceContext.Provider>
</ModalContext.Provider>
</Container>

View File

@ -1,7 +1,9 @@
import { motion } from 'framer-motion';
import { lazy, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import styled from 'styled-components';
import { ErrorBoundaryFallback } from 'checkout/components/ui';
import { FormName, ModalForms, ModalName, SlideDirection } from 'checkout/hooks';
import { findNamed } from 'checkout/utils';
import { device } from 'checkout/utils/device';
@ -128,7 +130,7 @@ export const FormContainer = () => {
initial={{ x: toInitialPos(slideDirection) }}
transition={{ duration: 0.3 }}
>
{renderForm(formName, onMount)}
<ErrorBoundary fallback={<ErrorBoundaryFallback />}>{renderForm(formName, onMount)}</ErrorBoundary>
{inProcess && <FormLoader />}
</motion.div>
</Form>

View File

@ -1,4 +1,4 @@
import { useContext, useEffect } from 'react';
import { useContext, useEffect, startTransition } from 'react';
import styled from 'styled-components';
import { FormName, PaymentTerminalFormInfo, PaymentTerminalSelectorFormInfo } from 'checkout/hooks';
@ -37,9 +37,11 @@ const PaymentTerminalSelectorForm = ({ onMount }: { onMount: () => void }) => {
{serviceProviders && (
<ServiceProvidersGrid
serviceProviders={serviceProviders}
onPaneClick={(providerID) =>
goToFormInfo(new PaymentTerminalFormInfo(providerID, FormName.paymentTerminalSelector))
}
onPaneClick={(providerID) => {
startTransition(() => {
goToFormInfo(new PaymentTerminalFormInfo(providerID, FormName.paymentTerminalSelector));
});
}}
/>
)}
</Container>

View File

@ -0,0 +1,25 @@
import styled from 'styled-components';
import { Button } from './button';
const Container = styled.div`
display: flex;
flex-direction: column;
gap: 48px;
background-color: ${({ theme }) => theme.form.background};
`;
const Message = styled.p`
margin: 0;
font-size: 16px;
color: ${({ theme }) => theme.font.primaryColor};
`;
export const ErrorBoundaryFallback = () => (
<Container>
<Message>Something went wrong. Try reloading.</Message>
<Button color="primary" onClick={() => location.reload()}>
Reload
</Button>
</Container>
);

View File

@ -10,3 +10,4 @@ export * from './hr';
export * from './copy-to-clipboard-button';
export * from './select';
export * from './chevron-button';
export * from './error-boundary-fallback';