From 6aa967ac587f48ee3958b21a119e701fea28c24a Mon Sep 17 00:00:00 2001 From: kopecmi8 Date: Thu, 7 Oct 2021 11:22:47 +0200 Subject: [PATCH] #51 Split view component into View and View.ErrorBoundary (#52) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * #51 Split view component into View and View.ErrorBoundary * #51 Exclude ErrorBoundary to own independent component * #51 Make View.ErrorBoundary functional component * #51 Setup for DOM testing and add test for ErrorBoundary * #51 Downgrade JEST back to original version * #51 Refactor based on review comments * #51 Fix tests and change fallback from ReactChildren to ReactNode * #51 Render children inside View component when VM is not presented * #51 Make resetErrorBoundary readonly Co-authored-by: Michal Kopecký --- package.json | 4 +- .../views/__tests__/errorBoundary.test.tsx | 183 ++++++++++++++++++ packages/views/src/errorBoundary.tsx | 62 ++++++ packages/views/src/view.tsx | 84 +++----- yarn.lock | 144 ++++++++++++++ 5 files changed, 418 insertions(+), 59 deletions(-) create mode 100644 packages/views/__tests__/errorBoundary.test.tsx create mode 100644 packages/views/src/errorBoundary.tsx diff --git a/package.json b/package.json index 952e63de..a9dd9179 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ }, "devDependencies": { "@emanprague/eslint-config": "^1.0.2", + "@testing-library/react": "^12.1.1", "@types/jest": "^24.9.1", "@types/prettier": "^2.0.1", "@types/rimraf": "^3.0.0", @@ -57,6 +58,7 @@ }, "peerDependencies": { "mobx": "^4.15.4", - "react": "^16.13.0" + "react": "^16.13.0", + "react-dom": "^16.13.0" } } diff --git a/packages/views/__tests__/errorBoundary.test.tsx b/packages/views/__tests__/errorBoundary.test.tsx new file mode 100644 index 00000000..5903ae2f --- /dev/null +++ b/packages/views/__tests__/errorBoundary.test.tsx @@ -0,0 +1,183 @@ +/** + * @jest-environment jsdom + */ +import * as React from "react"; +import { fireEvent, render, screen } from "@testing-library/react"; +import { bound } from "@frui.ts/helpers"; +import ErrorBoundary, { ErrorBoundaryProps } from "../src/errorBoundary"; + +function SimulateError({ title }: { title: string }): JSX.Element { + throw new Error(title); +} + +function Test(): JSX.Element { + return ; +} + +class TestApp extends React.Component { + state = { + error: false, + }; + + @bound + protected setError(error: boolean) { + this.setState({ error }); + } + + render() { + return ( + { + this.setError(false); + if (this.props.onReset) { + this.props.onReset(...args); + } + }}> + {this.state.error ? : this.props.children} + + + ); + } +} + +describe("ErrorBoundary", () => { + it("renders children correctly when there is no error", () => { + const { container } = render( + Error Component}> +

children

+
+ ); + + expect(container.innerHTML).toBe("

children

"); + }); + + it("renders 'Something went wrong :-(' if not given `fallback` prop", () => { + const renderResult = render( + + + + ); + + expect(renderResult.container.innerHTML).toBe("

Something went wrong :-(

"); + }); + + it("renders a fallback string when error occur", () => { + const renderResult = render( + + + + ); + + expect(renderResult.container.innerHTML).toBe("Error occur"); + }); + + it("renders a fallback component on error", () => { + const { container } = render( + Error Component}> + + + ); + expect(container.innerHTML).toBe("

Error Component

"); + }); + + it("renders a fallback component when error occur", async () => { + const { container } = render( + You have hit an error

}> +

children

+
+ ); + + expect(container.innerHTML).toContain("

children

"); + + const btn = screen.getByTestId("raiseErrorBtn"); + fireEvent.click(btn); + + expect(container.innerHTML).not.toContain("

children

"); + expect(container.innerHTML).toBe("

You have hit an error

"); + }); + + it("renders fallback as function", async () => { + let errorString = ""; + let componentStackString = ""; + const { container } = render( + { + errorString = error.toString(); + componentStackString = errorInfo?.componentStack || ""; + return
Fallback here
; + }}> +

children

+
+ ); + + expect(container.innerHTML).toContain("

children

"); + + const btn = screen.getByTestId("raiseErrorBtn"); + fireEvent.click(btn); + + expect(container.innerHTML).not.toContain("

children

"); + expect(container.innerHTML).toBe("
Fallback here
"); + expect(errorString).toBe("Error: errorMessage"); + /* + in SimulateError + in Test + in ErrorBoundary + in TestApp + */ + expect(componentStackString).toMatch( + /\s*(in SimulateError)([a-z]|[A-Z]|\(|\)| )*\s*(in Test)([a-z]|[A-Z]|\(|\)| )*\s*(in ErrorBoundary)([a-z]|[A-Z]|\(|\)| )*\s*(in TestApp)([a-z]|[A-Z]|\(|\)| )*/g + ); + }); + + it("renders children component after reset from error", async () => { + const { container } = render( + { + return ( + + ); + }}> +

children

+
+ ); + + expect(container.innerHTML).toContain("

children

"); + + const btn = screen.getByTestId("raiseErrorBtn"); + fireEvent.click(btn); + + expect(container.innerHTML).not.toContain("

children

"); + expect(container.innerHTML).toContain(''); + + const resetErrorBtn = screen.getByTestId("resetErrorBtn"); + fireEvent.click(resetErrorBtn); + + expect(container.innerHTML).toContain("

children

"); + }); + + it("calls `componentDidCatch() when an error occurs`", () => { + const mockOnError = jest.fn(); + render( + You have hit an error

} onError={mockOnError}> +

children

+
+ ); + + expect(mockOnError).toHaveBeenCalledTimes(0); + + const btn = screen.getByTestId("raiseErrorBtn"); + fireEvent.click(btn); + + expect(mockOnError).toHaveBeenCalledTimes(1); + expect(mockOnError).toHaveBeenCalledWith(expect.any(Error), expect.any(Object)); + }); +}); diff --git a/packages/views/src/errorBoundary.tsx b/packages/views/src/errorBoundary.tsx new file mode 100644 index 00000000..f46644db --- /dev/null +++ b/packages/views/src/errorBoundary.tsx @@ -0,0 +1,62 @@ +import * as React from "react"; + +export type FallbackRender = (errorData: { + error: Error; + errorInfo: React.ErrorInfo | null; + resetError(): void; +}) => React.ReactNode; + +export interface ErrorBoundaryProps { + onError?: (error: Error, errorInfo: React.ErrorInfo) => void; + /** Called if resetError() is called from the fallback render props function */ + onReset?(error: Error | null, errorInfo: React.ErrorInfo | null): void; + fallback?: React.ReactNode | FallbackRender; +} + +interface ErrorBoundaryState { + error: Error | null; + errorInfo: React.ErrorInfo | null; +} + +const INITIAL_STATE = Object.freeze({ + errorInfo: null, + error: null, +}); + +export default class ErrorBoundary extends React.PureComponent { + state: ErrorBoundaryState = INITIAL_STATE; + + public readonly resetErrorBoundary = () => { + const { onReset } = this.props; + + if (onReset) { + const { error, errorInfo } = this.state; + onReset(error, errorInfo); + } + + this.setState(INITIAL_STATE); + }; + + componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { + this.props.onError?.(error, errorInfo); + + this.setState({ error, errorInfo }); + } + + render() { + if (this.state.error) { + const { error, errorInfo } = this.state; + const { fallback } = this.props; + const element = + typeof fallback === "function" ? fallback({ error, errorInfo, resetError: this.resetErrorBoundary }) : fallback; + + if (element !== undefined) { + return element; + } + + return

Something went wrong :-(

; + } + + return {this.props.children}; + } +} diff --git a/packages/views/src/view.tsx b/packages/views/src/view.tsx index 846878cd..882a8a0b 100644 --- a/packages/views/src/view.tsx +++ b/packages/views/src/view.tsx @@ -1,81 +1,49 @@ -import { isActivatable, isDeactivatable } from "@frui.ts/screens"; import * as React from "react"; +import ErrorBoundary, { ErrorBoundaryProps } from "./errorBoundary"; +import useScreenLifecycle from "./useScreenLifecycle"; import { getView, tryGetView } from "./viewLocator"; interface ViewProps { vm: any; context?: string; useLifecycle?: boolean; - fallbackMode?: "message" | "children"; - onError?: (error: Error, errorInfo: React.ErrorInfo) => void; } -interface ViewState { - hasError: boolean; -} - -export default class View extends React.PureComponent { - static defaultProps: Partial = { - fallbackMode: "children", - }; - - static getDerivedStateFromError(error: any) { - return { hasError: true }; - } +const PureView: React.FunctionComponent = props => { + const { vm, children, context, useLifecycle } = props; - componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { - this.props.onError?.(error, errorInfo); + if (!vm) { + return {children}; } - componentDidMount() { - this.tryActivateViewModel(); - } + const FoundView = children === undefined ? getView(vm.constructor, context) : tryGetView(vm.constructor, context); - componentDidUpdate(prevProps: ViewProps) { - if (prevProps.vm !== this.props.vm) { - this.tryActivateViewModel(); - } + if (!FoundView) { + return {children}; } - componentWillUnmount() { - if (this.props.useLifecycle) { - const { vm } = this.props; - if (vm && isDeactivatable(vm)) { - vm.deactivate(true); - } - } + if (!!useLifecycle) { + useScreenLifecycle(vm); } - render() { - if (this.state?.hasError) { - return

Something went wrong :-(

; - } + return ; +}; - const { vm, context, fallbackMode, children } = this.props; +PureView.displayName = "View"; - if (!vm) { - return {children}; - } +const ViewWithErrorBoundary: React.FunctionComponent = props => { + const { onError, onReset, fallback, ...rest } = props; - const FoundView = fallbackMode ? tryGetView(vm.constructor, context) : getView(vm.constructor, context); + return ( + + + + ); +}; - if (!FoundView) { - return fallbackMode === "message" ? ( -

Could not find a view for {vm.constructor.name}

- ) : ( - {children} - ); - } +ViewWithErrorBoundary.displayName = "View.ErrorBoundary"; - return ; - } +const View = PureView as React.FunctionComponent & { ErrorBoundary: typeof ViewWithErrorBoundary }; +View.ErrorBoundary = ViewWithErrorBoundary; - protected tryActivateViewModel() { - if (this.props.useLifecycle) { - const { vm } = this.props; - if (vm && isActivatable(vm)) { - vm.activate(); - } - } - } -} +export default View; diff --git a/yarn.lock b/yarn.lock index f5dbb7e9..62684eb7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,6 +9,13 @@ dependencies: "@babel/highlight" "^7.8.3" +"@babel/code-frame@^7.10.4": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" + integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== + dependencies: + "@babel/highlight" "^7.14.5" + "@babel/core@^7.1.0": version "7.8.7" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.8.7.tgz#b69017d221ccdeb203145ae9da269d72cf102f3b" @@ -68,6 +75,11 @@ dependencies: "@babel/types" "^7.8.3" +"@babel/helper-validator-identifier@^7.14.5": + version "7.15.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" + integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== + "@babel/helper-validator-identifier@^7.9.0": version "7.9.0" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz#ad53562a7fc29b3b9a91bbf7d10397fd146346ed" @@ -82,6 +94,15 @@ "@babel/traverse" "^7.8.4" "@babel/types" "^7.8.3" +"@babel/highlight@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" + integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== + dependencies: + "@babel/helper-validator-identifier" "^7.14.5" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/highlight@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" @@ -103,6 +124,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/runtime-corejs3@^7.10.2": + version "7.15.4" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.15.4.tgz#403139af262b9a6e8f9ba04a6fdcebf8de692bf1" + integrity sha512-lWcAqKeB624/twtTc3w6w/2o9RqJPaNBhPGK6DKLSiwuVWC7WFkypWyNg+CpZoyJH0jVzv1uMtXZ/5/lQOLtCg== + dependencies: + core-js-pure "^3.16.0" + regenerator-runtime "^0.13.4" + "@babel/runtime-corejs3@^7.8.3": version "7.9.2" resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.9.2.tgz#26fe4aa77e9f1ecef9b776559bbb8e84d34284b7" @@ -111,6 +140,13 @@ core-js-pure "^3.0.0" regenerator-runtime "^0.13.4" +"@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5": + version "7.15.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" + integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.4.0", "@babel/template@^7.8.3", "@babel/template@^7.8.6": version "7.8.6" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" @@ -388,6 +424,17 @@ "@types/istanbul-reports" "^1.1.1" "@types/yargs" "^13.0.0" +"@jest/types@^27.2.4": + version "27.2.4" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.2.4.tgz#2430042a66e00dc5b140c3636f4474d464c21ee8" + integrity sha512-IDO2ezTxeMvQAHxzG/ZvEyA47q0aVfzT95rGFl7bZs/Go0aIucvfDbS2rmnoEdXxlLQhcolmoG/wvL/uKx4tKA== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^16.0.0" + chalk "^4.0.0" + "@lerna/add@3.21.0": version "3.21.0" resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.21.0.tgz#27007bde71cc7b0a2969ab3c2f0ae41578b4577b" @@ -1179,6 +1226,33 @@ dependencies: "@types/node" ">= 8" +"@testing-library/dom@^8.0.0": + version "8.7.1" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.7.1.tgz#c17b1e90e682c7945b2d88ce2f078e522c775ce3" + integrity sha512-nMqxP8qPHgYAiIPMVb6zko5DInPJ/LdILgrkWB5HBJhBCIU5avso8pU9IafnXVGLRq+LDtbvo0LuVbjEo27BgQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^4.2.0" + aria-query "^4.2.2" + chalk "^4.1.0" + dom-accessibility-api "^0.5.6" + lz-string "^1.4.4" + pretty-format "^27.0.2" + +"@testing-library/react@^12.1.1": + version "12.1.1" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.1.tgz#e693943aa48d0190099acdc3928a751d73bcf7d5" + integrity sha512-JDyWbvMuedEpP6SPL4Cvbhk59TVxQ3pwuR6ZfJHdRsHuxDd/ziSMA3nVM3fViaSbsQhuQFE/mvFrPrvQbL5kRQ== + dependencies: + "@babel/runtime" "^7.12.5" + "@testing-library/dom" "^8.0.0" + +"@types/aria-query@^4.2.0": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc" + integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig== + "@types/babel__core@^7.1.0": version "7.1.6" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.6.tgz#16ff42a5ae203c9af1c6e190ed1f30f83207b610" @@ -1256,6 +1330,13 @@ "@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-report" "*" +"@types/istanbul-reports@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" + integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + dependencies: + "@types/istanbul-lib-report" "*" + "@types/jest@^24.9.1": version "24.9.1" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.9.1.tgz#02baf9573c78f1b9974a5f36778b366aa77bd534" @@ -1308,6 +1389,13 @@ dependencies: "@types/yargs-parser" "*" +"@types/yargs@^16.0.0": + version "16.0.4" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" + integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== + dependencies: + "@types/yargs-parser" "*" + "@typescript-eslint/eslint-plugin-tslint@^2.18.0": version "2.34.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin-tslint/-/eslint-plugin-tslint-2.34.0.tgz#0a2cea8e9a0726ae5a42cecd94095b41b749a8e7" @@ -1482,6 +1570,11 @@ ansi-regex@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -1497,6 +1590,11 @@ ansi-styles@^4.1.0: "@types/color-name" "^1.1.1" color-convert "^2.0.1" +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + any-promise@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" @@ -1540,6 +1638,14 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +aria-query@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b" + integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA== + dependencies: + "@babel/runtime" "^7.10.2" + "@babel/runtime-corejs3" "^7.10.2" + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -1949,6 +2055,14 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -2241,6 +2355,11 @@ core-js-pure@^3.0.0: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.4.tgz#4bf1ba866e25814f149d4e9aaa08c36173506e3a" integrity sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw== +core-js-pure@^3.16.0: + version "3.18.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.18.1.tgz#097d34d24484be45cea700a448d1e74622646c80" + integrity sha512-kmW/k8MaSuqpvA1xm2l3TVlBuvW+XBkcaOroFUpO3D4lsTGQWBTb/tBDCf/PNkkPLrwgrkQRIYNPB0CeqGJWGQ== + core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -2488,6 +2607,11 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-accessibility-api@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.7.tgz#8c2aa6325968f2933160a0b7dbb380893ddf3e7d" + integrity sha512-ml3lJIq9YjUfM9TUnEPvEYWFSwivwIGBPKpewX7tii7fwCazA8yCioGdqQcNsItPpfFvSJ3VIdMQPj60LJhcQA== + domexception@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" @@ -4665,6 +4789,11 @@ lunr@^2.3.8: resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.8.tgz#a8b89c31f30b5a044b97d2d28e2da191b6ba2072" integrity sha512-oxMeX/Y35PNFuZoHp+jUj5OSEmLCaIH4KTFJh7a93cHBoFmpw2IoPs22VIz7vyO2YUnx2Tn9dzIwO2P/4quIRg== +lz-string@^1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" + integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY= + macos-release@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.3.0.tgz#eb1930b036c0800adebccd5f17bc4c12de8bb71f" @@ -5615,6 +5744,16 @@ pretty-format@^24.9.0: ansi-styles "^3.2.0" react-is "^16.8.4" +pretty-format@^27.0.2: + version "27.2.4" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.2.4.tgz#08ea39c5eab41b082852d7093059a091f6ddc748" + integrity sha512-NUjw22WJHldzxyps2YjLZkUj6q1HvjqFezkB9Y2cklN8NtVZN/kZEXGZdFw4uny3oENzV5EEMESrkI0YDUH8vg== + dependencies: + "@jest/types" "^27.2.4" + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + pretty-hrtime@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" @@ -5739,6 +5878,11 @@ react-is@^16.8.1, react-is@^16.8.4: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + read-cmd-shim@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.5.tgz#87e43eba50098ba5a32d0ceb583ab8e43b961c16"