From 036e05a30a58883cbec2ee22aa4f68172f6a5634 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 14 May 2018 09:45:58 +0100 Subject: [PATCH 1/6] Blocks: Introduce block definitions and implementations --- blocks/api/registration.js | 16 ++++++++++++++-- blocks/store/actions.js | 14 ++++++++++++++ blocks/store/reducer.js | 23 +++++++++++++++++++++++ blocks/store/selectors.js | 34 +++++++++++++++++++++++++++++----- 4 files changed, 80 insertions(+), 7 deletions(-) diff --git a/blocks/api/registration.js b/blocks/api/registration.js index 77be97707861dd..87366f15c94007 100644 --- a/blocks/api/registration.js +++ b/blocks/api/registration.js @@ -3,7 +3,7 @@ /** * External dependencies */ -import { get, set, isFunction, some } from 'lodash'; +import { get, set, isFunction, some, omit, mapValues, pick } from 'lodash'; /** * WordPress dependencies @@ -168,7 +168,19 @@ export function registerBlockType( name, settings ) { set( settings, [ 'supports', 'multiple' ], ! settings.useOnce ); } - dispatch( 'core/blocks' ).addBlockTypes( settings ); + const blockTypeDefinition = omit( settings, [ 'transforms', 'edit', 'save', 'icon', 'getEditWrapperProps' ] ); + blockTypeDefinition.attributes = mapValues( + settings.attributes, + ( attribute ) => pick( attribute, [ 'type', 'default' ] ) + ); + const blockTypeImplementation = pick( settings, [ 'name', 'transforms', 'edit', 'save', 'icon', 'getEditWrapperProps' ] ); + blockTypeImplementation.attributes = mapValues( + settings.attributes, + ( attribute ) => omit( attribute, [ 'type', 'default' ] ) + ); + + dispatch( 'core/blocks' ).addBlockTypes( blockTypeDefinition ); + dispatch( 'core/blocks' ).implementBlockTypes( blockTypeImplementation ); return settings; } diff --git a/blocks/store/actions.js b/blocks/store/actions.js index a97ab4e1d52e3d..c743f81d47623a 100644 --- a/blocks/store/actions.js +++ b/blocks/store/actions.js @@ -17,6 +17,20 @@ export function addBlockTypes( blockTypes ) { }; } +/** + * Returns an action object used in signalling that block types have been added. + * + * @param {Array|Object} implementations Block types implementations. + * + * @return {Object} Action object. + */ +export function implementBlockTypes( implementations ) { + return { + type: 'IMPLEMENT_BLOCK_TYPES', + implementations: castArray( implementations ), + }; +} + /** * Returns an action object used to remove a registered block type. * diff --git a/blocks/store/reducer.js b/blocks/store/reducer.js index c1623ae38692db..32581a3fb29148 100644 --- a/blocks/store/reducer.js +++ b/blocks/store/reducer.js @@ -43,6 +43,28 @@ export function blockTypes( state = {}, action ) { return state; } +/** + * Reducer managing the block type implementations + * + * @param {Object} state Current state. + * @param {Object} action Dispatched action. + * + * @return {Object} Updated state. + */ +export function implementations( state = {}, action ) { + switch ( action.type ) { + case 'IMPLEMENT_BLOCK_TYPES': + return { + ...state, + ...keyBy( action.implementations, 'name' ), + }; + case 'REMOVE_BLOCK_TYPES': + return omit( state, action.names ); + } + + return state; +} + /** * Higher-order Reducer creating a reducer keeping track of given block name. * @@ -91,6 +113,7 @@ export function categories( state = DEFAULT_CATEGORIES, action ) { export default combineReducers( { blockTypes, + implementations, defaultBlockName, fallbackBlockName, categories, diff --git a/blocks/store/selectors.js b/blocks/store/selectors.js index bf6541ae3a1950..9d76f5bb285e55 100644 --- a/blocks/store/selectors.js +++ b/blocks/store/selectors.js @@ -2,7 +2,7 @@ * External dependencies */ import createSelector from 'rememo'; -import { filter, includes, map } from 'lodash'; +import { filter, includes, map, mapValues, compact } from 'lodash'; /** * Returns all the available block types. @@ -12,9 +12,10 @@ import { filter, includes, map } from 'lodash'; * @return {Array} Block Types. */ export const getBlockTypes = createSelector( - ( state ) => Object.values( state.blockTypes ), + ( state ) => compact( Object.values( state.blockTypes ).map( ( { name } ) => getBlockType( state, name ) ) ), ( state ) => [ state.blockTypes, + state.implementations, ] ); @@ -26,9 +27,32 @@ export const getBlockTypes = createSelector( * * @return {Object?} Block Type. */ -export function getBlockType( state, name ) { - return state.blockTypes[ name ]; -} +export const getBlockType = createSelector( + ( state, name ) => { + const blockTypeDefinition = state.blockTypes[ name ]; + const blockTypeImplementation = state.implementations[ name ]; + + if ( ! blockTypeDefinition || ! blockTypeImplementation ) { + return null; + } + + return { + ...blockTypeDefinition, + ...blockTypeImplementation, + attributes: mapValues( blockTypeDefinition.attributes, ( attribute, key ) => { + const implementationAttribute = blockTypeImplementation.attributes ? blockTypeImplementation.attributes[ key ] : {}; + return { + ...attribute, + ...implementationAttribute, + }; + } ), + }; + }, + ( state ) => [ + state.blockTypes, + state.implementations, + ] +); /** * Returns all the available categories. From a691fc70f81decdfc5db12a7cd89994ab6ebf569 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 14 May 2018 10:00:26 +0100 Subject: [PATCH 2/6] Use the new API for the video block --- core-blocks/index.js | 7 ++++++- core-blocks/video/index.js | 20 +++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/core-blocks/index.js b/core-blocks/index.js index eb19acfe8b10bf..9b2b48a83ce29a 100644 --- a/core-blocks/index.js +++ b/core-blocks/index.js @@ -6,6 +6,7 @@ import { setDefaultBlockName, setUnknownTypeHandlerName, } from '@wordpress/blocks'; +import { dispatch } from '@wordpress/data'; /** * Internal dependencies @@ -77,11 +78,15 @@ export const registerCoreBlocks = () => { table, textColumns, verse, - video, ].forEach( ( { name, settings } ) => { registerBlockType( name, settings ); } ); + [ video ].forEach( ( { name, definition, implementation } ) => { + dispatch( 'core/blocks' ).addBlockTypes( { name, ...definition } ); + dispatch( 'core/blocks' ).implementBlockTypes( { name, ...implementation } ); + } ); + setDefaultBlockName( paragraph.name ); setUnknownTypeHandlerName( freeform.name ); }; diff --git a/core-blocks/video/index.js b/core-blocks/video/index.js index 0c946bce4e67a2..365b69accf5892 100644 --- a/core-blocks/video/index.js +++ b/core-blocks/video/index.js @@ -16,27 +16,33 @@ import edit from './edit'; export const name = 'core/video'; -export const settings = { +export const definition = { title: __( 'Video' ), - description: __( 'Embed an video file and a simple video player.' ), - - icon: 'format-video', - category: 'common', - attributes: { id: { type: 'number', }, src: { type: 'string', + }, + caption: { + type: 'array', + }, + }, +}; + +export const implementation = { + icon: 'format-video', + + attributes: { + src: { source: 'attribute', selector: 'video', attribute: 'src', }, caption: { - type: 'array', source: 'children', selector: 'figcaption', }, From feb06001b74bff691c706bdb4b402c045c693155 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 17 May 2018 09:24:26 +0100 Subject: [PATCH 3/6] Clarify implementation only attributes --- blocks/api/registration.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/blocks/api/registration.js b/blocks/api/registration.js index 87366f15c94007..fe7a862cf5992d 100644 --- a/blocks/api/registration.js +++ b/blocks/api/registration.js @@ -168,12 +168,13 @@ export function registerBlockType( name, settings ) { set( settings, [ 'supports', 'multiple' ], ! settings.useOnce ); } - const blockTypeDefinition = omit( settings, [ 'transforms', 'edit', 'save', 'icon', 'getEditWrapperProps' ] ); + const implementationOnlyAttributes = [ 'transforms', 'edit', 'save', 'icon', 'getEditWrapperProps' ]; + const blockTypeDefinition = omit( settings, implementationOnlyAttributes ); blockTypeDefinition.attributes = mapValues( settings.attributes, ( attribute ) => pick( attribute, [ 'type', 'default' ] ) ); - const blockTypeImplementation = pick( settings, [ 'name', 'transforms', 'edit', 'save', 'icon', 'getEditWrapperProps' ] ); + const blockTypeImplementation = pick( settings, [ 'name' ].concat( implementationOnlyAttributes ) ); blockTypeImplementation.attributes = mapValues( settings.attributes, ( attribute ) => omit( attribute, [ 'type', 'default' ] ) From 3d1fd0cd335e1d28dee87e609db7d473ef7c995b Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 17 May 2018 09:31:05 +0100 Subject: [PATCH 4/6] Small changes per review --- blocks/store/actions.js | 2 +- blocks/store/selectors.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/blocks/store/actions.js b/blocks/store/actions.js index c743f81d47623a..dafdde6fb4b936 100644 --- a/blocks/store/actions.js +++ b/blocks/store/actions.js @@ -18,7 +18,7 @@ export function addBlockTypes( blockTypes ) { } /** - * Returns an action object used in signalling that block types have been added. + * Returns an action object used in signalling that block types have been implemented. * * @param {Array|Object} implementations Block types implementations. * diff --git a/blocks/store/selectors.js b/blocks/store/selectors.js index 9d76f5bb285e55..f898f692c2af4b 100644 --- a/blocks/store/selectors.js +++ b/blocks/store/selectors.js @@ -2,7 +2,7 @@ * External dependencies */ import createSelector from 'rememo'; -import { filter, includes, map, mapValues, compact } from 'lodash'; +import { filter, includes, map, mapValues, compact, get } from 'lodash'; /** * Returns all the available block types. @@ -40,7 +40,7 @@ export const getBlockType = createSelector( ...blockTypeDefinition, ...blockTypeImplementation, attributes: mapValues( blockTypeDefinition.attributes, ( attribute, key ) => { - const implementationAttribute = blockTypeImplementation.attributes ? blockTypeImplementation.attributes[ key ] : {}; + const implementationAttribute = get( blockTypeImplementation.attributes, [ key ], {} ); return { ...attribute, ...implementationAttribute, From 44ab9938a91329822c9431eb65489ed141e53252 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 17 May 2018 09:34:00 +0100 Subject: [PATCH 5/6] Fix unit tests --- core-blocks/video/test/index.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/core-blocks/video/test/index.js b/core-blocks/video/test/index.js index a947f278a58826..1319a91177855e 100644 --- a/core-blocks/video/test/index.js +++ b/core-blocks/video/test/index.js @@ -1,11 +1,27 @@ +/** + * External dependencies + */ +import { get, mapValues } from 'lodash'; + /** * Internal dependencies */ -import { name, settings } from '../'; +import { name, definition, implementation } from '../'; import { blockEditRender } from '../../test/helpers'; describe( 'core/video', () => { test( 'block edit matches snapshot', () => { + const settings = { + ...definition, + ...implementation, + attributes: mapValues( definition.attributes, ( attribute, key ) => { + const implementationAttribute = get( implementation.attributes, [ key ], {} ); + return { + ...attribute, + ...implementationAttribute, + }; + } ), + }; const wrapper = blockEditRender( name, settings ); expect( wrapper ).toMatchSnapshot(); From c55f8d944481279e31f7b3e13e4b0223d81628c7 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 21 Jun 2018 10:17:54 +0100 Subject: [PATCH 6/6] Fix unit tests --- blocks/api/test/registration.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/blocks/api/test/registration.js b/blocks/api/test/registration.js index 99e5e1b866a9da..c113dc0feb0840 100644 --- a/blocks/api/test/registration.js +++ b/blocks/api/test/registration.js @@ -214,6 +214,7 @@ describe( 'blocks', () => { fill="red" stroke="blue" strokeWidth="10" /> ), }, + attributes: {}, } ); } ); @@ -233,6 +234,7 @@ describe( 'blocks', () => { icon: { src: 'foo', }, + attributes: {}, } ); } ); @@ -258,6 +260,7 @@ describe( 'blocks', () => { icon: { src: MyTestIcon, }, + attributes: {}, } ); } ); @@ -289,6 +292,7 @@ describe( 'blocks', () => { fill="red" stroke="blue" strokeWidth="10" /> ), }, + attributes: {}, } ); } ); @@ -305,6 +309,7 @@ describe( 'blocks', () => { icon: { src: 'block-default', }, + attributes: {}, } ); } ); @@ -345,6 +350,7 @@ describe( 'blocks', () => { icon: { src: 'block-default', }, + attributes: {}, }, ] ); const oldBlock = unregisterBlockType( 'core/test-block' ); @@ -357,6 +363,7 @@ describe( 'blocks', () => { icon: { src: 'block-default', }, + attributes: {}, } ); expect( getBlockTypes() ).toEqual( [] ); } ); @@ -401,6 +408,7 @@ describe( 'blocks', () => { icon: { src: 'block-default', }, + attributes: {}, } ); } ); @@ -416,6 +424,7 @@ describe( 'blocks', () => { icon: { src: 'block-default', }, + attributes: {}, } ); } ); } ); @@ -438,6 +447,7 @@ describe( 'blocks', () => { icon: { src: 'block-default', }, + attributes: {}, }, { name: 'core/test-block-with-settings', @@ -448,6 +458,7 @@ describe( 'blocks', () => { icon: { src: 'block-default', }, + attributes: {}, }, ] ); } );