From 89421b812aaa39675a7005f0567f3a609ceb966d Mon Sep 17 00:00:00 2001 From: Ella van Durpe Date: Tue, 9 Jun 2020 01:00:14 +0300 Subject: [PATCH 1/3] Enable multi select block attribute controls --- .../developers/data/data-core-block-editor.md | 6 +- .../block-controls-toolbar-and-sidebar.md | 3 + .../src/components/block-controls/index.js | 5 +- .../src/components/block-inspector/index.js | 7 +- .../src/components/block-list/block.js | 19 ++++- .../src/components/block-toolbar/index.js | 21 +++-- .../components/inspector-controls/index.js | 5 +- .../use-display-block-controls/index.js | 38 ++++++++++ packages/block-editor/src/store/actions.js | 12 +-- packages/block-editor/src/store/reducer.js | 76 ++++++++++++------- .../block-editor/src/store/test/actions.js | 6 +- .../block-editor/src/store/test/reducer.js | 42 +++++----- 12 files changed, 156 insertions(+), 84 deletions(-) create mode 100644 packages/block-editor/src/components/use-display-block-controls/index.js diff --git a/docs/designers-developers/developers/data/data-core-block-editor.md b/docs/designers-developers/developers/data/data-core-block-editor.md index 58b7bf52e33270..04bbcae93c77cc 100644 --- a/docs/designers-developers/developers/data/data-core-block-editor.md +++ b/docs/designers-developers/developers/data/data-core-block-editor.md @@ -1395,12 +1395,12 @@ _Returns_ # **updateBlockAttributes** -Returns an action object used in signalling that the block attributes with -the specified client ID has been updated. +Returns an action object used in signalling that the multiple blocks' +attributes with the specified client IDs have been updated. _Parameters_ -- _clientId_ `string`: Block client ID. +- _clientIds_ `(string|Array)`: Block client IDs. - _attributes_ `Object`: Block attributes to be merged. _Returns_ diff --git a/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbar-and-sidebar.md b/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbar-and-sidebar.md index 6d9268c3d225fe..b1798ccde55ca9 100644 --- a/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbar-and-sidebar.md +++ b/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbar-and-sidebar.md @@ -187,3 +187,6 @@ If you have settings that affects only selected content inside a block (example: The Block Tab is shown in place of the Document Tab when a block is selected. Similar to rendering a toolbar, if you include an `InspectorControls` element in the return value of your block type's `edit` function, those controls will be shown in the Settings Sidebar region. + +Block controls rendered in both the toolbar and sidebar will also be used when +multiple blocks of the same type are selected. diff --git a/packages/block-editor/src/components/block-controls/index.js b/packages/block-editor/src/components/block-controls/index.js index 28d9d94a1585ae..297d3839df4aa9 100644 --- a/packages/block-editor/src/components/block-controls/index.js +++ b/packages/block-editor/src/components/block-controls/index.js @@ -16,7 +16,7 @@ import { /** * Internal dependencies */ -import { useBlockEditContext } from '../block-edit/context'; +import useDisplayBlockControls from '../use-display-block-controls'; const { Fill, Slot } = createSlotFill( 'BlockControls' ); @@ -26,8 +26,7 @@ function BlockControlsSlot( props ) { } function BlockControlsFill( { controls, children } ) { - const { isSelected } = useBlockEditContext(); - if ( ! isSelected ) { + if ( ! useDisplayBlockControls() ) { return null; } diff --git a/packages/block-editor/src/components/block-inspector/index.js b/packages/block-editor/src/components/block-inspector/index.js index 24a86350e06ff1..37db430c300c09 100644 --- a/packages/block-editor/src/components/block-inspector/index.js +++ b/packages/block-editor/src/components/block-inspector/index.js @@ -32,7 +32,12 @@ const BlockInspector = ( { showNoBlockSelectedMessage = true, } ) => { if ( count > 1 ) { - return ; + return ( +
+ + +
+ ); } const isSelectedBlockUnregistered = diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index 6e8cb32daff44e..f91b5e5223a98d 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -229,6 +229,7 @@ const applyWithSelect = withSelect( getTemplateLock, __unstableGetBlockWithoutInnerBlocks, isNavigationMode, + getMultiSelectedBlockClientIds, } = select( 'core/block-editor' ); const block = __unstableGetBlockWithoutInnerBlocks( clientId ); const isSelected = isBlockSelected( clientId ); @@ -247,6 +248,7 @@ const applyWithSelect = withSelect( // the state. It happens now because the order in withSelect rendering // is not correct. const { name, attributes, isValid } = block || {}; + const isFirstMultiSelected = isFirstMultiSelectedBlock( clientId ); // Do not add new properties here, use `useSelect` instead to avoid // leaking new props to the public API (editor.BlockListBlock filter). @@ -255,9 +257,12 @@ const applyWithSelect = withSelect( isPartOfMultiSelection: isBlockMultiSelected( clientId ) || isAncestorMultiSelected( clientId ), - isFirstMultiSelected: isFirstMultiSelectedBlock( clientId ), + isFirstMultiSelected, isLastMultiSelected: getLastMultiSelectedBlockClientId() === clientId, + multiSelectedClientIds: isFirstMultiSelected + ? getMultiSelectedBlockClientIds() + : undefined, // We only care about this prop when the block is selected // Thus to avoid unnecessary rerenders we avoid updating the prop if @@ -301,8 +306,16 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => { // leaking new props to the public API (editor.BlockListBlock filter). return { setAttributes( newAttributes ) { - const { clientId } = ownProps; - updateBlockAttributes( clientId, newAttributes ); + const { + clientId, + isFirstMultiSelected, + multiSelectedClientIds, + } = ownProps; + const clientIds = isFirstMultiSelected + ? multiSelectedClientIds + : [ clientId ]; + + updateBlockAttributes( clientIds, newAttributes ); }, onInsertBlocks( blocks, index ) { const { rootClientId } = ownProps; diff --git a/packages/block-editor/src/components/block-toolbar/index.js b/packages/block-editor/src/components/block-toolbar/index.js index 8572805766e988..49c0d946ad4ca9 100644 --- a/packages/block-editor/src/components/block-toolbar/index.js +++ b/packages/block-editor/src/components/block-toolbar/index.js @@ -28,7 +28,7 @@ export default function BlockToolbar( { hideDragHandle } ) { blockType, hasFixedToolbar, isValid, - mode, + isVisual, moverDirection, } = useSelect( ( select ) => { const { getBlockType } = select( 'core/blocks' ); @@ -56,14 +56,12 @@ export default function BlockToolbar( { hideDragHandle } ) { getBlockType( getBlockName( selectedBlockClientId ) ), hasFixedToolbar: getSettings().hasFixedToolbar, rootClientId: blockRootClientId, - isValid: - selectedBlockClientIds.length === 1 - ? isBlockValid( selectedBlockClientIds[ 0 ] ) - : null, - mode: - selectedBlockClientIds.length === 1 - ? getBlockMode( selectedBlockClientIds[ 0 ] ) - : null, + isValid: selectedBlockClientIds.every( ( id ) => + isBlockValid( id ) + ), + isVisual: selectedBlockClientIds.every( + ( id ) => getBlockMode( id ) === 'visual' + ), moverDirection: __experimentalMoverDirection, }; }, [] ); @@ -93,7 +91,7 @@ export default function BlockToolbar( { hideDragHandle } ) { return null; } - const shouldShowVisualToolbar = isValid && mode === 'visual'; + const shouldShowVisualToolbar = isValid && isVisual; const isMultiToolbar = blockClientIds.length > 1; const animatedMoverStyles = { @@ -144,8 +142,7 @@ export default function BlockToolbar( { hideDragHandle } ) { ) } - - { shouldShowVisualToolbar && ! isMultiToolbar && ( + { shouldShowVisualToolbar && ( <> { children } : null; + return useDisplayBlockControls() ? { children } : null; } InspectorControls.Slot = Slot; diff --git a/packages/block-editor/src/components/use-display-block-controls/index.js b/packages/block-editor/src/components/use-display-block-controls/index.js new file mode 100644 index 00000000000000..58bb0e18ffa0ac --- /dev/null +++ b/packages/block-editor/src/components/use-display-block-controls/index.js @@ -0,0 +1,38 @@ +/** + * WordPress dependencies + */ +import { useSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { useBlockEditContext } from '../block-edit/context'; + +export default function useDisplayBlockControls() { + const { isSelected, clientId, name } = useBlockEditContext(); + const isFirstAndSameTypeMultiSelected = useSelect( + ( select ) => { + // Don't bother checking, see OR statement below. + if ( isSelected ) { + return; + } + + const { + getBlockName, + isFirstMultiSelectedBlock, + getMultiSelectedBlockClientIds, + } = select( 'core/block-editor' ); + + if ( ! isFirstMultiSelectedBlock( clientId ) ) { + return false; + } + + return getMultiSelectedBlockClientIds().every( + ( id ) => getBlockName( id ) === name + ); + }, + [ clientId, name ] + ); + + return isSelected || isFirstAndSameTypeMultiSelected; +} diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js index ff7874e849c832..89ce0d29b1615d 100644 --- a/packages/block-editor/src/store/actions.js +++ b/packages/block-editor/src/store/actions.js @@ -97,18 +97,18 @@ export function receiveBlocks( blocks ) { } /** - * Returns an action object used in signalling that the block attributes with - * the specified client ID has been updated. + * Returns an action object used in signalling that the multiple blocks' + * attributes with the specified client IDs have been updated. * - * @param {string} clientId Block client ID. - * @param {Object} attributes Block attributes to be merged. + * @param {string|string[]} clientIds Block client IDs. + * @param {Object} attributes Block attributes to be merged. * * @return {Object} Action object. */ -export function updateBlockAttributes( clientId, attributes ) { +export function updateBlockAttributes( clientIds, attributes ) { return { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId, + clientIds: castArray( clientIds ), attributes, }; } diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 223fe269508841..38e6bbde0d7c92 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -211,7 +211,7 @@ export function isUpdatingSameBlockAttribute( action, lastAction ) { action.type === 'UPDATE_BLOCK_ATTRIBUTES' && lastAction !== undefined && lastAction.type === 'UPDATE_BLOCK_ATTRIBUTES' && - action.clientId === lastAction.clientId && + isEqual( action.clientIds, lastAction.clientIds ) && hasSameKeys( action.attributes, lastAction.attributes ) ); } @@ -293,7 +293,6 @@ const withBlockCache = ( reducer ) => ( state = {}, action ) => { break; } case 'UPDATE_BLOCK': - case 'UPDATE_BLOCK_ATTRIBUTES': newState.cache = { ...newState.cache, ...fillKeysWithEmptyObject( @@ -301,6 +300,14 @@ const withBlockCache = ( reducer ) => ( state = {}, action ) => { ), }; break; + case 'UPDATE_BLOCK_ATTRIBUTES': + newState.cache = { + ...newState.cache, + ...fillKeysWithEmptyObject( + getBlocksWithParentsClientIds( action.clientIds ) + ), + }; + break; case 'REPLACE_BLOCKS_AUGMENTED_WITH_CHILDREN': const parentClientIds = fillKeysWithEmptyObject( getBlocksWithParentsClientIds( action.replacedClientIds ) @@ -810,40 +817,45 @@ export const blocks = flow( }, }; - case 'UPDATE_BLOCK_ATTRIBUTES': - // Ignore updates if block isn't known - if ( ! state[ action.clientId ] ) { + case 'UPDATE_BLOCK_ATTRIBUTES': { + // Avoid a state change if none of the block IDs are known. + if ( action.clientIds.every( ( id ) => ! state[ id ] ) ) { return state; } - // Consider as updates only changed values - const nextAttributes = reduce( - action.attributes, - ( result, value, key ) => { - if ( value !== result[ key ] ) { - result = getMutateSafeObject( - state[ action.clientId ], - result - ); - result[ key ] = value; - } - - return result; - }, - state[ action.clientId ] + const next = action.clientIds.reduce( + ( accumulator, id ) => ( { + ...accumulator, + [ id ]: reduce( + action.attributes, + ( result, value, key ) => { + // Consider as updates only changed values. + if ( value !== result[ key ] ) { + result = getMutateSafeObject( + state[ id ], + result + ); + result[ key ] = value; + } + + return result; + }, + state[ id ] + ), + } ), + {} ); - // Skip update if nothing has been changed. The reference will - // match the original block if `reduce` had no changed values. - if ( nextAttributes === state[ action.clientId ] ) { + if ( + action.clientIds.every( + ( id ) => next[ id ] === state[ id ] + ) + ) { return state; } - // Otherwise replace attributes in state - return { - ...state, - [ action.clientId ]: nextAttributes, - }; + return { ...state, ...next }; + } case 'REPLACE_BLOCKS_AUGMENTED_WITH_CHILDREN': if ( ! action.blocks ) { @@ -1541,7 +1553,13 @@ export function lastBlockAttributesChange( state, action ) { return { [ action.clientId ]: action.updates.attributes }; case 'UPDATE_BLOCK_ATTRIBUTES': - return { [ action.clientId ]: action.attributes }; + return action.clientIds.reduce( + ( accumulator, id ) => ( { + ...accumulator, + [ id ]: action.attributes, + } ), + {} + ); } return null; diff --git a/packages/block-editor/src/store/test/actions.js b/packages/block-editor/src/store/test/actions.js index 3cad986cd639be..1e90130293cb34 100644 --- a/packages/block-editor/src/store/test/actions.js +++ b/packages/block-editor/src/store/test/actions.js @@ -46,12 +46,12 @@ describe( 'actions', () => { describe( 'updateBlockAttributes', () => { it( 'should return the UPDATE_BLOCK_ATTRIBUTES action', () => { - const clientId = 'myclientid'; + const clientIds = [ 'myclientid' ]; const attributes = {}; - const result = updateBlockAttributes( clientId, attributes ); + const result = updateBlockAttributes( clientIds, attributes ); expect( result ).toEqual( { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId, + clientIds, attributes, } ); } ); diff --git a/packages/block-editor/src/store/test/reducer.js b/packages/block-editor/src/store/test/reducer.js index aa1b9fe8bcee97..5f5a43eaa7e91b 100644 --- a/packages/block-editor/src/store/test/reducer.js +++ b/packages/block-editor/src/store/test/reducer.js @@ -70,7 +70,7 @@ describe( 'state', () => { it( 'should return false if last action was not updating block attributes', () => { const action = { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189', + clientIds: [ '9db792c6-a25a-495d-adbd-97d56a4c4189' ], attributes: { foo: 10, }, @@ -88,14 +88,14 @@ describe( 'state', () => { it( 'should return false if not updating the same block', () => { const action = { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189', + clientIds: [ '9db792c6-a25a-495d-adbd-97d56a4c4189' ], attributes: { foo: 10, }, }; const previousAction = { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1', + clientIds: [ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' ], attributes: { foo: 20, }, @@ -109,14 +109,14 @@ describe( 'state', () => { it( 'should return false if not updating the same block attributes', () => { const action = { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189', + clientIds: [ '9db792c6-a25a-495d-adbd-97d56a4c4189' ], attributes: { foo: 10, }, }; const previousAction = { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189', + clientIds: [ '9db792c6-a25a-495d-adbd-97d56a4c4189' ], attributes: { bar: 20, }, @@ -130,7 +130,7 @@ describe( 'state', () => { it( 'should return false if no previous action', () => { const action = { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189', + clientIds: [ '9db792c6-a25a-495d-adbd-97d56a4c4189' ], attributes: { foo: 10, }, @@ -145,14 +145,14 @@ describe( 'state', () => { it( 'should return true if updating the same block attributes', () => { const action = { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189', + clientIds: [ '9db792c6-a25a-495d-adbd-97d56a4c4189' ], attributes: { foo: 10, }, }; const previousAction = { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId: '9db792c6-a25a-495d-adbd-97d56a4c4189', + clientIds: [ '9db792c6-a25a-495d-adbd-97d56a4c4189' ], attributes: { foo: 20, }, @@ -1640,7 +1640,7 @@ describe( 'state', () => { ); const state = blocks( original, { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId: 'kumquat', + clientIds: [ 'kumquat' ], attributes: { updated: true, }, @@ -1666,7 +1666,7 @@ describe( 'state', () => { ); const state = blocks( original, { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId: 'kumquat', + clientIds: [ 'kumquat' ], attributes: { updated: true, }, @@ -1692,7 +1692,7 @@ describe( 'state', () => { ); const state = blocks( original, { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId: 'kumquat', + clientIds: [ 'kumquat' ], attributes: { updated: true, }, @@ -1718,7 +1718,7 @@ describe( 'state', () => { ); const state = blocks( original, { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId: 'kumquat', + clientIds: [ 'kumquat' ], attributes: { moreUpdated: true, }, @@ -1739,7 +1739,7 @@ describe( 'state', () => { ); const state = blocks( original, { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId: 'kumquat', + clientIds: [ 'kumquat' ], attributes: { updated: true, }, @@ -1765,7 +1765,7 @@ describe( 'state', () => { ); const state = blocks( original, { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId: 'kumquat', + clientIds: [ 'kumquat' ], attributes: { updated: true, }, @@ -1792,7 +1792,7 @@ describe( 'state', () => { const state = blocks( original, { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId: 'kumquat', + clientIds: [ 'kumquat' ], attributes: { updated: true, }, @@ -1826,7 +1826,7 @@ describe( 'state', () => { const state = blocks( original, { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId: 'kumquat', + clientIds: [ 'kumquat' ], attributes: { updated: false, }, @@ -1850,7 +1850,7 @@ describe( 'state', () => { ); original = blocks( original, { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId: 'kumquat', + clientIds: [ 'kumquat' ], attributes: { updated: false, }, @@ -1858,7 +1858,7 @@ describe( 'state', () => { const state = blocks( original, { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId: 'kumquat', + clientIds: [ 'kumquat' ], attributes: { updated: true, }, @@ -1882,14 +1882,14 @@ describe( 'state', () => { ); original = blocks( original, { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId: 'kumquat', + clientIds: [ 'kumquat' ], attributes: { updated: false, }, } ); original = blocks( original, { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId: 'kumquat', + clientIds: [ 'kumquat' ], attributes: { updated: true, }, @@ -2605,7 +2605,7 @@ describe( 'state', () => { const state = lastBlockAttributesChange( original, { type: 'UPDATE_BLOCK_ATTRIBUTES', - clientId: 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1', + clientIds: [ 'afd1cb17-2c08-4e7a-91be-007ba7ddc3a1' ], attributes: { food: 'banana', }, From d9e3b4ee7639d14082caa4f24141f6572286f633 Mon Sep 17 00:00:00 2001 From: Ella van Durpe Date: Tue, 9 Jun 2020 15:21:04 +0300 Subject: [PATCH 2/3] Add e2e test --- .../multi-block-selection.test.js.snap | 10 ++++++++++ .../editor/various/multi-block-selection.test.js | 14 ++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/packages/e2e-tests/specs/editor/various/__snapshots__/multi-block-selection.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/multi-block-selection.test.js.snap index a227c9f3f56cff..02609fe3594712 100644 --- a/packages/e2e-tests/specs/editor/various/__snapshots__/multi-block-selection.test.js.snap +++ b/packages/e2e-tests/specs/editor/various/__snapshots__/multi-block-selection.test.js.snap @@ -124,6 +124,16 @@ exports[`Multi-block selection should return original focus after failed multi s " `; +exports[`Multi-block selection should set attributes for multiple paragraphs 1`] = ` +" +

1

+ + + +

2

+" +`; + exports[`Multi-block selection should use selection direction to determine vertical edge 1`] = ` "

1
2.

diff --git a/packages/e2e-tests/specs/editor/various/multi-block-selection.test.js b/packages/e2e-tests/specs/editor/various/multi-block-selection.test.js index bbd07d83e510a3..e4537418ea693d 100644 --- a/packages/e2e-tests/specs/editor/various/multi-block-selection.test.js +++ b/packages/e2e-tests/specs/editor/various/multi-block-selection.test.js @@ -8,6 +8,7 @@ import { pressKeyTimes, getEditedPostContent, clickBlockToolbarButton, + clickButton, } from '@wordpress/e2e-test-utils'; async function getSelectedFlatIndices() { @@ -540,4 +541,17 @@ describe( 'Multi-block selection', () => { expect( await getEditedPostContent() ).toMatchSnapshot(); } ); + + it( 'should set attributes for multiple paragraphs', async () => { + await clickBlockAppender(); + await page.keyboard.type( '1' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( '2' ); + await pressKeyWithModifier( 'primary', 'a' ); + await pressKeyWithModifier( 'primary', 'a' ); + await clickBlockToolbarButton( 'Change text alignment' ); + await clickButton( 'Align text center' ); + + expect( await getEditedPostContent() ).toMatchSnapshot(); + } ); } ); From f0b354307c722f3d7db3957c203affdcd47dab07 Mon Sep 17 00:00:00 2001 From: Ella van Durpe Date: Tue, 9 Jun 2020 15:26:18 +0300 Subject: [PATCH 3/3] Add action test --- packages/block-editor/src/store/test/actions.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/store/test/actions.js b/packages/block-editor/src/store/test/actions.js index 1e90130293cb34..d0eb2098adc52f 100644 --- a/packages/block-editor/src/store/test/actions.js +++ b/packages/block-editor/src/store/test/actions.js @@ -45,7 +45,18 @@ describe( 'actions', () => { } ); describe( 'updateBlockAttributes', () => { - it( 'should return the UPDATE_BLOCK_ATTRIBUTES action', () => { + it( 'should return the UPDATE_BLOCK_ATTRIBUTES action (string)', () => { + const clientId = 'myclientid'; + const attributes = {}; + const result = updateBlockAttributes( clientId, attributes ); + expect( result ).toEqual( { + type: 'UPDATE_BLOCK_ATTRIBUTES', + clientIds: [ clientId ], + attributes, + } ); + } ); + + it( 'should return the UPDATE_BLOCK_ATTRIBUTES action (array)', () => { const clientIds = [ 'myclientid' ]; const attributes = {}; const result = updateBlockAttributes( clientIds, attributes );