From ab1ec6f807875bf15e27008b6e392bd97bc63585 Mon Sep 17 00:00:00 2001 From: Nik Tsekouras Date: Fri, 15 Apr 2022 14:44:31 +0300 Subject: [PATCH] [Multi-selection]: Copy whole block when selection is collapsed (#40374) * [Multi-selection]: Copy whole block when selection is collapsed * rename var to shouldHandleWholeBlocks --- .../src/components/copy-handler/index.js | 10 +++--- packages/block-editor/src/store/selectors.js | 19 ++++++++++ .../editor/various/copy-cut-paste.spec.js | 35 +++++++++++++++++++ ...ks-with-collapsed-selection-1-chromium.txt | 7 ++++ ...ks-with-collapsed-selection-2-chromium.txt | 11 ++++++ ...ks-with-collapsed-selection-1-chromium.txt | 3 ++ ...ks-with-collapsed-selection-2-chromium.txt | 7 ++++ 7 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 test/e2e/specs/editor/various/copy-cut-paste.spec.js-snapshots/Copy-cut-paste-should-copy-and-paste-individual-blocks-with-collapsed-selection-1-chromium.txt create mode 100644 test/e2e/specs/editor/various/copy-cut-paste.spec.js-snapshots/Copy-cut-paste-should-copy-and-paste-individual-blocks-with-collapsed-selection-2-chromium.txt create mode 100644 test/e2e/specs/editor/various/copy-cut-paste.spec.js-snapshots/Copy-cut-paste-should-cut-and-paste-individual-blocks-with-collapsed-selection-1-chromium.txt create mode 100644 test/e2e/specs/editor/various/copy-cut-paste.spec.js-snapshots/Copy-cut-paste-should-cut-and-paste-individual-blocks-with-collapsed-selection-2-chromium.txt diff --git a/packages/block-editor/src/components/copy-handler/index.js b/packages/block-editor/src/components/copy-handler/index.js index 2c72309852cebe..0958bac863216b 100644 --- a/packages/block-editor/src/components/copy-handler/index.js +++ b/packages/block-editor/src/components/copy-handler/index.js @@ -79,6 +79,7 @@ export function useClipboardHandler() { hasMultiSelection, getSettings, __unstableIsFullySelected, + __unstableIsSelectionCollapsed, __unstableIsSelectionMergeable, __unstableGetSelectedBlocksWithPartialSelection, } = useSelect( blockEditorStore ); @@ -123,10 +124,11 @@ export function useClipboardHandler() { const eventDefaultPrevented = event.defaultPrevented; event.preventDefault(); - const isFullySelected = __unstableIsFullySelected(); const isSelectionMergeable = __unstableIsSelectionMergeable(); + const shouldHandleWholeBlocks = + __unstableIsSelectionCollapsed() || __unstableIsFullySelected(); const expandSelectionIsNeeded = - ! isFullySelected && ! isSelectionMergeable; + ! shouldHandleWholeBlocks && ! isSelectionMergeable; if ( event.type === 'copy' || event.type === 'cut' ) { if ( selectedBlockClientIds.length === 1 ) { flashBlock( selectedBlockClientIds[ 0 ] ); @@ -139,7 +141,7 @@ export function useClipboardHandler() { notifyCopy( event.type, selectedBlockClientIds ); let blocks; // Check if we have partial selection. - if ( isFullySelected ) { + if ( shouldHandleWholeBlocks ) { blocks = getBlocksByClientId( selectedBlockClientIds ); } else { const [ @@ -165,7 +167,7 @@ export function useClipboardHandler() { // We need to also check if at the start we needed to // expand the selection, as in this point we might have // programmatically fully selected the blocks above. - if ( isFullySelected && ! expandSelectionIsNeeded ) { + if ( shouldHandleWholeBlocks && ! expandSelectionIsNeeded ) { removeBlocks( selectedBlockClientIds ); } else { __unstableDeleteSelection(); diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index df2c16aa912c51..ef9c97f613d16c 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -932,6 +932,25 @@ export function __unstableIsFullySelected( state ) { ); } +/** + * Returns true if the selection is collapsed. + * + * @param {Object} state Editor state. + * + * @return {boolean} Whether the selection is collapsed. + */ +export function __unstableIsSelectionCollapsed( state ) { + const selectionAnchor = getSelectionStart( state ); + const selectionFocus = getSelectionEnd( state ); + return ( + !! selectionAnchor && + !! selectionFocus && + selectionAnchor.clientId === selectionFocus.clientId && + selectionAnchor.attributeKey === selectionFocus.attributeKey && + selectionAnchor.offset === selectionFocus.offset + ); +} + /** * Check whether the selection is mergeable. * diff --git a/test/e2e/specs/editor/various/copy-cut-paste.spec.js b/test/e2e/specs/editor/various/copy-cut-paste.spec.js index 90563f950005ae..b6adfd758b3e07 100644 --- a/test/e2e/specs/editor/various/copy-cut-paste.spec.js +++ b/test/e2e/specs/editor/various/copy-cut-paste.spec.js @@ -4,6 +4,41 @@ const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); test.describe( 'Copy/cut/paste', () => { + test( 'should copy and paste individual blocks with collapsed selection', async ( { + page, + pageUtils, + } ) => { + await pageUtils.createNewPost(); + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( 'Copy - collapsed selection' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( '2' ); + await page.keyboard.press( 'ArrowUp' ); + await pageUtils.pressKeyWithModifier( 'primary', 'c' ); + expect( await pageUtils.getEditedPostContent() ).toMatchSnapshot(); + + await page.keyboard.press( 'ArrowDown' ); + await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + expect( await pageUtils.getEditedPostContent() ).toMatchSnapshot(); + } ); + test( 'should cut and paste individual blocks with collapsed selection', async ( { + page, + pageUtils, + } ) => { + await pageUtils.createNewPost(); + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( 'Cut - collapsed selection' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( '2' ); + await page.keyboard.press( 'ArrowUp' ); + await pageUtils.pressKeyWithModifier( 'primary', 'x' ); + expect( await pageUtils.getEditedPostContent() ).toMatchSnapshot(); + + await page.keyboard.press( 'Tab' ); + await page.keyboard.press( 'ArrowDown' ); + await pageUtils.pressKeyWithModifier( 'primary', 'v' ); + expect( await pageUtils.getEditedPostContent() ).toMatchSnapshot(); + } ); test( 'should copy blocks when non textual elements are focused (image, spacer)', async ( { page, pageUtils, diff --git a/test/e2e/specs/editor/various/copy-cut-paste.spec.js-snapshots/Copy-cut-paste-should-copy-and-paste-individual-blocks-with-collapsed-selection-1-chromium.txt b/test/e2e/specs/editor/various/copy-cut-paste.spec.js-snapshots/Copy-cut-paste-should-copy-and-paste-individual-blocks-with-collapsed-selection-1-chromium.txt new file mode 100644 index 00000000000000..494835790fb361 --- /dev/null +++ b/test/e2e/specs/editor/various/copy-cut-paste.spec.js-snapshots/Copy-cut-paste-should-copy-and-paste-individual-blocks-with-collapsed-selection-1-chromium.txt @@ -0,0 +1,7 @@ + +

Copy - collapsed selection

+ + + +

2

+ \ No newline at end of file diff --git a/test/e2e/specs/editor/various/copy-cut-paste.spec.js-snapshots/Copy-cut-paste-should-copy-and-paste-individual-blocks-with-collapsed-selection-2-chromium.txt b/test/e2e/specs/editor/various/copy-cut-paste.spec.js-snapshots/Copy-cut-paste-should-copy-and-paste-individual-blocks-with-collapsed-selection-2-chromium.txt new file mode 100644 index 00000000000000..3dac62748e8e02 --- /dev/null +++ b/test/e2e/specs/editor/various/copy-cut-paste.spec.js-snapshots/Copy-cut-paste-should-copy-and-paste-individual-blocks-with-collapsed-selection-2-chromium.txt @@ -0,0 +1,11 @@ + +

Copy - collapsed selection

+ + + +

2

+ + + +

Copy - collapsed selection

+ \ No newline at end of file diff --git a/test/e2e/specs/editor/various/copy-cut-paste.spec.js-snapshots/Copy-cut-paste-should-cut-and-paste-individual-blocks-with-collapsed-selection-1-chromium.txt b/test/e2e/specs/editor/various/copy-cut-paste.spec.js-snapshots/Copy-cut-paste-should-cut-and-paste-individual-blocks-with-collapsed-selection-1-chromium.txt new file mode 100644 index 00000000000000..863fd4e6a1ce49 --- /dev/null +++ b/test/e2e/specs/editor/various/copy-cut-paste.spec.js-snapshots/Copy-cut-paste-should-cut-and-paste-individual-blocks-with-collapsed-selection-1-chromium.txt @@ -0,0 +1,3 @@ + +

2

+ \ No newline at end of file diff --git a/test/e2e/specs/editor/various/copy-cut-paste.spec.js-snapshots/Copy-cut-paste-should-cut-and-paste-individual-blocks-with-collapsed-selection-2-chromium.txt b/test/e2e/specs/editor/various/copy-cut-paste.spec.js-snapshots/Copy-cut-paste-should-cut-and-paste-individual-blocks-with-collapsed-selection-2-chromium.txt new file mode 100644 index 00000000000000..ad1a1ef6e5c723 --- /dev/null +++ b/test/e2e/specs/editor/various/copy-cut-paste.spec.js-snapshots/Copy-cut-paste-should-cut-and-paste-individual-blocks-with-collapsed-selection-2-chromium.txt @@ -0,0 +1,7 @@ + +

2

+ + + +

Cut - collapsed selection

+ \ No newline at end of file