From 4a6d0068bb8171518dafe702b6b2a725a5cb47f4 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 9 Aug 2021 18:44:15 +0400 Subject: [PATCH] Site Editor: Add error boundary (#33921) --- .../edit-site/src/components/editor/index.js | 161 +++++++++--------- .../src/components/error-boundary/index.js | 64 +++++++ packages/edit-site/src/index.js | 26 ++- 3 files changed, 169 insertions(+), 82 deletions(-) create mode 100644 packages/edit-site/src/components/error-boundary/index.js diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index 64ebff5a9612de..c9225340907ba3 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -39,6 +39,7 @@ import NavigationSidebar from '../navigation-sidebar'; import URLQueryController from '../url-query-controller'; import InserterSidebar from '../secondary-sidebar/inserter-sidebar'; import ListViewSidebar from '../secondary-sidebar/list-view-sidebar'; +import ErrorBoundary from '../error-boundary'; import { store as editSiteStore } from '../../store'; const interfaceLabels = { @@ -46,7 +47,7 @@ const interfaceLabels = { drawer: __( 'Navigation Sidebar' ), }; -function Editor( { initialSettings } ) { +function Editor( { initialSettings, onError } ) { const { isInserterOpen, isListViewOpen, @@ -179,8 +180,6 @@ function Editor( { initialSettings } ) { return ( <> - - - - - } - secondarySidebar={ secondarySidebar() } - sidebar={ - sidebarIsOpened && ( - - ) - } - header={ -
- } - notices={ } - content={ - <> - - { template && ( - - ) } - { templateResolved && - ! template && - settings?.siteUrl && - entityId && ( - + + + + + } + secondarySidebar={ secondarySidebar() } + sidebar={ + sidebarIsOpened && ( + + ) + } + header={ +
+ } + notices={ } + content={ + <> + + { template && ( + - { __( - "You attempted to edit an item that doesn't exist. Perhaps it was deleted?" - ) } - + /> ) } - - - } - actions={ - <> - { isEntitiesSavedStatesOpen ? ( - - ) : ( -
- -
- ) } - - } - footer={ } - /> - - + /> + ) : ( +
+ +
+ ) } + + } + footer={ } + /> + + + diff --git a/packages/edit-site/src/components/error-boundary/index.js b/packages/edit-site/src/components/error-boundary/index.js new file mode 100644 index 00000000000000..e425df492405f3 --- /dev/null +++ b/packages/edit-site/src/components/error-boundary/index.js @@ -0,0 +1,64 @@ +/** + * WordPress dependencies + */ +import { Component } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { Button } from '@wordpress/components'; +import { Warning } from '@wordpress/block-editor'; +import { useCopyToClipboard } from '@wordpress/compose'; + +function CopyButton( { text, children } ) { + const ref = useCopyToClipboard( text ); + return ( + + ); +} + +export default class ErrorBoundary extends Component { + constructor() { + super( ...arguments ); + + this.reboot = this.reboot.bind( this ); + + this.state = { + error: null, + }; + } + + static getDerivedStateFromError( error ) { + return { error }; + } + + reboot() { + this.props.onError(); + } + + render() { + const { error } = this.state; + if ( ! error ) { + return this.props.children; + } + + return ( + + { __( 'Attempt Recovery' ) } + , + + { __( 'Copy Error' ) } + , + ] } + > + { __( 'The editor has encountered an unexpected error.' ) } + + ); + } +} diff --git a/packages/edit-site/src/index.js b/packages/edit-site/src/index.js index fe42ad69af26c6..8a33589ad9f604 100644 --- a/packages/edit-site/src/index.js +++ b/packages/edit-site/src/index.js @@ -5,7 +5,7 @@ import { registerCoreBlocks, __experimentalRegisterExperimentalCoreBlocks, } from '@wordpress/block-library'; -import { render } from '@wordpress/element'; +import { render, unmountComponentAtNode } from '@wordpress/element'; import { __experimentalFetchLinkSuggestions as fetchLinkSuggestions } from '@wordpress/core-data'; /** @@ -16,6 +16,23 @@ import './hooks'; import './store'; import Editor from './components/editor'; +/** + * Reinitializes the editor after the user chooses to reboot the editor after + * an unhandled error occurs, replacing previously mounted editor element using + * an initial state from prior to the crash. + * + * @param {Element} target DOM node in which editor is rendered. + * @param {?Object} settings Editor settings object. + */ +export function reinitializeEditor( target, settings ) { + unmountComponentAtNode( target ); + const reboot = reinitializeEditor.bind( null, target, settings ); + render( + , + target + ); +} + /** * Initializes the site editor screen. * @@ -27,6 +44,9 @@ export function initialize( id, settings ) { fetchLinkSuggestions( search, searchOptions, settings ); settings.__experimentalSpotlightEntityBlocks = [ 'core/template-part' ]; + const target = document.getElementById( id ); + const reboot = reinitializeEditor.bind( null, target, settings ); + registerCoreBlocks(); if ( process.env.GUTENBERG_PHASE === 2 ) { __experimentalRegisterExperimentalCoreBlocks( { @@ -35,8 +55,8 @@ export function initialize( id, settings ) { } render( - , - document.getElementById( id ) + , + target ); }