Skip to content

Commit

Permalink
Add new action REPLACE_INNER_BLOCKS for InnerBlocks replacement (#14291)
Browse files Browse the repository at this point in the history
  • Loading branch information
jorgefilipecosta authored Mar 18, 2019
1 parent cff0acf commit 1936caa
Show file tree
Hide file tree
Showing 6 changed files with 502 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,17 @@ specified client ID is to be removed.
* selectPrevious: True if the previous block should be
selected when a block is removed.

### replaceInnerBlocks

Returns an action object used in signalling that the inner blocks with the
specified client ID should be replaced.

*Parameters*

* rootClientId: Client ID of the block whose InnerBlocks will re replaced.
* blocks: Block objects to insert as new InnerBlocks
* updateSelection: If true block selection will be updated. If false, block selection will not change. Defaults to true.

### toggleBlockMode

Returns an action object used to toggle the block editing mode between
Expand Down
12 changes: 3 additions & 9 deletions packages/block-editor/src/components/inner-blocks/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { pick, isEqual, map } from 'lodash';
import { pick, isEqual } from 'lodash';
import classnames from 'classnames';

/**
Expand Down Expand Up @@ -144,20 +144,14 @@ InnerBlocks = compose( [
} ),
withDispatch( ( dispatch, ownProps ) => {
const {
replaceBlocks,
insertBlocks,
replaceInnerBlocks,
updateBlockListSettings,
} = dispatch( 'core/block-editor' );
const { block, clientId, templateInsertUpdatesSelection = true } = ownProps;

return {
replaceInnerBlocks( blocks ) {
const clientIds = map( block.innerBlocks, 'clientId' );
if ( clientIds.length ) {
replaceBlocks( clientIds, blocks );
} else {
insertBlocks( blocks, undefined, clientId, templateInsertUpdatesSelection );
}
replaceInnerBlocks( clientId, blocks, block.innerBlocks.length === 0 && templateInsertUpdatesSelection );
},
updateNestedSettings( settings ) {
dispatch( updateBlockListSettings( clientId, settings ) );
Expand Down
20 changes: 20 additions & 0 deletions packages/block-editor/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,26 @@ export function removeBlock( clientId, selectPrevious ) {
return removeBlocks( [ clientId ], selectPrevious );
}

/**
* Returns an action object used in signalling that the inner blocks with the
* specified client ID should be replaced.
*
* @param {string} rootClientId Client ID of the block whose InnerBlocks will re replaced.
* @param {Object[]} blocks Block objects to insert as new InnerBlocks
* @param {?boolean} updateSelection If true block selection will be updated. If false, block selection will not change. Defaults to true.
*
* @return {Object} Action object.
*/
export function replaceInnerBlocks( rootClientId, blocks, updateSelection = true ) {
return {
type: 'REPLACE_INNER_BLOCKS',
rootClientId,
blocks,
updateSelection,
time: Date.now(),
};
}

/**
* Returns an action object used to toggle the block editing mode between
* visual and HTML modes.
Expand Down
34 changes: 34 additions & 0 deletions packages/block-editor/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,38 @@ const withBlockReset = ( reducer ) => ( state, action ) => {
return reducer( state, action );
};

/**
* Higher-order reducer which targets the combined blocks reducer and handles
* the `REPLACE_INNER_BLOCKS` action. When dispatched, this action the state should become equivalent
* to the execution of a `REMOVE_BLOCKS` action containing all the child's of the root block followed by
* the execution of `INSERT_BLOCKS` with the new blocks.
*
* @param {Function} reducer Original reducer function.
*
* @return {Function} Enhanced reducer function.
*/
const withReplaceInnerBlocks = ( reducer ) => ( state, action ) => {
if ( action.type !== 'REPLACE_INNER_BLOCKS' ) {
return reducer( state, action );
}
let stateAfterBlocksRemoval = state;
if ( state.order[ action.rootClientId ] ) {
stateAfterBlocksRemoval = reducer( stateAfterBlocksRemoval, {
type: 'REMOVE_BLOCKS',
clientIds: state.order[ action.rootClientId ],
} );
}
let stateAfterInsert = stateAfterBlocksRemoval;
if ( action.blocks.length ) {
stateAfterInsert = reducer( stateAfterInsert, {
...action,
type: 'INSERT_BLOCKS',
index: 0,
} );
}
return stateAfterInsert;
};

/**
* Higher-order reducer which targets the combined blocks reducer and handles
* the `SAVE_REUSABLE_BLOCK_SUCCESS` action. This action can't be handled by
Expand Down Expand Up @@ -344,6 +376,7 @@ const withSaveReusableBlock = ( reducer ) => ( state, action ) => {
export const blocks = flow(
combineReducers,
withInnerBlocksRemoveCascade,
withReplaceInnerBlocks, // needs to be after withInnerBlocksRemoveCascade
withBlockReset,
withSaveReusableBlock,
withPersistentBlockChange,
Expand Down Expand Up @@ -713,6 +746,7 @@ export function blockSelection( state = {
end: action.clientId,
initialPosition: action.initialPosition,
};
case 'REPLACE_INNER_BLOCKS': // REPLACE_INNER_BLOCKS and INSERT_BLOCKS should follow the same logic.
case 'INSERT_BLOCKS': {
if ( action.updateSelection ) {
return {
Expand Down
59 changes: 43 additions & 16 deletions packages/block-editor/src/store/test/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,31 @@
* Internal dependencies
*/
import {
replaceBlocks,
startTyping,
stopTyping,
clearSelectedBlock,
enterFormattedText,
exitFormattedText,
toggleSelection,
hideInsertionPoint,
insertBlock,
insertBlocks,
mergeBlocks,
multiSelect,
removeBlock,
removeBlocks,
replaceBlock,
replaceBlocks,
replaceInnerBlocks,
resetBlocks,
updateBlockAttributes,
updateBlock,
selectBlock,
selectPreviousBlock,
showInsertionPoint,
startMultiSelect,
startTyping,
stopMultiSelect,
multiSelect,
clearSelectedBlock,
replaceBlock,
insertBlock,
insertBlocks,
showInsertionPoint,
hideInsertionPoint,
mergeBlocks,
removeBlocks,
removeBlock,
stopTyping,
toggleBlockMode,
toggleSelection,
updateBlock,
updateBlockAttributes,
updateBlockListSettings,
} from '../actions';
import { select } from '../controls';
Expand Down Expand Up @@ -346,4 +347,30 @@ describe( 'actions', () => {
} );
} );
} );

describe( 'replaceInnerBlocks', () => {
const block = {
clientId: 'ribs',
};

it( 'should return the REPLACE_INNER_BLOCKS action with default values set', () => {
expect( replaceInnerBlocks( 'root', [ block ] ) ).toEqual( {
type: 'REPLACE_INNER_BLOCKS',
blocks: [ block ],
rootClientId: 'root',
time: expect.any( Number ),
updateSelection: true,
} );
} );

it( 'should return the REPLACE_INNER_BLOCKS action with updateSelection false', () => {
expect( replaceInnerBlocks( 'root', [ block ], false ) ).toEqual( {
type: 'REPLACE_INNER_BLOCKS',
blocks: [ block ],
rootClientId: 'root',
time: expect.any( Number ),
updateSelection: false,
} );
} );
} );
} );
Loading

0 comments on commit 1936caa

Please sign in to comment.