From dd9bf52648d431a7fead7ef3f6b35d63f7e95dad Mon Sep 17 00:00:00 2001 From: Yavor Krastev <4502045+yavorsk@users.noreply.github.com> Date: Tue, 14 May 2024 21:15:33 +0300 Subject: [PATCH] [sitecore-jss-react] ErrorBoundary - Show error message with failed component name in preview mode (#1794) * show error message including component name in preview mode * update unit tests * update change log * fix unit test * log rendering uid to console when component fails in edit/preview/development mode; remove console.logs in unit tests * update console error message --- CHANGELOG.md | 2 +- .../src/components/ErrorBoundary.test.tsx | 55 +++++++++++++++++-- .../src/components/ErrorBoundary.tsx | 20 ++++++- 3 files changed, 67 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c93816b4d7..1cea1443ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ Our versioning strategy is as follows: ### 🎉 New Features & Improvements -* `[sitecore-jss-react]`Introduce ErrorBoundary component. All rendered components are wrapped with it and it will catch client or server side errors from any of its children, display appropriate message and prevent the rest of the application from failing. It accepts and can display custom error component and loading message if it is passed as a prop to parent Placeholder. ([#1786](https://github.com/Sitecore/jss/pull/1786) [#1790](https://github.com/Sitecore/jss/pull/1790) [#1793](https://github.com/Sitecore/jss/pull/1793)) +* `[sitecore-jss-react]`Introduce ErrorBoundary component. All rendered components are wrapped with it and it will catch client or server side errors from any of its children, display appropriate message and prevent the rest of the application from failing. It accepts and can display custom error component and loading message if it is passed as a prop to parent Placeholder. ([#1786](https://github.com/Sitecore/jss/pull/1786) [#1790](https://github.com/Sitecore/jss/pull/1790) [#1793](https://github.com/Sitecore/jss/pull/1793) [#1794](https://github.com/Sitecore/jss/pull/1794)) ## 22.0.0 diff --git a/packages/sitecore-jss-react/src/components/ErrorBoundary.test.tsx b/packages/sitecore-jss-react/src/components/ErrorBoundary.test.tsx index 578e0a8dc3..be5a9fc123 100644 --- a/packages/sitecore-jss-react/src/components/ErrorBoundary.test.tsx +++ b/packages/sitecore-jss-react/src/components/ErrorBoundary.test.tsx @@ -4,16 +4,16 @@ import { mount } from 'enzyme'; import { spy } from 'sinon'; import ErrorBoundary from './ErrorBoundary'; import { SitecoreContextReactContext } from '../components/SitecoreContext'; -import { ComponentRendering } from '@sitecore-jss/sitecore-jss/layout'; +import { ComponentRendering, LayoutServicePageState } from '@sitecore-jss/sitecore-jss/layout'; describe('ErrorBoundary', () => { - describe('when in page editing mode', () => { + describe('when in page editing or preview mode', () => { it('Should render custom error component when custom error component is provided and error is thrown', () => { const setContext = spy(); const testComponentProps = { context: { - pageEditing: true, + pageState: LayoutServicePageState.Preview, }, setContext, }; @@ -42,12 +42,55 @@ describe('ErrorBoundary', () => { expect(rendered.find('div').text()).to.equal('This is a custom error component!'); }); - it('Should render errors message and errored component name when error is thrown', () => { + it('Should render errors message and errored component name when error is thrown in edit mode', () => { const setContext = spy(); const testComponentProps = { context: { - pageEditing: true, + pageState: LayoutServicePageState.Edit, + }, + setContext, + }; + + const testComponentName = 'Test component Name'; + const rendering: ComponentRendering = { componentName: testComponentName }; + + const errorMessage = 'an error occured'; + const TestErrorComponent: React.FC = () => { + throw Error(errorMessage); + }; + + const rendered = mount( + + + + + + ); + + expect(rendered.html()).to.contain('class="sc-jss-placeholder-error"'); + expect(rendered.html()).to.contain('A rendering error occurred in component'); + expect(rendered.find('em').length).to.equal(2); + expect( + rendered + .find('em') + .at(0) + .text() + ).to.equal(testComponentName); + expect( + rendered + .find('em') + .at(1) + .text() + ).to.equal(errorMessage); + }); + + it('Should render errors message and errored component name when error is thrown in preview mode', () => { + const setContext = spy(); + + const testComponentProps = { + context: { + pageState: LayoutServicePageState.Preview, }, setContext, }; @@ -230,7 +273,7 @@ describe('ErrorBoundary', () => { ); - console.log(rendered.html()); + expect(rendered.html()).to.contain('class="sc-jss-placeholder-error"'); expect(rendered.html()).to.contain( 'There was a problem loading this section.' // eslint-disable-line diff --git a/packages/sitecore-jss-react/src/components/ErrorBoundary.tsx b/packages/sitecore-jss-react/src/components/ErrorBoundary.tsx index 67cb3d4a04..cfa36356b8 100644 --- a/packages/sitecore-jss-react/src/components/ErrorBoundary.tsx +++ b/packages/sitecore-jss-react/src/components/ErrorBoundary.tsx @@ -1,5 +1,5 @@ import React, { ReactNode, Suspense } from 'react'; -import { ComponentRendering } from '@sitecore-jss/sitecore-jss/layout'; +import { ComponentRendering, LayoutServicePageState } from '@sitecore-jss/sitecore-jss/layout'; import { withSitecoreContext } from '../enhancers/withSitecoreContext'; import { SitecoreContextValue } from './SitecoreContext'; @@ -19,7 +19,7 @@ export type ErrorBoundaryProps = { }; class ErrorBoundary extends React.Component { - defaultErrorMessage = 'There was a problem loading this section.'; // eslint-disable-line + defaultErrorMessage = 'There was a problem loading this section.'; defaultLoadingMessage = 'Loading component...'; state: { error: Error }; @@ -33,6 +33,12 @@ class ErrorBoundary extends React.Component { } componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { + if (this.showErrorDetails()) { + console.error( + `An error occurred in component ${this.props.rendering?.componentName} (${this.props.rendering?.uid}): ` + ); + } + console.error({ error, errorInfo }); } @@ -40,12 +46,20 @@ class ErrorBoundary extends React.Component { return process.env.NODE_ENV === 'development'; } + showErrorDetails(): boolean { + return ( + this.isInDevMode() || + this.props.sitecoreContext?.pageState === LayoutServicePageState.Edit || + this.props.sitecoreContext?.pageState === LayoutServicePageState.Preview + ); + } + render() { if (this.state.error) { if (this.props.customErrorComponent) { return ; } else { - if (this.isInDevMode() || this.props.sitecoreContext?.pageEditing) { + if (this.showErrorDetails()) { return (