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 && ( - + { isValid && ! error && ( + + + ) } { ! isValid && ( blockType.save( { @@ -426,6 +439,7 @@ class VisualEditorBlock extends Component { } ) ) } + { !! error && } { ! isValid && } ); diff --git a/editor/modes/visual-editor/create-block-warning.js b/editor/modes/visual-editor/create-block-warning.js new file mode 100644 index 00000000000000..ce2f5607f68959 --- /dev/null +++ b/editor/modes/visual-editor/create-block-warning.js @@ -0,0 +1,11 @@ +/** + * WordPress dependencies + */ +import { Dashicon } from '@wordpress/components'; + +export default ( text ) => ( +
+ +

{ text }

+
+); diff --git a/editor/modes/visual-editor/invalid-block-warning.js b/editor/modes/visual-editor/invalid-block-warning.js index 05ae9910ac5f6c..65cd06b14e8007 100644 --- a/editor/modes/visual-editor/invalid-block-warning.js +++ b/editor/modes/visual-editor/invalid-block-warning.js @@ -1,17 +1,16 @@ /** * WordPress dependencies */ -import { Dashicon } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -const warning = ( -
- -

{ __( - 'This block has been modified externally and has been locked to ' + - 'protect against content loss.' - ) }

-
-); +/** + * Internal dependencies + */ +import createBlockWarning from './create-block-warning'; + +const warning = createBlockWarning( __( + 'This block has been modified externally and has been locked to protect ' + + 'against content loss.' +) ); export default () => warning; diff --git a/editor/modes/visual-editor/style.scss b/editor/modes/visual-editor/style.scss index e51453e0669f68..c76a553d194a32 100644 --- a/editor/modes/visual-editor/style.scss +++ b/editor/modes/visual-editor/style.scss @@ -56,14 +56,14 @@ } } - &.is-invalid .editor-visual-editor__block-edit { + &.has-warning .editor-visual-editor__block-edit { position: relative; min-height: 250px; pointer-events: none; user-select: none; } - &.is-invalid .editor-visual-editor__block-edit::after { + &.has-warning .editor-visual-editor__block-edit::after { content: ''; position: absolute; top: 0; @@ -453,8 +453,8 @@ $sticky-bottom-offset: 20px; } } -.editor-visual-editor__invalid-block-warning { - z-index: z-index( '.editor-visual-editor__invalid-block-warning' ); +.editor-visual-editor__block-warning { + z-index: z-index( '.editor-visual-editor__block-warning' ); position: absolute; top: 50%; left: 50%;