Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

List View: Add keyboard shortcut for duplicating blocks #53559

Merged
merged 6 commits into from Aug 20, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -6,6 +6,7 @@ import classnames from 'classnames';
/**
* WordPress dependencies
*/
import { hasBlockSupport } from '@wordpress/blocks';
import {
Button,
__experimentalHStack as HStack,
Expand Down Expand Up @@ -55,13 +56,15 @@ function ListViewBlockSelectButton(
} );
const { isLocked } = useBlockLock( clientId );
const {
canInsertBlockType,
getSelectedBlockClientIds,
getPreviousBlockClientId,
getBlockRootClientId,
getBlockOrder,
getBlocksByClientId,
canRemoveBlocks,
} = useSelect( blockEditorStore );
const { removeBlocks } = useDispatch( blockEditorStore );
const { duplicateBlocks, removeBlocks } = useDispatch( blockEditorStore );
const isMatch = useShortcutEventMatch();
const isSticky = blockInformation?.positionType === 'sticky';
const images = useListViewImages( { clientId, isExpanded } );
Expand All @@ -83,6 +86,31 @@ function ListViewBlockSelectButton(
onDragStart?.( event );
};

// Determine which blocks to update:
// If the current (focused) block is part of the block selection, use the whole selection.
// If the focused block is not part of the block selection, only update the focused block.
function getBlocksToUpdate() {
const selectedBlockClientIds = getSelectedBlockClientIds();
const isUpdatingSelectedBlocks =
selectedBlockClientIds.includes( clientId );
const firstBlockClientId = isUpdatingSelectedBlocks
? selectedBlockClientIds[ 0 ]
: clientId;
const firstBlockRootClientId =
getBlockRootClientId( firstBlockClientId );

const blocksToUpdate = isUpdatingSelectedBlocks
? selectedBlockClientIds
: [ clientId ];

return {
blocksToUpdate,
firstBlockClientId,
firstBlockRootClientId,
selectedBlockClientIds,
};
}

/**
* @param {KeyboardEvent} event
*/
Expand All @@ -94,18 +122,12 @@ function ListViewBlockSelectButton(
event.keyCode === DELETE ||
isMatch( 'core/block-editor/remove', event )
) {
const selectedBlockClientIds = getSelectedBlockClientIds();
const isDeletingSelectedBlocks =
selectedBlockClientIds.includes( clientId );
const firstBlockClientId = isDeletingSelectedBlocks
? selectedBlockClientIds[ 0 ]
: clientId;
const firstBlockRootClientId =
getBlockRootClientId( firstBlockClientId );

const blocksToDelete = isDeletingSelectedBlocks
? selectedBlockClientIds
: [ clientId ];
const {
blocksToUpdate: blocksToDelete,
firstBlockClientId,
firstBlockRootClientId,
selectedBlockClientIds,
} = getBlocksToUpdate();

// Don't update the selection if the blocks cannot be deleted.
if ( ! canRemoveBlocks( blocksToDelete, firstBlockRootClientId ) ) {
Expand All @@ -131,6 +153,29 @@ function ListViewBlockSelectButton(
}

updateFocusAndSelection( blockToFocus, shouldUpdateSelection );
} else if ( isMatch( 'core/block-editor/duplicate', event ) ) {
if ( event.defaultPrevented ) {
return;
}
event.preventDefault();

const { blocksToUpdate, firstBlockRootClientId } =
getBlocksToUpdate();

const canDuplicate = getBlocksByClientId( blocksToUpdate ).every(
( block ) => {
return (
!! block &&
hasBlockSupport( block.name, 'multiple', true ) &&
canInsertBlockType( block.name, firstBlockRootClientId )
);
}
);

if ( canDuplicate ) {
// Duplicate blocks, but do not update the selection.
duplicateBlocks( blocksToUpdate, false );
}
}
}

Expand Down
25 changes: 23 additions & 2 deletions test/e2e/specs/editor/various/list-view.spec.js
Expand Up @@ -431,7 +431,7 @@ test.describe( 'List View', () => {
).toBeFocused();
} );

test( 'should delete blocks using keyboard', async ( {
test( 'should duplicate and delete blocks using keyboard', async ( {
editor,
page,
pageUtils,
Expand Down Expand Up @@ -474,6 +474,20 @@ test.describe( 'List View', () => {
{ name: 'core/file', selected: true, focused: true },
] );

await pageUtils.pressKeys( 'primaryShift+d' );

await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'Duplicating a block should retain focus and selection on existing block.'
)
.toMatchObject( [
{ name: 'core/group' },
{ name: 'core/columns' },
{ name: 'core/file', selected: true, focused: true },
{ name: 'core/file' },
] );

await page.keyboard.press( 'Delete' );
await expect
.poll(
Expand All @@ -483,6 +497,7 @@ test.describe( 'List View', () => {
.toMatchObject( [
{ name: 'core/group' },
{ name: 'core/columns', selected: true, focused: true },
{ name: 'core/file' },
] );

// Expand the current column.
Expand All @@ -504,6 +519,7 @@ test.describe( 'List View', () => {
{ name: 'core/column', focused: true },
],
},
{ name: 'core/file' },
] );

await page.keyboard.press( 'Delete' );
Expand All @@ -525,6 +541,7 @@ test.describe( 'List View', () => {
},
],
},
{ name: 'core/file' },
] );

// Expand the current column.
Expand Down Expand Up @@ -555,6 +572,7 @@ test.describe( 'List View', () => {
},
],
},
{ name: 'core/file' },
] );

// Move focus and select the first block.
Expand All @@ -573,14 +591,17 @@ test.describe( 'List View', () => {
selected: true,
focused: true,
},
{ name: 'core/file' },
] );

// Delete remaining blocks.
// Keyboard shortcut should also work.
await pageUtils.pressKeys( 'access+z' );
await pageUtils.pressKeys( 'access+z' );
await expect
.poll(
listViewUtils.getBlocksWithA11yAttributes,
'Deleting the only block left will create a default block and focus/select it'
'Deleting the only blocks left will create a default block and focus/select it'
)
.toMatchObject( [
{
Expand Down