diff --git a/editor/assets/stylesheets/_z-index.scss b/editor/assets/stylesheets/_z-index.scss
index 079552ede7de93..b0d05c71e61e00 100644
--- a/editor/assets/stylesheets/_z-index.scss
+++ b/editor/assets/stylesheets/_z-index.scss
@@ -8,7 +8,7 @@ $z-layers: (
'.editor-visual-editor__block .wp-block-more:before': -1,
'.editor-visual-editor__block {core/image aligned left or right}': 10,
'.editor-visual-editor__block-controls': 1,
- '.editor-visual-editor__invalid-block-warning': 1,
+ '.editor-visual-editor__block-warning': 1,
'.components-form-toggle__input': 1,
'.editor-format-list__menu': 1,
'.editor-inserter__tabs': 1,
diff --git a/editor/modes/visual-editor/block-crash-boundary.js b/editor/modes/visual-editor/block-crash-boundary.js
new file mode 100644
index 00000000000000..08aa7951a1b4fc
--- /dev/null
+++ b/editor/modes/visual-editor/block-crash-boundary.js
@@ -0,0 +1,16 @@
+/**
+ * WordPress dependencies
+ */
+import { Component } from '@wordpress/element';
+
+class BlockCrashBoundary extends Component {
+ componentDidCatch( error ) {
+ this.props.onError( error );
+ }
+
+ render() {
+ return this.props.children;
+ }
+}
+
+export default BlockCrashBoundary;
diff --git a/editor/modes/visual-editor/block-crash-warning.js b/editor/modes/visual-editor/block-crash-warning.js
new file mode 100644
index 00000000000000..bbb3b090a64e21
--- /dev/null
+++ b/editor/modes/visual-editor/block-crash-warning.js
@@ -0,0 +1,15 @@
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import createBlockWarning from './create-block-warning';
+
+const warning = createBlockWarning( __(
+ 'This block has suffered from an unhandled error and cannot be previewed.'
+) );
+
+export default () => warning;
diff --git a/editor/modes/visual-editor/block.js b/editor/modes/visual-editor/block.js
index 818687ee9a74f4..fe2425349bbc87 100644
--- a/editor/modes/visual-editor/block.js
+++ b/editor/modes/visual-editor/block.js
@@ -20,6 +20,8 @@ import { __, sprintf } from '@wordpress/i18n';
* Internal dependencies
*/
import InvalidBlockWarning from './invalid-block-warning';
+import BlockCrashWarning from './block-crash-warning';
+import BlockCrashBoundary from './block-crash-boundary';
import BlockMover from '../../block-mover';
import BlockRightMenu from '../../block-settings-menu';
import BlockSwitcher from '../../block-switcher';
@@ -58,9 +60,7 @@ function FirstChild( { children } ) {
class VisualEditorBlock extends Component {
constructor() {
super( ...arguments );
- this.state = {
- showMobileControls: false,
- };
+
this.bindBlockNode = this.bindBlockNode.bind( this );
this.setAttributes = this.setAttributes.bind( this );
this.maybeHover = this.maybeHover.bind( this );
@@ -74,7 +74,14 @@ class VisualEditorBlock extends Component {
this.onKeyUp = this.onKeyUp.bind( this );
this.handleArrowKey = this.handleArrowKey.bind( this );
this.toggleMobileControls = this.toggleMobileControls.bind( this );
+ this.onBlockError = this.onBlockError.bind( this );
+
this.previousOffset = null;
+
+ this.state = {
+ showMobileControls: false,
+ error: null,
+ };
}
componentDidMount() {
@@ -305,6 +312,10 @@ class VisualEditorBlock extends Component {
} );
}
+ onBlockError( error ) {
+ this.setState( { error } );
+ }
+
render() {
const { block, multiSelectedBlockUids } = this.props;
const { name: blockName, isValid } = block;
@@ -330,9 +341,9 @@ class VisualEditorBlock extends Component {
// Generate the wrapper class names handling the different states of the block.
const { isHovered, isSelected, isMultiSelected, isFirstMultiSelected, focus } = this.props;
const showUI = isSelected && ( ! this.props.isTyping || focus.collapsed === false );
- const { showMobileControls } = this.state;
+ const { error, showMobileControls } = this.state;
const wrapperClassname = classnames( 'editor-visual-editor__block', {
- 'is-invalid': ! isValid,
+ 'has-warning': ! isValid || !! error,
'is-selected': showUI,
'is-multi-selected': isMultiSelected,
'is-hovered': isHovered,
@@ -406,18 +417,20 @@ class VisualEditorBlock extends Component {
onTouchStart={ this.onPointerDown }
className="editor-visual-editor__block-edit"
>
- { isValid && (
-
{ text }
+{ __( - 'This block has been modified externally and has been locked to ' + - 'protect against content loss.' - ) }
-