diff --git a/blocks/api/factory.js b/blocks/api/factory.js
index 796c60fac2309c..7beaf4b596163d 100644
--- a/blocks/api/factory.js
+++ b/blocks/api/factory.js
@@ -46,11 +46,6 @@ export function createBlock( name, blockAttributes = {} ) {
return result;
}, {} );
- // Keep the anchor if the block supports it
- if ( blockType.supportAnchor && blockAttributes.anchor ) {
- attributes.anchor = blockAttributes.anchor;
- }
-
// Keep the className if the block supports it
if ( blockType.className !== false && blockAttributes.className ) {
attributes.className = blockAttributes.className;
diff --git a/blocks/api/index.js b/blocks/api/index.js
index 30a3dadb11f0a2..4cb6c022af85da 100644
--- a/blocks/api/index.js
+++ b/blocks/api/index.js
@@ -19,4 +19,5 @@ export {
getDefaultBlockName,
getBlockType,
getBlockTypes,
+ hasBlockSupport,
} from './registration';
diff --git a/blocks/api/parser.js b/blocks/api/parser.js
index ff78f55f0b8fa4..159f8c9ecdb7bc 100644
--- a/blocks/api/parser.js
+++ b/blocks/api/parser.js
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import { parse as hpqParse, attr } from 'hpq';
+import { parse as hpqParse } from 'hpq';
import { mapValues, reduce, pickBy } from 'lodash';
/**
@@ -148,11 +148,6 @@ export function getBlockAttributes( blockType, innerHTML, attributes ) {
return result;
}, {} );
- // If the block supports anchor, parse the id
- if ( blockType.supportAnchor ) {
- blockAttributes.anchor = hpqParse( innerHTML, attr( '*', 'id' ) );
- }
-
// If the block supports a custom className parse it
if ( blockType.className !== false && attributes && attributes.className ) {
blockAttributes.className = attributes.className;
diff --git a/blocks/api/registration.js b/blocks/api/registration.js
index ed6c4fa1a436c6..b220d9778f9317 100644
--- a/blocks/api/registration.js
+++ b/blocks/api/registration.js
@@ -10,6 +10,11 @@ import { get, isFunction, some } from 'lodash';
*/
import { getCategories } from './categories';
+/**
+ * Internal dependencies
+ */
+import { applyFilters } from '../hooks';
+
/**
* Block settings keyed by block name.
*
@@ -113,13 +118,15 @@ export function registerBlockType( name, settings ) {
if ( ! settings.icon ) {
settings.icon = 'block-default';
}
- const block = blocks[ name ] = {
+ settings = {
name,
attributes: get( window._wpBlocksAttributes, name ),
...settings,
};
- return block;
+ settings = applyFilters( 'registerBlockType', settings, name );
+
+ return blocks[ name ] = settings;
}
/**
@@ -196,3 +203,23 @@ export function getBlockType( name ) {
export function getBlockTypes() {
return Object.values( blocks );
}
+
+/**
+ * Returns true if the block defines support for a feature, or false otherwise
+ *
+ * @param {(String|Object)} nameOrType Block name or type object
+ * @param {String} feature Feature to test
+ * @param {Boolean} defaultSupports Whether feature is supported by
+ * default if not explicitly defined
+ * @return {Boolean} Whether block supports feature
+ */
+export function hasBlockSupport( nameOrType, feature, defaultSupports ) {
+ const blockType = 'string' === typeof nameOrType ?
+ getBlockType( nameOrType ) :
+ nameOrType;
+
+ return !! get( blockType, [
+ 'supports',
+ feature,
+ ], defaultSupports );
+}
diff --git a/blocks/api/serializer.js b/blocks/api/serializer.js
index 2c6980810e9c37..4c88e5275fb110 100644
--- a/blocks/api/serializer.js
+++ b/blocks/api/serializer.js
@@ -14,6 +14,7 @@ import { Component, createElement, renderToString, cloneElement, Children } from
* Internal dependencies
*/
import { getBlockType, getUnknownTypeHandlerName } from './registration';
+import { applyFilters } from '../hooks';
/**
* Returns the block's default classname from its name
@@ -55,7 +56,7 @@ export function getSaveContent( blockType, attributes ) {
return element;
}
- const extraProps = {};
+ const extraProps = applyFilters( 'getSaveContent.extraProps', {}, blockType, attributes );
if ( !! className ) {
const updatedClassName = classnames(
className,
@@ -65,10 +66,6 @@ export function getSaveContent( blockType, attributes ) {
extraProps.className = updatedClassName;
}
- if ( blockType.supportAnchor && attributes.anchor ) {
- extraProps.id = attributes.anchor;
- }
-
return cloneElement( element, extraProps );
};
const contentWithClassname = Children.map( saveContent, addAdvancedAttributes );
diff --git a/blocks/api/test/factory.js b/blocks/api/test/factory.js
index d5a71698c29b10..893d1ec59e74f3 100644
--- a/blocks/api/test/factory.js
+++ b/blocks/api/test/factory.js
@@ -57,30 +57,6 @@ describe( 'block factory', () => {
expect( typeof block.uid ).toBe( 'string' );
} );
- it( 'should keep the anchor if the block supports it', () => {
- registerBlockType( 'core/test-block', {
- attributes: {
- align: {
- type: 'string',
- },
- },
- save: noop,
- category: 'common',
- title: 'test block',
- supportAnchor: true,
- } );
- const block = createBlock( 'core/test-block', {
- align: 'left',
- anchor: 'chicken',
- } );
-
- expect( block.attributes ).toEqual( {
- anchor: 'chicken',
- align: 'left',
- } );
- expect( block.isValid ).toBe( true );
- } );
-
it( 'should keep the className if the block supports it', () => {
registerBlockType( 'core/test-block', {
attributes: {},
diff --git a/blocks/api/test/parser.js b/blocks/api/test/parser.js
index 9d248cf6501fda..cd3cf20177b3f7 100644
--- a/blocks/api/test/parser.js
+++ b/blocks/api/test/parser.js
@@ -157,26 +157,6 @@ describe( 'block parser', () => {
} );
} );
- it( 'should parse the anchor if the block supports it', () => {
- const blockType = {
- attributes: {
- content: {
- type: 'string',
- source: text( 'div' ),
- },
- },
- supportAnchor: true,
- };
-
- const innerHTML = '
Ribs
';
- const attrs = {};
-
- expect( getBlockAttributes( blockType, innerHTML, attrs ) ).toEqual( {
- content: 'Ribs',
- anchor: 'chicken',
- } );
- } );
-
it( 'should parse the className if the block supports it', () => {
const blockType = {
attributes: {},
diff --git a/blocks/api/test/registration.js b/blocks/api/test/registration.js
index 82ecf564af60e6..4411d93276e607 100644
--- a/blocks/api/test/registration.js
+++ b/blocks/api/test/registration.js
@@ -17,6 +17,7 @@ import {
getDefaultBlockName,
getBlockType,
getBlockTypes,
+ hasBlockSupport,
} from '../registration';
describe( 'blocks', () => {
@@ -272,4 +273,67 @@ describe( 'blocks', () => {
] );
} );
} );
+
+ describe( 'hasBlockSupport', () => {
+ it( 'should return false if block has no supports', () => {
+ registerBlockType( 'core/test-block', defaultBlockSettings );
+
+ expect( hasBlockSupport( 'core/test-block', 'foo' ) ).toBe( false );
+ } );
+
+ it( 'should return false if block does not define support by name', () => {
+ registerBlockType( 'core/test-block', {
+ ...defaultBlockSettings,
+ supports: {
+ bar: true,
+ },
+ } );
+
+ expect( hasBlockSupport( 'core/test-block', 'foo' ) ).toBe( false );
+ } );
+
+ it( 'should return custom default supports if block does not define support by name', () => {
+ registerBlockType( 'core/test-block', {
+ ...defaultBlockSettings,
+ supports: {
+ bar: true,
+ },
+ } );
+
+ expect( hasBlockSupport( 'core/test-block', 'foo', true ) ).toBe( true );
+ } );
+
+ it( 'should return true if block type supports', () => {
+ registerBlockType( 'core/test-block', {
+ ...defaultBlockSettings,
+ supports: {
+ foo: true,
+ },
+ } );
+
+ expect( hasBlockSupport( 'core/test-block', 'foo' ) ).toBe( true );
+ } );
+
+ it( 'should return true if block author defines unsupported but truthy value', () => {
+ registerBlockType( 'core/test-block', {
+ ...defaultBlockSettings,
+ supports: {
+ foo: 'hmmm',
+ },
+ } );
+
+ expect( hasBlockSupport( 'core/test-block', 'foo' ) ).toBe( true );
+ } );
+
+ it( 'should handle block settings object as argument to test', () => {
+ const settings = {
+ ...defaultBlockSettings,
+ supports: {
+ foo: true,
+ },
+ };
+
+ expect( hasBlockSupport( settings, 'foo' ) ).toBe( true );
+ } );
+ } );
} );
diff --git a/blocks/api/test/serializer.js b/blocks/api/test/serializer.js
index 7a64ebd33e3e7f..71f116fd77eec0 100644
--- a/blocks/api/test/serializer.js
+++ b/blocks/api/test/serializer.js
@@ -121,20 +121,6 @@ describe( 'block serializer', () => {
expect( saved ).toBe( 'Bananas
' );
} );
-
- it( 'should add an id if the block supports anchors', () => {
- const saved = getSaveContent(
- {
- save: ( { attributes } ) => createElement( 'div', null, attributes.fruit ),
- supportAnchor: true,
- name: 'myplugin/fruit',
- className: false,
- },
- { fruit: 'Bananas', anchor: 'my-fruit' }
- );
-
- expect( saved ).toBe( 'Bananas
' );
- } );
} );
describe( 'component save', () => {
diff --git a/blocks/block-edit/index.js b/blocks/block-edit/index.js
new file mode 100644
index 00000000000000..ee1cb389cb47bf
--- /dev/null
+++ b/blocks/block-edit/index.js
@@ -0,0 +1,26 @@
+/**
+ * Internal dependencies
+ */
+import { getBlockType } from '../api';
+import { applyFilters } from '../hooks';
+
+function BlockEdit( props ) {
+ const { name, ...editProps } = props;
+ const blockType = getBlockType( name );
+
+ if ( ! blockType ) {
+ return null;
+ }
+
+ // `edit` and `save` are functions or components describing the markup
+ // with which a block is displayed. If `blockType` is valid, assign
+ // them preferencially as the render value for the block.
+ let Edit;
+ if ( blockType ) {
+ Edit = blockType.edit || blockType.save;
+ }
+
+ return applyFilters( 'BlockEdit', , props );
+}
+
+export default BlockEdit;
diff --git a/blocks/block-edit/test/index.js b/blocks/block-edit/test/index.js
new file mode 100644
index 00000000000000..c24648e6f47959
--- /dev/null
+++ b/blocks/block-edit/test/index.js
@@ -0,0 +1,56 @@
+/**
+ * External dependencies
+ */
+import { shallow } from 'enzyme';
+import { noop } from 'lodash';
+
+/**
+ * Internal dependencies
+ */
+import BlockEdit from '../';
+import {
+ registerBlockType,
+ unregisterBlockType,
+ getBlockTypes,
+} from '../../api';
+
+describe( 'BlockEdit', () => {
+ afterEach( () => {
+ getBlockTypes().forEach( ( block ) => {
+ unregisterBlockType( block.name );
+ } );
+ } );
+
+ it( 'should return null if block type not defined', () => {
+ const wrapper = shallow( );
+
+ expect( wrapper.type() ).toBe( null );
+ } );
+
+ it( 'should use edit implementation of block', () => {
+ const edit = () => ;
+ registerBlockType( 'core/test-block', {
+ save: noop,
+ category: 'common',
+ title: 'block title',
+ edit,
+ } );
+
+ const wrapper = shallow( );
+
+ expect( wrapper.type() ).toBe( edit );
+ } );
+
+ it( 'should use save implementation of block as fallback', () => {
+ const save = () => ;
+ registerBlockType( 'core/test-block', {
+ save,
+ category: 'common',
+ title: 'block title',
+ } );
+
+ const wrapper = shallow( );
+
+ expect( wrapper.type() ).toBe( save );
+ } );
+} );
diff --git a/blocks/hooks/anchor.js b/blocks/hooks/anchor.js
new file mode 100644
index 00000000000000..00274520932e8b
--- /dev/null
+++ b/blocks/hooks/anchor.js
@@ -0,0 +1,99 @@
+/**
+ * External dependencies
+ */
+import { assign } from 'lodash';
+
+/**
+ * WordPress dependencies
+ */
+import { cloneElement } from '@wordpress/element';
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import { source, hasBlockSupport } from '../api';
+import InspectorControls from '../inspector-controls';
+
+/**
+ * Regular expression matching invalid anchor characters for replacement.
+ *
+ * @type {RegExp}
+ */
+const ANCHOR_REGEX = /[\s#]/g;
+
+/**
+ * Filters registered block settings, extending attributes with anchor using ID
+ * of the first node
+ *
+ * @param {Object} settings Original block settings
+ * @return {Object} Filtered block settings
+ */
+export function addAttribute( settings ) {
+ if ( hasBlockSupport( settings, 'anchor' ) ) {
+ // Use Lodash's assign to gracefully handle if attributes are undefined
+ settings.attributes = assign( settings.attributes, {
+ anchor: {
+ type: 'string',
+ source: source.attr( '*', 'id' ),
+ },
+ } );
+ }
+
+ return settings;
+}
+
+/**
+ * Override the default edit UI to include a new block inspector control for
+ * assigning the anchor ID, if block supports anchor
+ *
+ * @param {Element} element Original edit element
+ * @param {Object} props Props passed to BlockEdit
+ * @return {Element} Filtered edit element
+ */
+export function addInspectorControl( element, props ) {
+ if ( hasBlockSupport( props.name, 'anchor' ) && props.focus ) {
+ element = [
+ cloneElement( element, { key: 'edit' } ),
+
+ {
+ nextValue = nextValue.replace( ANCHOR_REGEX, '-' );
+
+ props.setAttributes( {
+ anchor: nextValue,
+ } );
+ } } />
+ ,
+ ];
+ }
+
+ return element;
+}
+
+/**
+ * Override props assigned to save component to inject anchor ID, if block
+ * supports anchor. This is only applied if the block's save result is an
+ * element and not a markup string.
+ *
+ * @param {Object} extraProps Additional props applied to save element
+ * @param {Object} blockType Block type
+ * @param {Object} attributes Current block attributes
+ * @return {Object} Filtered props applied to save element
+ */
+export function addSaveProps( extraProps, blockType, attributes ) {
+ if ( hasBlockSupport( blockType, 'anchor' ) ) {
+ extraProps.id = attributes.anchor;
+ }
+
+ return extraProps;
+}
+
+export default function anchor( { addFilter } ) {
+ addFilter( 'registerBlockType', 'core\anchor-attribute', addAttribute );
+ addFilter( 'BlockEdit', 'core\anchor-inspector-control', addInspectorControl );
+ addFilter( 'getSaveContent.extraProps', 'core\anchor-save-props', addSaveProps );
+}
diff --git a/blocks/hooks/index.js b/blocks/hooks/index.js
new file mode 100644
index 00000000000000..1a61cfddb51f16
--- /dev/null
+++ b/blocks/hooks/index.js
@@ -0,0 +1,47 @@
+/**
+ * WordPress dependencies
+ */
+import createHooks from '@wordpress/hooks';
+
+/**
+ * Internal dependencies
+ */
+import anchor from './anchor';
+
+const hooks = createHooks();
+
+const {
+ addAction,
+ addFilter,
+ removeAction,
+ removeFilter,
+ removeAllActions,
+ removeAllFilters,
+ doAction,
+ applyFilters,
+ doingAction,
+ doingFilter,
+ didAction,
+ didFilter,
+ hasAction,
+ hasFilter,
+} = hooks;
+
+export {
+ addAction,
+ addFilter,
+ removeAction,
+ removeFilter,
+ removeAllActions,
+ removeAllFilters,
+ doAction,
+ applyFilters,
+ doingAction,
+ doingFilter,
+ didAction,
+ didFilter,
+ hasAction,
+ hasFilter,
+};
+
+anchor( hooks );
diff --git a/blocks/hooks/test/anchor.js b/blocks/hooks/test/anchor.js
new file mode 100644
index 00000000000000..1fcecf44d6a0d5
--- /dev/null
+++ b/blocks/hooks/test/anchor.js
@@ -0,0 +1,79 @@
+/**
+ * External dependencies
+ */
+import { noop } from 'lodash';
+
+/**
+ * External dependencies
+ */
+import createHooks from '@wordpress/hooks';
+
+/**
+ * Internal dependencies
+ */
+import anchor from '../anchor';
+
+describe( 'anchor', () => {
+ const hooks = createHooks();
+
+ let blockSettings;
+ beforeEach( () => {
+ anchor( hooks );
+
+ blockSettings = {
+ save: noop,
+ category: 'common',
+ title: 'block title',
+ };
+ } );
+
+ afterEach( () => {
+ hooks.removeAllFilters( 'registerBlockType' );
+ hooks.removeAllFilters( 'getSaveContent.extraProps' );
+ } );
+
+ describe( 'addAttribute()', () => {
+ const addAttribute = hooks.applyFilters.bind( null, 'registerBlockType' );
+
+ it( 'should do nothing if the block settings do not define anchor support', () => {
+ const settings = addAttribute( blockSettings );
+
+ expect( settings.attributes ).toBe( undefined );
+ } );
+
+ it( 'should assign a new anchor attribute', () => {
+ const settings = addAttribute( {
+ ...blockSettings,
+ supports: {
+ anchor: true,
+ },
+ } );
+
+ expect( settings.attributes ).toHaveProperty( 'anchor' );
+ } );
+ } );
+
+ describe( 'addSaveProps', () => {
+ const addSaveProps = hooks.applyFilters.bind( null, 'getSaveContent.extraProps' );
+
+ it( 'should do nothing if the block settings do not define anchor support', () => {
+ const attributes = { anchor: 'foo' };
+ const extraProps = addSaveProps( blockSettings, attributes );
+
+ expect( extraProps ).not.toHaveProperty( 'id' );
+ } );
+
+ it( 'should inject anchor attribute ID', () => {
+ const attributes = { anchor: 'foo' };
+ blockSettings = {
+ ...blockSettings,
+ supports: {
+ anchor: true,
+ },
+ };
+ const extraProps = addSaveProps( {}, blockSettings, attributes );
+
+ expect( extraProps.id ).toBe( 'foo' );
+ } );
+ } );
+} );
diff --git a/blocks/index.js b/blocks/index.js
index faebbaa6c03fb3..ef56387023922d 100644
--- a/blocks/index.js
+++ b/blocks/index.js
@@ -13,10 +13,12 @@ import './library';
// Blocks are inferred from the HTML source of a post through a parsing mechanism
// and then stored as objects in state, from which it is then rendered for editing.
export * from './api';
+export * from './hooks';
export { default as AlignmentToolbar } from './alignment-toolbar';
export { default as BlockAlignmentToolbar } from './block-alignment-toolbar';
export { default as BlockControls } from './block-controls';
export { default as BlockDescription } from './block-description';
+export { default as BlockEdit } from './block-edit';
export { default as BlockIcon } from './block-icon';
export { default as ColorPalette } from './color-palette';
export { default as Editable } from './editable';
diff --git a/blocks/library/heading/index.js b/blocks/library/heading/index.js
index 5e4f01dc44d5a9..d61c2024565bc9 100644
--- a/blocks/library/heading/index.js
+++ b/blocks/library/heading/index.js
@@ -29,7 +29,9 @@ registerBlockType( 'core/heading', {
className: false,
- supportAnchor: true,
+ supports: {
+ anchor: true,
+ },
attributes: {
content: {
diff --git a/docs/block-api.md b/docs/block-api.md
index fe66d6dcec01a8..1e230e9e3f4bd0 100644
--- a/docs/block-api.md
+++ b/docs/block-api.md
@@ -119,16 +119,17 @@ Whether a block can only be used once per post.
useOnce: true,
```
-#### supportAnchor (optional)
+#### supports (optional)
-* **Type:** `Bool`
-* **Default:** `false`
+* **Type:** `Object`
+
+Optional block extended support features. The following options are supported, and should be specified as a boolean `true` or `false` value:
-Anchors let you link directly to a specific block on a page. This property adds a field to define an id for the block and a button to copy the direct link.
+- `anchor` (default `false`): Anchors let you link directly to a specific block on a page. This property adds a field to define an id for the block and a button to copy the direct link.
```js
// Add the support for an anchor link.
-supportAnchor: true,
+anchor: true,
```
#### supportHTML (optional)
diff --git a/editor/components/block-inspector/advanced-controls.js b/editor/components/block-inspector/advanced-controls.js
index 6df4d92c4da4a7..70f4df8b418714 100644
--- a/editor/components/block-inspector/advanced-controls.js
+++ b/editor/components/block-inspector/advanced-controls.js
@@ -9,29 +9,19 @@ import { connect } from 'react-redux';
import { Component } from '@wordpress/element';
import { getBlockType, InspectorControls } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
-import { ClipboardButton, Tooltip, PanelBody } from '@wordpress/components';
+import { PanelBody } from '@wordpress/components';
/**
* Internal Dependencies
*/
import { updateBlockAttributes } from '../../actions';
import { getSelectedBlock, getCurrentPost } from '../../selectors';
-import { filterURLForDisplay } from '../../utils/url';
-
-/**
- * Internal constants
- */
-const ANCHOR_REGEX = /[\s#]/g;
class BlockInspectorAdvancedControls extends Component {
constructor() {
super( ...arguments );
- this.state = {
- showCopyConfirmation: false,
- };
- this.onCopy = this.onCopy.bind( this );
+
this.setClassName = this.setClassName.bind( this );
- this.setAnchor = this.setAnchor.bind( this );
}
setClassName( className ) {
@@ -39,32 +29,10 @@ class BlockInspectorAdvancedControls extends Component {
setAttributes( selectedBlock.uid, { className } );
}
- setAnchor( anchor ) {
- const { selectedBlock, setAttributes } = this.props;
- setAttributes( selectedBlock.uid, { anchor: anchor.replace( ANCHOR_REGEX, '-' ) } );
- }
-
- componentWillUnmout() {
- clearTimeout( this.dismissCopyConfirmation );
- }
-
- onCopy() {
- this.setState( {
- showCopyConfirmation: true,
- } );
-
- clearTimeout( this.dismissCopyConfirmation );
- this.dismissCopyConfirmation = setTimeout( () => {
- this.setState( {
- showCopyConfirmation: false,
- } );
- }, 4000 );
- }
-
render() {
- const { selectedBlock, post } = this.props;
+ const { selectedBlock } = this.props;
const blockType = getBlockType( selectedBlock.name );
- if ( false === blockType.className && ! blockType.supportAnchor ) {
+ if ( false === blockType.className ) {
return null;
}
@@ -80,24 +48,6 @@ class BlockInspectorAdvancedControls extends Component {
value={ selectedBlock.attributes.className || '' }
onChange={ this.setClassName } />
}
- { blockType.supportAnchor &&
-
-
- { !! post.link && !! selectedBlock.attributes.anchor &&
-
-
-
- { this.state.showCopyConfirmation ? __( 'Copied!' ) : __( 'Copy Link' ) }
-
-
-
- }
-
- }
);
}
diff --git a/editor/modes/visual-editor/block.js b/editor/modes/visual-editor/block.js
index 530810bbe9c695..02cb53d21b4234 100644
--- a/editor/modes/visual-editor/block.js
+++ b/editor/modes/visual-editor/block.js
@@ -10,7 +10,7 @@ import { has, partial, reduce, size } from 'lodash';
*/
import { Component, createElement } from '@wordpress/element';
import { keycodes } from '@wordpress/utils';
-import { getBlockType, getBlockDefaultClassname, createBlock } from '@wordpress/blocks';
+import { getBlockType, BlockEdit, getBlockDefaultClassname, createBlock } from '@wordpress/blocks';
import { __, sprintf } from '@wordpress/i18n';
/**
@@ -318,20 +318,7 @@ class VisualEditorBlock extends Component {
// translators: %s: Type of block (i.e. Text, Image etc)
const blockLabel = sprintf( __( 'Block: %s' ), blockType.title );
// The block as rendered in the editor is composed of general block UI
- // (mover, toolbar, wrapper) and the display of the block content, which
- // is referred to as .
- let BlockEdit;
- // `edit` and `save` are functions or components describing the markup
- // with which a block is displayed. If `blockType` is valid, assign
- // them preferencially as the render value for the block.
- if ( blockType ) {
- BlockEdit = blockType.edit || blockType.save;
- }
-
- // Should `BlockEdit` return as null, we have nothing to display for the block.
- if ( ! BlockEdit ) {
- return null;
- }
+ // (mover, toolbar, wrapper) and the display of the block content.
// Generate the wrapper class names handling the different states of the block.
const { isHovered, isSelected, isMultiSelected, isFirstMultiSelected, focus } = this.props;
@@ -389,6 +376,7 @@ class VisualEditorBlock extends Component {
{ isValid && mode === 'visual' && (