Skip to content

Commit

Permalink
Merge inner blocks if wrappers are equal (#43181)
Browse files Browse the repository at this point in the history
  • Loading branch information
ellatrix authored Oct 10, 2022
1 parent b603286 commit 0a3bede
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 36 deletions.
118 changes: 111 additions & 7 deletions packages/block-editor/src/components/block-list/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -296,22 +296,25 @@ const applyWithSelect = withSelect( ( select, { clientId, rootClientId } ) => {
};
} );

const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => {
const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => {
const {
updateBlockAttributes,
insertBlocks,
mergeBlocks,
replaceBlocks,
toggleSelection,
__unstableMarkLastChangeAsPersistent,
moveBlocksToPosition,
removeBlock,
selectBlock,
} = dispatch( blockEditorStore );

// Do not add new properties here, use `useDispatch` instead to avoid
// leaking new props to the public API (editor.BlockListBlock filter).
return {
setAttributes( newAttributes ) {
const { getMultiSelectedBlockClientIds } =
select( blockEditorStore );
registry.select( blockEditorStore );
const multiSelectedBlockClientIds =
getMultiSelectedBlockClientIds();
const { clientId } = ownProps;
Expand All @@ -327,34 +330,135 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => {
},
onInsertBlocksAfter( blocks ) {
const { clientId, rootClientId } = ownProps;
const { getBlockIndex } = select( blockEditorStore );
const { getBlockIndex } = registry.select( blockEditorStore );
const index = getBlockIndex( clientId );
insertBlocks( blocks, index + 1, rootClientId );
},
onMerge( forward ) {
const { clientId, rootClientId } = ownProps;
const { getPreviousBlockClientId, getNextBlockClientId, getBlock } =
select( blockEditorStore );
const {
getPreviousBlockClientId,
getNextBlockClientId,
getBlock,
getBlockAttributes,
getBlockName,
getBlockOrder,
} = registry.select( blockEditorStore );

// For `Delete` or forward merge, we should do the exact same thing
// as `Backspace`, but from the other block.
if ( forward ) {
if ( rootClientId ) {
const nextRootClientId =
getNextBlockClientId( rootClientId );

if ( nextRootClientId ) {
// If there is a block that follows with the same parent
// block name and the same attributes, merge the inner
// blocks.
if (
getBlockName( rootClientId ) ===
getBlockName( nextRootClientId )
) {
const rootAttributes =
getBlockAttributes( rootClientId );
const previousRootAttributes =
getBlockAttributes( nextRootClientId );

if (
Object.keys( rootAttributes ).every(
( key ) =>
rootAttributes[ key ] ===
previousRootAttributes[ key ]
)
) {
registry.batch( () => {
moveBlocksToPosition(
getBlockOrder( nextRootClientId ),
nextRootClientId,
rootClientId
);
removeBlock( nextRootClientId, false );
} );
return;
}
} else {
mergeBlocks( rootClientId, nextRootClientId );
return;
}
}
}

const nextBlockClientId = getNextBlockClientId( clientId );
if ( nextBlockClientId ) {

if ( ! nextBlockClientId ) {
return;
}

// Check if it's possibile to "unwrap" the following block
// before trying to merge.
const replacement = switchToBlockType(
getBlock( nextBlockClientId ),
'*'
);

if ( replacement && replacement.length ) {
replaceBlocks( nextBlockClientId, replacement );
} else {
mergeBlocks( clientId, nextBlockClientId );
}
} else {
const previousBlockClientId =
getPreviousBlockClientId( clientId );

if ( previousBlockClientId ) {
mergeBlocks( previousBlockClientId, clientId );
} else if ( rootClientId ) {
const previousRootClientId =
getPreviousBlockClientId( rootClientId );

// If there is a preceding block with the same parent block
// name and the same attributes, merge the inner blocks.
if (
previousRootClientId &&
getBlockName( rootClientId ) ===
getBlockName( previousRootClientId )
) {
const rootAttributes =
getBlockAttributes( rootClientId );
const previousRootAttributes =
getBlockAttributes( previousRootClientId );

if (
Object.keys( rootAttributes ).every(
( key ) =>
rootAttributes[ key ] ===
previousRootAttributes[ key ]
)
) {
registry.batch( () => {
moveBlocksToPosition(
getBlockOrder( rootClientId ),
rootClientId,
previousRootClientId
);
removeBlock( rootClientId, false );
} );
return;
}
}

// Attempt to "unwrap" the block contents when there's no
// preceding block to merge with.
const replacement = switchToBlockType(
getBlock( rootClientId ),
'*'
);
if ( replacement && replacement.length ) {
replaceBlocks( rootClientId, replacement, 0 );
registry.batch( () => {
replaceBlocks( rootClientId, replacement );
selectBlock( replacement[ 0 ].clientId, 0 );
} );
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion packages/block-library/src/list-item/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export default function ListItemEdit( {
setAttributes,
onReplace,
clientId,
mergeBlocks,
} ) {
const { placeholder, content } = attributes;
const blockProps = useBlockProps( { ref: useCopy( clientId ) } );
Expand All @@ -70,7 +71,7 @@ export default function ListItemEdit( {
const useEnterRef = useEnter( { content, clientId } );
const useSpaceRef = useSpace( clientId );
const onSplit = useSplit( clientId );
const onMerge = useMerge( clientId );
const onMerge = useMerge( clientId, mergeBlocks );
return (
<>
<li { ...innerBlocksProps }>
Expand Down
27 changes: 4 additions & 23 deletions packages/block-library/src/list-item/hooks/use-merge.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
*/
import { useRegistry, useDispatch, useSelect } from '@wordpress/data';
import { store as blockEditorStore } from '@wordpress/block-editor';
import { getDefaultBlockName, switchToBlockType } from '@wordpress/blocks';

/**
* Internal dependencies
Expand All @@ -12,17 +11,16 @@ import useOutdentListItem from './use-outdent-list-item';

import { name as listItemName } from '../block.json';

export default function useMerge( clientId ) {
export default function useMerge( clientId, onMerge ) {
const registry = useRegistry();
const {
getPreviousBlockClientId,
getNextBlockClientId,
getBlockOrder,
getBlockRootClientId,
getBlockName,
getBlock,
} = useSelect( blockEditorStore );
const { mergeBlocks, moveBlocksToPosition, replaceBlock, selectBlock } =
const { mergeBlocks, moveBlocksToPosition } =
useDispatch( blockEditorStore );
const [ , outdentListItem ] = useOutdentListItem( clientId );

Expand Down Expand Up @@ -79,29 +77,12 @@ export default function useMerge( clientId ) {
return getBlockOrder( order[ 0 ] )[ 0 ];
}

function switchToDefaultBlockType( forward ) {
const rootClientId = getBlockRootClientId( clientId );
const replacement = switchToBlockType(
getBlock( rootClientId ),
getDefaultBlockName()
);
const indexToSelect = forward ? replacement.length - 1 : 0;
const initialPosition = forward ? -1 : 0;
registry.batch( () => {
replaceBlock( rootClientId, replacement );
selectBlock(
replacement[ indexToSelect ].clientId,
initialPosition
);
} );
}

return ( forward ) => {
if ( forward ) {
const nextBlockClientId = getNextId( clientId );

if ( ! nextBlockClientId ) {
switchToDefaultBlockType( forward );
onMerge( forward );
return;
}

Expand Down Expand Up @@ -134,7 +115,7 @@ export default function useMerge( clientId ) {
mergeBlocks( trailingId, clientId );
} );
} else {
switchToDefaultBlockType( forward );
onMerge( forward );
}
}
};
Expand Down
11 changes: 11 additions & 0 deletions packages/block-library/src/list/transforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,17 @@ const transforms = {
);
},
} ) ),
{
type: 'block',
blocks: [ '*' ],
transform: ( _attributes, childBlocks ) => {
return getListContentFlat( childBlocks ).map( ( content ) =>
createBlock( 'core/paragraph', {
content,
} )
);
},
},
],
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ describe( 'Block Switcher', () => {
await pressKeyWithModifier( 'alt', 'F10' );

// Verify the block switcher exists.
expect( await hasBlockSwitcher() ).toBeFalsy();
expect( await hasBlockSwitcher() ).toBeTruthy();
// Verify the correct block transforms appear.
expect( await getAvailableBlockTransforms() ).toHaveLength( 0 );
expect( await getAvailableBlockTransforms() ).toHaveLength( 1 );
} );

describe( 'Conditional tranformation options', () => {
Expand Down
30 changes: 27 additions & 3 deletions packages/e2e-tests/specs/editor/various/splitting-merging.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,20 @@ describe( 'splitting and merging blocks', () => {
await page.keyboard.press( 'Enter' );
await page.keyboard.type( 'item 2' );
await pressKeyTimes( 'ArrowUp', 3 );
await page.keyboard.press( 'Delete' );
expect( await getEditedPostContent() ).toMatchInlineSnapshot( `
"<!-- wp:paragraph -->
<p>hi</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>item 1</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>item 2</p>
<!-- /wp:paragraph -->"
` );
await page.keyboard.press( 'Delete' );
// Carret should be in the first block and at the proper position.
await page.keyboard.type( '-' );
Expand All @@ -259,15 +273,25 @@ describe( 'splitting and merging blocks', () => {
await page.keyboard.press( 'ArrowUp' );
await pressKeyTimes( 'ArrowLeft', 6 );
await page.keyboard.press( 'Backspace' );
// Carret should be in the first block and at the proper position.
await page.keyboard.type( '-' );
expect( await getEditedPostContent() ).toMatchInlineSnapshot( `
"<!-- wp:paragraph -->
<p>hi</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>-item 1</p>
<p>item 1</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>item 2</p>
<!-- /wp:paragraph -->"
` );
await page.keyboard.press( 'Backspace' );
// Carret should be in the first block and at the proper position.
await page.keyboard.type( '-' );
expect( await getEditedPostContent() ).toMatchInlineSnapshot( `
"<!-- wp:paragraph -->
<p>hi-item 1</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
Expand Down
46 changes: 46 additions & 0 deletions test/e2e/specs/editor/blocks/list.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1150,4 +1150,50 @@ test.describe( 'List', () => {
// Verify no WSOD and content is proper.
expect( await editor.getEditedPostContent() ).toMatchSnapshot();
} );

test( 'should merge two list with same attributes', async ( {
editor,
page,
} ) => {
await page.click( 'role=button[name="Add default block"i]' );
await page.keyboard.type( '* a' );
await page.keyboard.press( 'Enter' );
await page.keyboard.type( 'b' );
await page.keyboard.press( 'Enter' );
await page.keyboard.press( 'Enter' );
await page.keyboard.type( '* c' );

await expect.poll( editor.getEditedPostContent ).toBe( `<!-- wp:list -->
<ul><!-- wp:list-item -->
<li>a</li>
<!-- /wp:list-item -->
<!-- wp:list-item -->
<li>b</li>
<!-- /wp:list-item --></ul>
<!-- /wp:list -->
<!-- wp:list -->
<ul><!-- wp:list-item -->
<li>c</li>
<!-- /wp:list-item --></ul>
<!-- /wp:list -->` );

await page.keyboard.press( 'ArrowLeft' );
await page.keyboard.press( 'Backspace' );

await expect.poll( editor.getEditedPostContent ).toBe( `<!-- wp:list -->
<ul><!-- wp:list-item -->
<li>a</li>
<!-- /wp:list-item -->
<!-- wp:list-item -->
<li>b</li>
<!-- /wp:list-item -->
<!-- wp:list-item -->
<li>c</li>
<!-- /wp:list-item --></ul>
<!-- /wp:list -->` );
} );
} );

0 comments on commit 0a3bede

Please sign in to comment.