diff --git a/packages/block-editor/src/components/block-list/use-multi-selection.js b/packages/block-editor/src/components/block-list/use-multi-selection.js index 3750c9334a1f61..1712970e7cbacb 100644 --- a/packages/block-editor/src/components/block-list/use-multi-selection.js +++ b/packages/block-editor/src/components/block-list/use-multi-selection.js @@ -147,7 +147,7 @@ export default function useMultiSelection( ref ) { selectedBlockClientId, ] ); - const onSelectionChange = useCallback( () => { + const onSelectionChange = useCallback( ( { isSelectionEnd } ) => { const selection = window.getSelection(); // If no selection is found, end multi selection. @@ -156,9 +156,27 @@ export default function useMultiSelection( ref ) { } const clientId = getBlockClientId( selection.focusNode ); + const isSingularSelection = startClientId.current === clientId; - if ( startClientId.current === clientId ) { + if ( isSingularSelection ) { selectBlock( clientId ); + + // If the selection is complete (on mouse up), and no multiple + // blocks have been selected, set focus back to the anchor element + // if the anchor element contains the selection. Additionally, rich + // text elements that were previously disabled can now be enabled + // again. + if ( isSelectionEnd ) { + toggleRichText( ref.current, true ); + + if ( selection.rangeCount ) { + const { commonAncestorContainer } = selection.getRangeAt( 0 ); + + if ( anchorElement.current.contains( commonAncestorContainer ) ) { + anchorElement.current.focus(); + } + } + } } else { const startPath = [ ...getBlockParents( startClientId.current ), startClientId.current ]; const endPath = [ ...getBlockParents( clientId ), clientId ]; @@ -178,21 +196,8 @@ export default function useMultiSelection( ref ) { // The browser selection won't have updated yet at this point, so wait // until the next animation frame to get the browser selection. rafId.current = window.requestAnimationFrame( () => { - onSelectionChange(); + onSelectionChange( { isSelectionEnd: true } ); stopMultiSelect(); - toggleRichText( ref.current, true ); - - const selection = window.getSelection(); - - // If the anchor element contains the selection, set focus back to - // the anchor element. - if ( selection.rangeCount ) { - const { commonAncestorContainer } = selection.getRangeAt( 0 ); - - if ( anchorElement.current.contains( commonAncestorContainer ) ) { - anchorElement.current.focus(); - } - } } ); }, [ onSelectionChange, stopMultiSelect ] ); 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 e3125d7972ea80..68f5047e1bc9c0 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 @@ -52,6 +52,20 @@ exports[`Multi-block selection should only trigger multi-selection when at the e " `; +exports[`Multi-block selection should preserve dragged selection on move 1`] = ` +" +

2

+ + + +

3

+ + + +

1

+" +`; + exports[`Multi-block selection should return original focus after failed multi selection attempt 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 8eaf697fb5d3c0..cd7b19f264666c 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 @@ -7,6 +7,7 @@ import { pressKeyWithModifier, pressKeyTimes, getEditedPostContent, + clickBlockToolbarButton, } from '@wordpress/e2e-test-utils'; async function getSelectedFlatIndices() { @@ -408,4 +409,44 @@ describe( 'Multi-block selection', () => { expect( await getEditedPostContent() ).toMatchSnapshot(); } ); + + it( 'should preserve dragged selection on move', async () => { + await clickBlockAppender(); + await page.keyboard.type( '1' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( '2' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( '3' ); + + const [ coord1, coord2 ] = await page.evaluate( () => { + const elements = Array.from( document.querySelectorAll( '.wp-block-paragraph' ) ); + const rect1 = elements[ 2 ].getBoundingClientRect(); + const rect2 = elements[ 1 ].getBoundingClientRect(); + return [ + { + x: rect1.x + ( rect1.width / 2 ), + y: rect1.y + ( rect1.height / 2 ), + }, + { + x: rect2.x + ( rect2.width / 2 ), + y: rect2.y + ( rect2.height / 2 ), + }, + ]; + } ); + + await page.mouse.move( coord1.x, coord1.y ); + await page.mouse.down(); + await page.mouse.move( coord2.x, coord2.y ); + await page.mouse.up(); + + await testNativeSelection(); + expect( await getSelectedFlatIndices() ).toEqual( [ 2, 3 ] ); + + await clickBlockToolbarButton( 'Move up' ); + + await testNativeSelection(); + expect( await getSelectedFlatIndices() ).toEqual( [ 1, 2 ] ); + + expect( await getEditedPostContent() ).toMatchSnapshot(); + } ); } );