diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index c32d929392d8d..70456ac213fc3 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -19,6 +19,8 @@ import { serializeRawBlock, switchToBlockType, store as blocksStore, + getDefaultBlockName, + isUnmodifiedBlock, } from '@wordpress/blocks'; import { withFilters } from '@wordpress/components'; import { @@ -311,7 +313,6 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => { __unstableMarkLastChangeAsPersistent, moveBlocksToPosition, removeBlock, - selectBlock, } = dispatch( blockEditorStore ); // Do not add new properties here, use `useDispatch` instead to avoid @@ -348,8 +349,71 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => { getBlockAttributes, getBlockName, getBlockOrder, + getBlockIndex, + getBlockRootClientId, + canInsertBlockType, } = registry.select( blockEditorStore ); + /** + * Moves the block with clientId up one level. If the block type + * cannot be inserted at the new location, it will be attempted to + * convert to the default block type. + * + * @param {string} _clientId The block to move. + * @param {boolean} changeSelection Whether to change the selection + * to the moved block. + */ + function moveFirstItemUp( _clientId, changeSelection = true ) { + const targetRootClientId = getBlockRootClientId( _clientId ); + const blockOrder = getBlockOrder( _clientId ); + const [ firstClientId ] = blockOrder; + + if ( + blockOrder.length === 1 && + isUnmodifiedBlock( getBlock( firstClientId ) ) + ) { + removeBlock( _clientId ); + } else { + if ( + canInsertBlockType( + getBlockName( firstClientId ), + targetRootClientId + ) + ) { + moveBlocksToPosition( + [ firstClientId ], + _clientId, + targetRootClientId, + getBlockIndex( _clientId ) + ); + } else { + const replacement = switchToBlockType( + getBlock( firstClientId ), + getDefaultBlockName() + ); + + if ( replacement && replacement.length ) { + registry.batch( () => { + insertBlocks( + replacement, + getBlockIndex( _clientId ), + targetRootClientId, + changeSelection + ); + removeBlock( firstClientId, false ); + } ); + } + } + + if ( + ! getBlockOrder( _clientId ).length && + isUnmodifiedBlock( getBlock( _clientId ) ) + ) { + removeBlock( _clientId, false ); + } + } + } + // For `Delete` or forward merge, we should do the exact same thing // as `Backspace`, but from the other block. if ( forward ) { @@ -400,15 +464,8 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => { 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 ); + if ( getBlockOrder( nextBlockClientId ).length ) { + moveFirstItemUp( nextBlockClientId, false ); } else { mergeBlocks( clientId, nextBlockClientId ); } @@ -453,18 +510,7 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => { } } - // Attempt to "unwrap" the block contents when there's no - // preceding block to merge with. - const replacement = switchToBlockType( - getBlock( rootClientId ), - '*' - ); - if ( replacement && replacement.length ) { - registry.batch( () => { - replaceBlocks( rootClientId, replacement ); - selectBlock( replacement[ 0 ].clientId, 0 ); - } ); - } + moveFirstItemUp( rootClientId ); } } }, diff --git a/packages/block-editor/src/components/block-list/block.native.js b/packages/block-editor/src/components/block-list/block.native.js index 91817675d6eed..061ff216a1383 100644 --- a/packages/block-editor/src/components/block-list/block.native.js +++ b/packages/block-editor/src/components/block-list/block.native.js @@ -20,6 +20,8 @@ import { getBlockType, __experimentalGetAccessibleBlockLabel as getAccessibleBlockLabel, switchToBlockType, + getDefaultBlockName, + isUnmodifiedBlock, } from '@wordpress/blocks'; import { useSetting } from '@wordpress/block-editor'; @@ -436,8 +438,72 @@ export default compose( [ getBlockAttributes, getBlockName, getBlockOrder, + getBlockIndex, + getBlockRootClientId, + canInsertBlockType, } = registry.select( blockEditorStore ); + /** + * Moves the block with clientId up one level. If the block type + * cannot be inserted at the new location, it will be attempted to + * convert to the default block type. + * + * @param {string} _clientId The block to move. + * @param {boolean} changeSelection Whether to change the selection + * to the moved block. + */ + function moveFirstItemUp( _clientId, changeSelection = true ) { + const targetRootClientId = + getBlockRootClientId( _clientId ); + const blockOrder = getBlockOrder( _clientId ); + const [ firstClientId ] = blockOrder; + + if ( + blockOrder.length === 1 && + isUnmodifiedBlock( getBlock( firstClientId ) ) + ) { + removeBlock( _clientId ); + } else { + if ( + canInsertBlockType( + getBlockName( firstClientId ), + targetRootClientId + ) + ) { + moveBlocksToPosition( + [ firstClientId ], + _clientId, + targetRootClientId, + getBlockIndex( _clientId ) + ); + } else { + const replacement = switchToBlockType( + getBlock( firstClientId ), + getDefaultBlockName() + ); + + if ( replacement && replacement.length ) { + registry.batch( () => { + insertBlocks( + replacement, + getBlockIndex( _clientId ), + targetRootClientId, + changeSelection + ); + removeBlock( firstClientId, false ); + } ); + } + } + + if ( + ! getBlockOrder( _clientId ).length && + isUnmodifiedBlock( getBlock( _clientId ) ) + ) { + removeBlock( _clientId, false ); + } + } + } + // For `Delete` or forward merge, we should do the exact same thing // as `Backspace`, but from the other block. if ( forward ) { @@ -488,15 +554,8 @@ export default compose( [ 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 ); + if ( getBlockOrder( nextBlockClientId ).length ) { + moveFirstItemUp( nextBlockClientId, false ); } else { mergeBlocks( clientId, nextBlockClientId ); } @@ -541,18 +600,7 @@ export default compose( [ } } - // Attempt to "unwrap" the block contents when there's no - // preceding block to merge with. - const replacement = switchToBlockType( - getBlock( rootClientId ), - '*' - ); - if ( replacement && replacement.length ) { - registry.batch( () => { - replaceBlocks( rootClientId, replacement ); - selectBlock( replacement[ 0 ].clientId, 0 ); - } ); - } + moveFirstItemUp( rootClientId ); } } }, diff --git a/packages/block-library/src/list-item/index.js b/packages/block-library/src/list-item/index.js index 61756019baf92..00adc1c2c4026 100644 --- a/packages/block-library/src/list-item/index.js +++ b/packages/block-library/src/list-item/index.js @@ -10,6 +10,7 @@ import initBlock from '../utils/init-block'; import metadata from './block.json'; import edit from './edit'; import save from './save'; +import transforms from './transforms'; const { name } = metadata; @@ -25,6 +26,7 @@ export const settings = { content: attributes.content + attributesToMerge.content, }; }, + transforms, }; export const init = () => initBlock( { name, metadata, settings } ); diff --git a/packages/block-library/src/list-item/transforms.js b/packages/block-library/src/list-item/transforms.js new file mode 100644 index 0000000000000..6e05f8501b5a3 --- /dev/null +++ b/packages/block-library/src/list-item/transforms.js @@ -0,0 +1,17 @@ +/** + * WordPress dependencies + */ +import { createBlock } from '@wordpress/blocks'; + +const transforms = { + to: [ + { + type: 'block', + blocks: [ 'core/paragraph' ], + transform: ( attributes ) => + createBlock( 'core/paragraph', attributes ), + }, + ], +}; + +export default transforms; diff --git a/packages/block-library/src/list/test/edit.native.js b/packages/block-library/src/list/test/edit.native.js index 9defa338782db..e661fbe3a626d 100644 --- a/packages/block-library/src/list/test/edit.native.js +++ b/packages/block-library/src/list/test/edit.native.js @@ -361,7 +361,7 @@ describe( 'List block', () => { ` ); } ); - it( 'unwraps list items when attempting to merge with non-list block', async () => { + it( 'unwraps first item when attempting to merge with non-list block', async () => { const initialHtml = `
A quick brown fox.
@@ -400,14 +400,16 @@ describe( 'List block', () => { "A quick brown fox.
- +One
- - -Two
- " + + +1
- -2
- " + +2+ " ` ); } ); } ); diff --git a/packages/e2e-tests/specs/editor/various/block-switcher.test.js b/packages/e2e-tests/specs/editor/various/block-switcher.test.js index eea665e688888..6e5c192a758ad 100644 --- a/packages/e2e-tests/specs/editor/various/block-switcher.test.js +++ b/packages/e2e-tests/specs/editor/various/block-switcher.test.js @@ -82,9 +82,9 @@ describe( 'Block Switcher', () => { await pressKeyWithModifier( 'alt', 'F10' ); // Verify the block switcher exists. - expect( await hasBlockSwitcher() ).toBeTruthy(); + expect( await hasBlockSwitcher() ).toBeFalsy(); // Verify the correct block transforms appear. - expect( await getAvailableBlockTransforms() ).toHaveLength( 1 ); + expect( await getAvailableBlockTransforms() ).toHaveLength( 0 ); } ); describe( 'Conditional tranformation options', () => { diff --git a/test/e2e/specs/editor/blocks/list.spec.js b/test/e2e/specs/editor/blocks/list.spec.js index daa23241a4e8e..0a1159f6d5d49 100644 --- a/test/e2e/specs/editor/blocks/list.spec.js +++ b/test/e2e/specs/editor/blocks/list.spec.js @@ -1090,9 +1090,11 @@ test.describe( 'List', () => { - -
2
-` + +hi-item 1
+ + + +hi-
- \ No newline at end of file +hi
+ + + +item 1
+ + + +hi-item 1
+ + + +-item 1
+item 1
- -item 2
- \ No newline at end of file + +