From 1b5ac4098d0571ab54b5ddb95d55615ad82d9be2 Mon Sep 17 00:00:00 2001 From: Grzegorz Ziolkowski Date: Fri, 12 Oct 2018 15:12:43 +0200 Subject: [PATCH 1/2] Blocks: Try to move filtering blocks until editor mounts --- packages/blocks/src/api/index.js | 1 + packages/blocks/src/api/registration.js | 109 ++++++++++-------- packages/edit-post/src/index.js | 45 ++++++++ .../editor/src/components/provider/index.js | 64 +++++++--- 4 files changed, 157 insertions(+), 62 deletions(-) diff --git a/packages/blocks/src/api/index.js b/packages/blocks/src/api/index.js index 127b329df4fe9..446f582a3045e 100644 --- a/packages/blocks/src/api/index.js +++ b/packages/blocks/src/api/index.js @@ -36,6 +36,7 @@ export { getBlockTypes, getBlockSupport, hasBlockSupport, + isBlockDefinitionValid, isReusableBlock, getChildBlockNames, hasChildBlocks, diff --git a/packages/blocks/src/api/registration.js b/packages/blocks/src/api/registration.js index 267ab2443b90d..3867050c84a59 100644 --- a/packages/blocks/src/api/registration.js +++ b/packages/blocks/src/api/registration.js @@ -8,7 +8,7 @@ import { get, isFunction, some } from 'lodash'; /** * WordPress dependencies */ -import { applyFilters, addFilter } from '@wordpress/hooks'; +import { addFilter } from '@wordpress/hooks'; import { select, dispatch } from '@wordpress/data'; /** @@ -53,96 +53,107 @@ export function unstable__bootstrapServerSideBlockDefinitions( definitions ) { / } /** - * Registers a new block provided a unique name and an object defining its - * behavior. Once registered, the block is made available as an option to any - * editor interface where blocks are implemented. + * Checks whether a block's definition is valid. * - * @param {string} name Block name. * @param {Object} settings Block settings. - * - * @return {?WPBlock} The block, if it has been successfully registered; - * otherwise `undefined`. + * @return {boolean} True when block settings are correct. */ -export function registerBlockType( name, settings ) { - settings = { - name, - ...get( serverSideBlockDefinitions, name ), - ...settings, - }; - - if ( typeof name !== 'string' ) { - console.error( - 'Block names must be strings.' - ); - return; - } - if ( ! /^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/.test( name ) ) { - console.error( - 'Block names must contain a namespace prefix, include only lowercase alphanumeric characters or dashes, and start with a letter. Example: my-plugin/my-custom-block' - ); - return; - } - if ( select( 'core/blocks' ).getBlockType( name ) ) { - console.error( - 'Block "' + name + '" is already registered.' - ); - return; - } - - settings = applyFilters( 'blocks.registerBlockType', settings, name ); - +export function isBlockDefinitionValid( settings ) { if ( ! settings || ! isFunction( settings.save ) ) { console.error( 'The "save" property must be specified and must be a valid function.' ); - return; + return false; } if ( 'edit' in settings && ! isFunction( settings.edit ) ) { console.error( 'The "edit" property must be a valid function.' ); - return; + return false; } if ( 'keywords' in settings && settings.keywords.length > 3 ) { console.error( - 'The block "' + name + '" can have a maximum of 3 keywords.' + 'The block "' + settings.name + '" can have a maximum of 3 keywords.' ); - return; + return false; } if ( ! ( 'category' in settings ) ) { console.error( - 'The block "' + name + '" must have a category.' + 'The block "' + settings.name + '" must have a category.' ); - return; + return false; } if ( 'category' in settings && ! some( select( 'core/blocks' ).getCategories(), { slug: settings.category } ) ) { console.error( - 'The block "' + name + '" must have a registered category.' + 'The block "' + settings.name + '" must have a registered category.' ); - return; + return false; } if ( ! ( 'title' in settings ) || settings.title === '' ) { console.error( - 'The block "' + name + '" must have a title.' + 'The block "' + settings.name + '" must have a title.' ); - return; + return false; } if ( typeof settings.title !== 'string' ) { console.error( 'Block titles must be strings.' ); - return; + return false; } - - settings.icon = normalizeIconObject( settings.icon ); if ( ! isValidIcon( settings.icon.src ) ) { console.error( 'The icon passed is invalid. ' + 'The icon should be a string, an element, a function, or an object following the specifications documented in https://wordpress.org/gutenberg/handbook/block-api/#icon-optional' ); + return false; + } + + return true; +} + +/** + * Registers a new block provided a unique name and an object defining its + * behavior. Once registered, the block is made available as an option to any + * editor interface where blocks are implemented. + * + * @param {string} name Block name. + * @param {Object} settings Block settings. + * + * @return {?WPBlock} The block, if it has been successfully registered; + * otherwise `undefined`. + */ +export function registerBlockType( name, settings = {} ) { + if ( typeof name !== 'string' ) { + console.error( + 'Block names must be strings.' + ); + return; + } + if ( ! /^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/.test( name ) ) { + console.error( + 'Block names must contain a namespace prefix, include only lowercase alphanumeric characters or dashes, and start with a letter. Example: my-plugin/my-custom-block' + ); + return; + } + if ( select( 'core/blocks' ).getBlockType( name ) ) { + console.error( + 'Block "' + name + '" is already registered.' + ); + return; + } + + settings = { + name, + ...get( serverSideBlockDefinitions, name ), + ...settings, + icon: normalizeIconObject( settings.icon ), + }; + + if ( ! isBlockDefinitionValid( settings ) ) { return; } diff --git a/packages/edit-post/src/index.js b/packages/edit-post/src/index.js index 9797f877b6c8f..20ed3ce5f373b 100644 --- a/packages/edit-post/src/index.js +++ b/packages/edit-post/src/index.js @@ -9,6 +9,8 @@ import { registerCoreBlocks } from '@wordpress/block-library'; import { render, unmountComponentAtNode } from '@wordpress/element'; import { dispatch } from '@wordpress/data'; import deprecated from '@wordpress/deprecated'; +import { registerBlockType, registerBlockStyle } from '@wordpress/blocks'; +import { addFilter } from '@wordpress/hooks'; /** * Internal dependencies @@ -58,8 +60,51 @@ export function initializeEditor( id, postType, postId, settings, overridePost ) const target = document.getElementById( id ); const reboot = reinitializeEditor.bind( null, postType, postId, target, settings, overridePost ); + // TODO: Remove this later. + registerBlockStyle( 'core/list', { + label: 'Large', + name: 'large', + } ); + registerBlockStyle( 'wp-js-plugin-starter/hello-world', { + label: 'Hello', + name: 'hello', + } ); + addFilter( 'blocks.registerBlockType', 'wp-js-plugin-starter/hello-world/filter-name', ( blockType, name ) => { + if ( name === 'wp-js-plugin-starter/hello-world' ) { + return { + ...blockType, + category: 'nope', + }; + } + + return blockType; + } ); + // TODO: Remove this later. + + // TODO: We no longer need to register core blocks in here. We can do it at any time. registerCoreBlocks(); + // TODO: Remove this later. + registerBlockType( 'wp-js-plugin-starter/hello-world', { + title: 'Hello World', + description: 'Just another Hello World block', + icon: 'admin-site', + category: 'widgets', + + edit: function() { + return ( +

Hello Editor

+ ); + }, + + save: function() { + return ( +

Hello Frontend

+ ); + }, + } ); + // TODO: Remove this later. + dispatch( 'core/nux' ).triggerGuide( [ 'core/editor.inserter', 'core/editor.settings', diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index e238b74d7ad18..f71fe1f53eca2 100644 --- a/packages/editor/src/components/provider/index.js +++ b/packages/editor/src/components/provider/index.js @@ -6,10 +6,13 @@ import { flow, map } from 'lodash'; /** * WordPress Dependencies */ +import { isBlockDefinitionValid, normalizeIconObject } from '@wordpress/blocks'; import { compose } from '@wordpress/compose'; import { createElement, Component } from '@wordpress/element'; import { DropZoneProvider, SlotFillProvider } from '@wordpress/components'; -import { withDispatch } from '@wordpress/data'; +import { withDispatch, withSelect } from '@wordpress/data'; +import { applyFilters } from '@wordpress/hooks'; +import isShallowEqual from '@wordpress/is-shallow-equal'; /** * Internal dependencies @@ -22,12 +25,36 @@ class EditorProvider extends Component { // Assume that we don't need to initialize in the case of an error recovery. if ( ! props.recovery ) { + this.applyFiltersToBlockTypes(); this.props.updateEditorSettings( props.settings ); this.props.updatePostLock( props.settings.postLock ); this.props.setupEditor( props.post, props.settings.autosave ); } } + applyFiltersToBlockTypes() { + const { + addBlockTypes, + blockTypes, + } = this.props; + + const modifiedBlockTypes = blockTypes.map( ( blockType ) => { + const modifiedBlockType = applyFilters( 'blocks.registerBlockType', blockType, blockType.name ); + + if ( isShallowEqual( blockType, modifiedBlockType ) ) { + return null; + } + + modifiedBlockType.icon = normalizeIconObject( modifiedBlockType.icon ); + + return modifiedBlockType; + } ) + .filter( ( blockType ) => blockType !== null ) + .filter( isBlockDefinitionValid ); + + addBlockTypes( modifiedBlockTypes ); + } + componentDidMount() { if ( ! this.props.settings.styles ) { return; @@ -87,15 +114,26 @@ class EditorProvider extends Component { } } -export default withDispatch( ( dispatch ) => { - const { - setupEditor, - updateEditorSettings, - updatePostLock, - } = dispatch( 'core/editor' ); - return { - setupEditor, - updateEditorSettings, - updatePostLock, - }; -} )( EditorProvider ); +export default compose( + withSelect( ( select ) => { + const { getBlockTypes } = select( 'core/blocks' ); + + return { + blockTypes: getBlockTypes(), + }; + } ), + withDispatch( ( dispatch ) => { + const { addBlockTypes } = dispatch( 'core/blocks' ); + const { + setupEditor, + updateEditorSettings, + updatePostLock, + } = dispatch( 'core/editor' ); + return { + addBlockTypes, + setupEditor, + updateEditorSettings, + updatePostLock, + }; + } ) +)( EditorProvider ); From af760e6a09533d22e5a0c755fb37cc553d6b378c Mon Sep 17 00:00:00 2001 From: Grzegorz Ziolkowski Date: Fri, 12 Oct 2018 16:01:35 +0200 Subject: [PATCH 2/2] Blocks: Ensure filters are applied for all newly regitered blocks after editor was initialized --- packages/edit-post/src/index.js | 31 ++++++++++++++++++- .../editor/src/components/provider/index.js | 17 +++++----- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/packages/edit-post/src/index.js b/packages/edit-post/src/index.js index 20ed3ce5f373b..0a8dfd56acaa1 100644 --- a/packages/edit-post/src/index.js +++ b/packages/edit-post/src/index.js @@ -79,9 +79,18 @@ export function initializeEditor( id, postType, postId, settings, overridePost ) return blockType; } ); + addFilter( 'blocks.registerBlockType', 'wp-js-plugin-starter/hello-world2/filter-name', ( blockType, name ) => { + if ( name === 'wp-js-plugin-starter/hello-world2' ) { + return { + ...blockType, + category: 'common', + }; + } + + return blockType; + } ); // TODO: Remove this later. - // TODO: We no longer need to register core blocks in here. We can do it at any time. registerCoreBlocks(); // TODO: Remove this later. @@ -103,6 +112,26 @@ export function initializeEditor( id, postType, postId, settings, overridePost ) ); }, } ); + /* + wp.blocks.registerBlockType( 'wp-js-plugin-starter/hello-world2', { + title: 'Hello World 2', + description: 'Just another Hello World block', + icon: 'admin-site', + category: 'widgets', + + edit: function() { + return ( + 'Hello Editor' + ); + }, + + save: function() { + return ( + 'Hello Frontend' + ); + } + } ); + */ // TODO: Remove this later. dispatch( 'core/nux' ).triggerGuide( [ diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index f71fe1f53eca2..fb17427118f27 100644 --- a/packages/editor/src/components/provider/index.js +++ b/packages/editor/src/components/provider/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { flow, map } from 'lodash'; +import { differenceBy, flow, map } from 'lodash'; /** * WordPress Dependencies @@ -25,18 +25,15 @@ class EditorProvider extends Component { // Assume that we don't need to initialize in the case of an error recovery. if ( ! props.recovery ) { - this.applyFiltersToBlockTypes(); + this.applyFiltersToBlockTypes( props.blockTypes ); this.props.updateEditorSettings( props.settings ); this.props.updatePostLock( props.settings.postLock ); this.props.setupEditor( props.post, props.settings.autosave ); } } - applyFiltersToBlockTypes() { - const { - addBlockTypes, - blockTypes, - } = this.props; + applyFiltersToBlockTypes( blockTypes ) { + const { addBlockTypes } = this.props; const modifiedBlockTypes = blockTypes.map( ( blockType ) => { const modifiedBlockType = applyFilters( 'blocks.registerBlockType', blockType, blockType.name ); @@ -81,6 +78,12 @@ class EditorProvider extends Component { if ( this.props.settings !== prevProps.settings ) { this.props.updateEditorSettings( this.props.settings ); } + if ( this.props.blockTypes !== prevProps.blockTypes ) { + const newBlockTypes = differenceBy( this.props.blockTypes, prevProps.blockTypes, 'name' ); + if ( newBlockTypes.length > 0 ) { + this.applyFiltersToBlockTypes( newBlockTypes ); + } + } } render() {