From e7c09860563936e1c69518a8a3b4e67339790c39 Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Tue, 31 Jan 2023 14:35:31 +0000 Subject: [PATCH 01/16] Define drag and drop contexts and compare to reject if not matching --- .../src/components/block-draggable/index.js | 2 ++ .../src/components/inner-blocks/index.js | 1 + .../off-canvas-editor/block-contents.js | 5 ++++- .../src/components/use-block-drop-zone/index.js | 2 ++ .../src/components/use-on-block-drop/index.js | 15 ++++++++++++--- 5 files changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.js b/packages/block-editor/src/components/block-draggable/index.js index 40f61bfc79ce4d..47766ecbd2f8b9 100644 --- a/packages/block-editor/src/components/block-draggable/index.js +++ b/packages/block-editor/src/components/block-draggable/index.js @@ -19,6 +19,7 @@ const BlockDraggable = ( { cloneClassname, onDragStart, onDragEnd, + context = 'canvas', } ) => { const { srcRootClientId, isDraggable, icon } = useSelect( ( select ) => { @@ -59,6 +60,7 @@ const BlockDraggable = ( { type: 'block', srcClientIds: clientIds, srcRootClientId, + context, }; return ( diff --git a/packages/block-editor/src/components/inner-blocks/index.js b/packages/block-editor/src/components/inner-blocks/index.js index d83f62cf2b45cd..cca27b5c9ed2d7 100644 --- a/packages/block-editor/src/components/inner-blocks/index.js +++ b/packages/block-editor/src/components/inner-blocks/index.js @@ -204,6 +204,7 @@ export function useInnerBlocksProps( props = {}, options = {} ) { const blockDropZoneRef = useBlockDropZone( { rootClientId: clientId, + context: 'canvas', } ); const ref = useMergeRefs( [ diff --git a/packages/block-editor/src/components/off-canvas-editor/block-contents.js b/packages/block-editor/src/components/off-canvas-editor/block-contents.js index 4048f25b49c989..4e41345ce6e34b 100644 --- a/packages/block-editor/src/components/off-canvas-editor/block-contents.js +++ b/packages/block-editor/src/components/off-canvas-editor/block-contents.js @@ -123,7 +123,10 @@ const ListViewBlockContents = forwardRef( } } /> ) } - + { ( { draggable, onDragStart, onDragEnd } ) => ( { const { @@ -78,8 +81,13 @@ export function onBlockDrop( srcClientIds: sourceClientIds, type: dropType, blocks, + context: dragContext, } = parseDropEvent( event ); + if ( context && dragContext && context !== dragContext ) { + return; + } + // If the user is inserting a block. if ( dropType === 'inserter' ) { clearSelectedBlock(); @@ -210,7 +218,7 @@ export default function useOnBlockDrop( targetBlockIndex, options = {} ) { - const { operation = 'insert' } = options; + const { operation = 'insert', context } = options; const hasUploadPermissions = useSelect( ( select ) => select( blockEditorStore ).getSettings().mediaUpload, [] @@ -307,7 +315,8 @@ export default function useOnBlockDrop( getClientIdsOfDescendants, moveBlocks, insertOrReplaceBlocks, - clearSelectedBlock + clearSelectedBlock, + context ); const _onFilesDrop = onFilesDrop( targetRootClientId, From fdc7599d24d5be67d6af56012e415ee23cb7fb2e Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Tue, 31 Jan 2023 15:02:26 +0000 Subject: [PATCH 02/16] Fixes and adds initial unit tests --- .../src/components/use-on-block-drop/index.js | 7 ++- .../use-on-block-drop/test/index.js | 48 +++++++++++++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/components/use-on-block-drop/index.js b/packages/block-editor/src/components/use-on-block-drop/index.js index 1bc4b7d44ea1df..3a8e9dd4a42c5a 100644 --- a/packages/block-editor/src/components/use-on-block-drop/index.js +++ b/packages/block-editor/src/components/use-on-block-drop/index.js @@ -84,10 +84,6 @@ export function onBlockDrop( context: dragContext, } = parseDropEvent( event ); - if ( context && dragContext && context !== dragContext ) { - return; - } - // If the user is inserting a block. if ( dropType === 'inserter' ) { clearSelectedBlock(); @@ -99,6 +95,9 @@ export function onBlockDrop( // If the user is moving a block. if ( dropType === 'block' ) { + if ( context && dragContext && context !== dragContext ) { + return; + } const sourceBlockIndex = getBlockIndex( sourceClientIds[ 0 ] ); // If the user is dropping to the same position, return early. diff --git a/packages/block-editor/src/components/use-on-block-drop/test/index.js b/packages/block-editor/src/components/use-on-block-drop/test/index.js index 1b95cc0085a79e..67f6cd0f710662 100644 --- a/packages/block-editor/src/components/use-on-block-drop/test/index.js +++ b/packages/block-editor/src/components/use-on-block-drop/test/index.js @@ -26,6 +26,7 @@ describe( 'parseDropEvent', () => { srcClientIds: [ 'abc' ], srcIndex: 1, type: 'block', + context: 'canvas', }; const event = { dataTransfer: { @@ -55,6 +56,7 @@ describe( 'parseDropEvent', () => { srcRootClientId: null, srcIndex: null, blocks: null, + context: null, ...rawDataTransfer, } ); } ); @@ -66,6 +68,7 @@ describe( 'parseDropEvent', () => { srcClientIds: null, srcIndex: null, type: null, + context: null, }; const event = { dataTransfer: { @@ -85,6 +88,7 @@ describe( 'parseDropEvent', () => { srcClientIds: null, srcIndex: null, type: null, + context: null, }; const event = {}; @@ -298,6 +302,50 @@ describe( 'onBlockDrop', () => { insertIndex ); } ); + + it( 'does nothing if the block is dropped into a context that does not match origin of the drag', () => { + const targetRootClientId = '1'; + const targetBlockIndex = 0; + + const dropContext = 'canvas'; + const dragContext = 'list-view'; + + const getBlockIndex = jest.fn( () => 1 ); + // Dragged block is being dropped as a descendant of itself. + const getClientIdsOfDescendants = jest.fn( () => [ + targetRootClientId, + ] ); + const moveBlocks = jest.fn(); + const insertOrReplaceBlocks = jest.fn(); + const clearSelectedBlock = jest.fn(); + + const event = { + dataTransfer: { + getData() { + return JSON.stringify( { + type: 'block', + srcRootClientId: '0', + srcClientIds: [ '5' ], + context: dragContext, + } ); + }, + }, + }; + + const eventHandler = onBlockDrop( + targetRootClientId, + targetBlockIndex, + getBlockIndex, + getClientIdsOfDescendants, + moveBlocks, + insertOrReplaceBlocks, + clearSelectedBlock, + dropContext + ); + eventHandler( event ); + + expect( moveBlocks ).not.toHaveBeenCalled(); + } ); } ); describe( 'onFilesDrop', () => { From 1e2b377ef1e152b87be17d25d8bf9cdb075adaef Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Wed, 1 Feb 2023 13:10:31 +0000 Subject: [PATCH 03/16] Remove default context value --- packages/block-editor/src/components/block-draggable/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/block-draggable/index.js b/packages/block-editor/src/components/block-draggable/index.js index 47766ecbd2f8b9..a6c551b0e711d9 100644 --- a/packages/block-editor/src/components/block-draggable/index.js +++ b/packages/block-editor/src/components/block-draggable/index.js @@ -19,7 +19,7 @@ const BlockDraggable = ( { cloneClassname, onDragStart, onDragEnd, - context = 'canvas', + context, } ) => { const { srcRootClientId, isDraggable, icon } = useSelect( ( select ) => { From 3c242afa41bbc5099914c7c3cd729c8172098b33 Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Wed, 1 Feb 2023 13:12:57 +0000 Subject: [PATCH 04/16] Only disable for offcanvas in Nav block --- .../src/components/off-canvas-editor/block-contents.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/off-canvas-editor/block-contents.js b/packages/block-editor/src/components/off-canvas-editor/block-contents.js index 4e41345ce6e34b..63d58d96c8c0be 100644 --- a/packages/block-editor/src/components/off-canvas-editor/block-contents.js +++ b/packages/block-editor/src/components/off-canvas-editor/block-contents.js @@ -125,7 +125,7 @@ const ListViewBlockContents = forwardRef( ) } { ( { draggable, onDragStart, onDragEnd } ) => ( Date: Wed, 1 Feb 2023 13:24:57 +0000 Subject: [PATCH 05/16] Improve nomenclature for clarity --- .../src/components/block-draggable/index.js | 4 ++-- .../src/components/inner-blocks/index.js | 2 +- .../off-canvas-editor/block-contents.js | 2 +- .../components/use-block-drop-zone/index.js | 4 ++-- .../src/components/use-on-block-drop/index.js | 18 +++++++++++------- .../components/use-on-block-drop/test/index.js | 18 +++++++++--------- 6 files changed, 26 insertions(+), 22 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.js b/packages/block-editor/src/components/block-draggable/index.js index a6c551b0e711d9..7dc0f267938982 100644 --- a/packages/block-editor/src/components/block-draggable/index.js +++ b/packages/block-editor/src/components/block-draggable/index.js @@ -19,7 +19,7 @@ const BlockDraggable = ( { cloneClassname, onDragStart, onDragEnd, - context, + dragOrigin, } ) => { const { srcRootClientId, isDraggable, icon } = useSelect( ( select ) => { @@ -60,7 +60,7 @@ const BlockDraggable = ( { type: 'block', srcClientIds: clientIds, srcRootClientId, - context, + dragOrigin, }; return ( diff --git a/packages/block-editor/src/components/inner-blocks/index.js b/packages/block-editor/src/components/inner-blocks/index.js index cca27b5c9ed2d7..359c37f9aaf59b 100644 --- a/packages/block-editor/src/components/inner-blocks/index.js +++ b/packages/block-editor/src/components/inner-blocks/index.js @@ -204,7 +204,7 @@ export function useInnerBlocksProps( props = {}, options = {} ) { const blockDropZoneRef = useBlockDropZone( { rootClientId: clientId, - context: 'canvas', + dropTargetName: 'canvas', } ); const ref = useMergeRefs( [ diff --git a/packages/block-editor/src/components/off-canvas-editor/block-contents.js b/packages/block-editor/src/components/off-canvas-editor/block-contents.js index 63d58d96c8c0be..2f5ae186641bc8 100644 --- a/packages/block-editor/src/components/off-canvas-editor/block-contents.js +++ b/packages/block-editor/src/components/off-canvas-editor/block-contents.js @@ -125,7 +125,7 @@ const ListViewBlockContents = forwardRef( ) } { ( { draggable, onDragStart, onDragEnd } ) => ( { const { @@ -81,7 +81,7 @@ export function onBlockDrop( srcClientIds: sourceClientIds, type: dropType, blocks, - context: dragContext, + dragOrigin, } = parseDropEvent( event ); // If the user is inserting a block. @@ -95,7 +95,11 @@ export function onBlockDrop( // If the user is moving a block. if ( dropType === 'block' ) { - if ( context && dragContext && context !== dragContext ) { + if ( + dropTargetName && + dragOrigin && + dropTargetName !== dragOrigin + ) { return; } const sourceBlockIndex = getBlockIndex( sourceClientIds[ 0 ] ); @@ -217,7 +221,7 @@ export default function useOnBlockDrop( targetBlockIndex, options = {} ) { - const { operation = 'insert', context } = options; + const { operation = 'insert', dropTargetName } = options; const hasUploadPermissions = useSelect( ( select ) => select( blockEditorStore ).getSettings().mediaUpload, [] @@ -315,7 +319,7 @@ export default function useOnBlockDrop( moveBlocks, insertOrReplaceBlocks, clearSelectedBlock, - context + dropTargetName ); const _onFilesDrop = onFilesDrop( targetRootClientId, diff --git a/packages/block-editor/src/components/use-on-block-drop/test/index.js b/packages/block-editor/src/components/use-on-block-drop/test/index.js index 67f6cd0f710662..f0341cfcc03970 100644 --- a/packages/block-editor/src/components/use-on-block-drop/test/index.js +++ b/packages/block-editor/src/components/use-on-block-drop/test/index.js @@ -26,7 +26,7 @@ describe( 'parseDropEvent', () => { srcClientIds: [ 'abc' ], srcIndex: 1, type: 'block', - context: 'canvas', + dragOrigin: 'canvas', }; const event = { dataTransfer: { @@ -56,7 +56,7 @@ describe( 'parseDropEvent', () => { srcRootClientId: null, srcIndex: null, blocks: null, - context: null, + dragOrigin: null, ...rawDataTransfer, } ); } ); @@ -68,7 +68,7 @@ describe( 'parseDropEvent', () => { srcClientIds: null, srcIndex: null, type: null, - context: null, + dragOrigin: null, }; const event = { dataTransfer: { @@ -88,7 +88,7 @@ describe( 'parseDropEvent', () => { srcClientIds: null, srcIndex: null, type: null, - context: null, + dragOrigin: null, }; const event = {}; @@ -303,12 +303,12 @@ describe( 'onBlockDrop', () => { ); } ); - it( 'does nothing if the block is dropped into a context that does not match origin of the drag', () => { + it( 'does nothing if the block is dropped into a drop target that does not match origin of the drag', () => { const targetRootClientId = '1'; const targetBlockIndex = 0; - const dropContext = 'canvas'; - const dragContext = 'list-view'; + const dropTargetName = 'canvas'; + const dragOrigin = 'list-view'; const getBlockIndex = jest.fn( () => 1 ); // Dragged block is being dropped as a descendant of itself. @@ -326,7 +326,7 @@ describe( 'onBlockDrop', () => { type: 'block', srcRootClientId: '0', srcClientIds: [ '5' ], - context: dragContext, + dragOrigin, } ); }, }, @@ -340,7 +340,7 @@ describe( 'onBlockDrop', () => { moveBlocks, insertOrReplaceBlocks, clearSelectedBlock, - dropContext + dropTargetName ); eventHandler( event ); From c29ceb166ac7deb3f5985a39d0a4f61bfc7fb55d Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Wed, 1 Feb 2023 18:06:27 +0000 Subject: [PATCH 06/16] Use prop to define inner blocks dropzone name --- packages/block-editor/README.md | 1 + .../src/components/inner-blocks/index.js | 16 ++++++++++------ .../src/components/use-block-drop-zone/index.js | 4 ++-- .../src/components/use-on-block-drop/index.js | 14 +++++--------- .../components/use-on-block-drop/test/index.js | 4 ++-- 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index 19433e516f4f5d..d0e8b5808dc785 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -830,6 +830,7 @@ _Parameters_ - _props_ `Object`: Optional. Props to pass to the element. Must contain the ref if one is defined. - _options_ `Object`: Optional. Inner blocks options. +- _options.dropZoneName_ `string`: Optional. The name of the drop zone. ### useSetting diff --git a/packages/block-editor/src/components/inner-blocks/index.js b/packages/block-editor/src/components/inner-blocks/index.js index 359c37f9aaf59b..b7eeb273fb0af9 100644 --- a/packages/block-editor/src/components/inner-blocks/index.js +++ b/packages/block-editor/src/components/inner-blocks/index.js @@ -154,15 +154,19 @@ const ForwardedInnerBlocks = forwardRef( ( props, ref ) => { * returns. Optionally, you can also pass any other props through this hook, and * they will be merged and returned. * - * @param {Object} props Optional. Props to pass to the element. Must contain - * the ref if one is defined. - * @param {Object} options Optional. Inner blocks options. + * @param {Object} props Optional. Props to pass to the element. Must contain + * the ref if one is defined. + * @param {Object} options Optional. Inner blocks options. * + * @param {string} options.dropZoneName Optional. The name of the drop zone. * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/inner-blocks/README.md */ export function useInnerBlocksProps( props = {}, options = {} ) { - const { __unstableDisableLayoutClassNames, __unstableDisableDropZone } = - options; + const { + __unstableDisableLayoutClassNames, + __unstableDisableDropZone, + __unstableDropZoneName = 'inner-blocks', + } = options; const { clientId, layout = null, @@ -204,7 +208,7 @@ export function useInnerBlocksProps( props = {}, options = {} ) { const blockDropZoneRef = useBlockDropZone( { rootClientId: clientId, - dropTargetName: 'canvas', + dropZoneName: __unstableDropZoneName, } ); const ref = useMergeRefs( [ diff --git a/packages/block-editor/src/components/use-block-drop-zone/index.js b/packages/block-editor/src/components/use-block-drop-zone/index.js index da02426a59973b..0dbe5f6934a591 100644 --- a/packages/block-editor/src/components/use-block-drop-zone/index.js +++ b/packages/block-editor/src/components/use-block-drop-zone/index.js @@ -141,7 +141,7 @@ export default function useBlockDropZone( { // values returned by the `getRootBlockClientId` selector, which also uses // an empty string to represent top-level blocks. rootClientId: targetRootClientId = '', - dropTargetName, + dropZoneName, } = {} ) { const [ dropTarget, setDropTarget ] = useState( { index: null, @@ -174,7 +174,7 @@ export default function useBlockDropZone( { const onBlockDrop = useOnBlockDrop( targetRootClientId, dropTarget.index, { operation: dropTarget.operation, - dropTargetName, + dropZoneName, } ); const throttled = useThrottle( useCallback( diff --git a/packages/block-editor/src/components/use-on-block-drop/index.js b/packages/block-editor/src/components/use-on-block-drop/index.js index 02b07fd33ee04e..b3a8025f198db0 100644 --- a/packages/block-editor/src/components/use-on-block-drop/index.js +++ b/packages/block-editor/src/components/use-on-block-drop/index.js @@ -62,7 +62,7 @@ export function parseDropEvent( event ) { * @param {Function} moveBlocks A function that moves blocks. * @param {Function} insertOrReplaceBlocks A function that inserts or replaces blocks. * @param {Function} clearSelectedBlock A function that clears block selection. - * @param {string} dropTargetName The dropTargetName where the drop event will occur (e.g. 'list-view', 'canvas) + * @param {string} dropZoneName The dropZoneName where the drop event will occur (e.g. 'list-view', 'canvas) * @return {Function} The event handler for a block drop event. */ export function onBlockDrop( @@ -73,7 +73,7 @@ export function onBlockDrop( moveBlocks, insertOrReplaceBlocks, clearSelectedBlock, - dropTargetName + dropZoneName ) { return ( event ) => { const { @@ -95,11 +95,7 @@ export function onBlockDrop( // If the user is moving a block. if ( dropType === 'block' ) { - if ( - dropTargetName && - dragOrigin && - dropTargetName !== dragOrigin - ) { + if ( dropZoneName && dragOrigin && dropZoneName !== dragOrigin ) { return; } const sourceBlockIndex = getBlockIndex( sourceClientIds[ 0 ] ); @@ -221,7 +217,7 @@ export default function useOnBlockDrop( targetBlockIndex, options = {} ) { - const { operation = 'insert', dropTargetName } = options; + const { operation = 'insert', dropZoneName } = options; const hasUploadPermissions = useSelect( ( select ) => select( blockEditorStore ).getSettings().mediaUpload, [] @@ -319,7 +315,7 @@ export default function useOnBlockDrop( moveBlocks, insertOrReplaceBlocks, clearSelectedBlock, - dropTargetName + dropZoneName ); const _onFilesDrop = onFilesDrop( targetRootClientId, diff --git a/packages/block-editor/src/components/use-on-block-drop/test/index.js b/packages/block-editor/src/components/use-on-block-drop/test/index.js index f0341cfcc03970..58428171e1bcb3 100644 --- a/packages/block-editor/src/components/use-on-block-drop/test/index.js +++ b/packages/block-editor/src/components/use-on-block-drop/test/index.js @@ -307,7 +307,7 @@ describe( 'onBlockDrop', () => { const targetRootClientId = '1'; const targetBlockIndex = 0; - const dropTargetName = 'canvas'; + const dropZoneName = 'canvas'; const dragOrigin = 'list-view'; const getBlockIndex = jest.fn( () => 1 ); @@ -340,7 +340,7 @@ describe( 'onBlockDrop', () => { moveBlocks, insertOrReplaceBlocks, clearSelectedBlock, - dropTargetName + dropZoneName ); eventHandler( event ); From f79b308c7b97245d8f114fa9580de586202afbad Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Thu, 2 Feb 2023 10:37:51 +0000 Subject: [PATCH 07/16] Track dragged blocks origin in state --- .../reference-guides/data/data-core-block-editor.md | 13 +++++++++++++ packages/block-editor/src/store/actions.js | 4 +++- packages/block-editor/src/store/reducer.js | 13 +++++++++++++ packages/block-editor/src/store/selectors.js | 10 ++++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/docs/reference-guides/data/data-core-block-editor.md b/docs/reference-guides/data/data-core-block-editor.md index cfb95389ba0391..8b3c9edb0879ac 100644 --- a/docs/reference-guides/data/data-core-block-editor.md +++ b/docs/reference-guides/data/data-core-block-editor.md @@ -518,6 +518,18 @@ _Returns_ - `string[]`: Array of dragged block client ids. +### getDraggedBlocksOrigin + +Returns the origin of the dragged blocks (if any). + +_Parameters_ + +- _state_ `Object`: Global application state. + +_Returns_ + +- `string`: The origin of the dragged blocks. + ### getFirstMultiSelectedBlockClientId Returns the client ID of the first block in the multi-selection set, or null @@ -1573,6 +1585,7 @@ Returns an action object used in signalling that the user has begun to drag bloc _Parameters_ - _clientIds_ `string[]`: An array of client ids being dragged +- _origin_ `string`: the name of the origin of the drag blocks. _Returns_ diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js index 98bdf1bffc78c0..7feef366d2ed10 100644 --- a/packages/block-editor/src/store/actions.js +++ b/packages/block-editor/src/store/actions.js @@ -1291,12 +1291,14 @@ export function stopTyping() { * * @param {string[]} clientIds An array of client ids being dragged * + * @param {string} origin the name of the origin of the drag blocks. * @return {Object} Action object. */ -export function startDraggingBlocks( clientIds = [] ) { +export function startDraggingBlocks( clientIds = [], origin ) { return { type: 'START_DRAGGING_BLOCKS', clientIds, + origin, }; } diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index c207df38692b2a..78fec99fedda1f 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1247,6 +1247,18 @@ export function draggedBlocks( state = [], action ) { return state; } +export function draggedBlocksOrigin( state = '', action ) { + switch ( action.type ) { + case 'START_DRAGGING_BLOCKS': + return action.origin; + + case 'STOP_DRAGGING_BLOCKS': + return ''; + } + + return state; +} + /** * Reducer tracking the visible blocks. * @@ -1886,4 +1898,5 @@ export default combineReducers( { lastBlockInserted, temporarilyEditingAsBlocks, blockVisibility, + draggedBlocksOrigin, } ); diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 5b70183acccf2a..c1cb7ac1580e47 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -1303,6 +1303,16 @@ export function getDraggedBlockClientIds( state ) { return state.draggedBlocks; } +/** + * Returns the origin of the dragged blocks (if any). + * + * @param {Object} state Global application state. + * @return {string} The origin of the dragged blocks. + */ +export function getDraggedBlocksOrigin( state ) { + return state.draggedBlocksOrigin; +} + /** * Returns whether the block is being dragged. * From f37c4eb286c728a49e2380dbdc8a914cecd06679 Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Thu, 2 Feb 2023 10:38:53 +0000 Subject: [PATCH 08/16] Compare dragged blocks origin in dragOver event to prevent UI affordances --- .../src/components/block-draggable/index.js | 2 +- .../components/use-block-drop-zone/index.js | 20 +++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.js b/packages/block-editor/src/components/block-draggable/index.js index 7dc0f267938982..47bba1e2aca6a9 100644 --- a/packages/block-editor/src/components/block-draggable/index.js +++ b/packages/block-editor/src/components/block-draggable/index.js @@ -69,7 +69,7 @@ const BlockDraggable = ( { __experimentalTransferDataType="wp-blocks" transferData={ transferData } onDragStart={ ( event ) => { - startDraggingBlocks( clientIds ); + startDraggingBlocks( clientIds, dragOrigin ); isDragging.current = true; startScrolling( event ); diff --git a/packages/block-editor/src/components/use-block-drop-zone/index.js b/packages/block-editor/src/components/use-block-drop-zone/index.js index 0dbe5f6934a591..8a53b86c0b339f 100644 --- a/packages/block-editor/src/components/use-block-drop-zone/index.js +++ b/packages/block-editor/src/components/use-block-drop-zone/index.js @@ -167,8 +167,13 @@ export default function useBlockDropZone( { [ targetRootClientId ] ); - const { getBlockListSettings, getBlocks, getBlockIndex } = - useSelect( blockEditorStore ); + const { + getBlockListSettings, + getBlocks, + getBlockIndex, + getDraggedBlocksOrigin, + } = useSelect( blockEditorStore ); + const { showInsertionPoint, hideInsertionPoint } = useDispatch( blockEditorStore ); @@ -179,6 +184,17 @@ export default function useBlockDropZone( { const throttled = useThrottle( useCallback( ( event, ownerDocument ) => { + const draggedBlocksOrigin = getDraggedBlocksOrigin(); + + if ( + dropZoneName && + draggedBlocksOrigin && + dropZoneName !== draggedBlocksOrigin + ) { + // If drag and drop names are available and they don't match, don't allow dropping. + return; + } + const blocks = getBlocks( targetRootClientId ); // The block list is empty, don't show the insertion point but still allow dropping. From eee42f2e2b9a8e27119b9ae81543bb5d99d6f112 Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Thu, 2 Feb 2023 10:57:18 +0000 Subject: [PATCH 09/16] Add tests for new state --- .../block-editor/src/store/test/actions.js | 11 ++++++++ .../block-editor/src/store/test/reducer.js | 25 +++++++++++++++++++ .../block-editor/src/store/test/selectors.js | 11 ++++++++ 3 files changed, 47 insertions(+) diff --git a/packages/block-editor/src/store/test/actions.js b/packages/block-editor/src/store/test/actions.js index 45e432ad8bf3f8..c0ec8656255e3b 100644 --- a/packages/block-editor/src/store/test/actions.js +++ b/packages/block-editor/src/store/test/actions.js @@ -799,6 +799,17 @@ describe( 'actions', () => { clientIds, } ); } ); + + it( 'should optionally return a drag origin with START_DRAGGING_BLOCKS action', () => { + const clientIds = [ 'block-1', 'block-2', 'block-3' ]; + expect( startDraggingBlocks( clientIds, 'inner-blocks' ) ).toEqual( + { + type: 'START_DRAGGING_BLOCKS', + clientIds, + origin: 'inner-blocks', + } + ); + } ); } ); describe( 'stopDraggingBlocks', () => { diff --git a/packages/block-editor/src/store/test/reducer.js b/packages/block-editor/src/store/test/reducer.js index 609cbb59c6e54b..7d6f327b0e2325 100644 --- a/packages/block-editor/src/store/test/reducer.js +++ b/packages/block-editor/src/store/test/reducer.js @@ -32,6 +32,7 @@ import { blockListSettings, lastBlockAttributesChange, lastBlockInserted, + draggedBlocksOrigin, } from '../reducer'; const noop = () => {}; @@ -2464,6 +2465,30 @@ describe( 'state', () => { } ); } ); + describe( 'draggedBlocksOrigin', () => { + it.each( [ '', 'inner-blocks' ] )( + `should store the dragged blocks' origin as "%s" when a user starts dragging blocks`, + ( theOrigin ) => { + const clientIds = [ 'block-1', 'block-2', 'block-3' ]; + const state = draggedBlocksOrigin( [], { + type: 'START_DRAGGING_BLOCKS', + clientIds, + origin: theOrigin, + } ); + + expect( state ).toBe( theOrigin ); + } + ); + + it( `should set the state to an empty string when a user stops dragging blocks`, () => { + const state = draggedBlocksOrigin( [], { + type: 'STOP_DRAGGING_BLOCKS', + } ); + + expect( state ).toBe( '' ); + } ); + } ); + describe( 'initialPosition()', () => { it( 'should return with block clientId as selected', () => { const state = initialPosition( undefined, { diff --git a/packages/block-editor/src/store/test/selectors.js b/packages/block-editor/src/store/test/selectors.js index 60d90d80b9d41e..9c2b3ec9249275 100644 --- a/packages/block-editor/src/store/test/selectors.js +++ b/packages/block-editor/src/store/test/selectors.js @@ -72,6 +72,7 @@ const { __experimentalGetPatternTransformItems, wasBlockJustInserted, __experimentalGetGlobalBlocksByName, + getDraggedBlocksOrigin, } = selectors; describe( 'selectors', () => { @@ -2301,6 +2302,16 @@ describe( 'selectors', () => { } ); } ); + describe( 'getDraggedBlocksOrigin', () => { + it( 'returns the draggedBlocks origin', () => { + const theOrigin = 'inner-blocks'; + const state = { + draggedBlocksOrigin: theOrigin, + }; + expect( getDraggedBlocksOrigin( state ) ).toBe( theOrigin ); + } ); + } ); + describe( 'isBlockBeingDragged', () => { it( 'returns true if the given client id is one of the blocks being dragged', () => { const state = { From f5ac858cf720396d4c059895e7c0a95bc3c9d888 Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Fri, 3 Feb 2023 09:06:45 +0000 Subject: [PATCH 10/16] Make getDraggedBlocksOrigin private --- docs/reference-guides/data/data-core-block-editor.md | 12 ------------ .../src/components/use-block-drop-zone/index.js | 3 ++- packages/block-editor/src/store/private-selectors.js | 10 ++++++++++ packages/block-editor/src/store/selectors.js | 10 ---------- .../block-editor/src/store/test/private-selectors.js | 11 +++++++++++ packages/block-editor/src/store/test/selectors.js | 11 ----------- 6 files changed, 23 insertions(+), 34 deletions(-) diff --git a/docs/reference-guides/data/data-core-block-editor.md b/docs/reference-guides/data/data-core-block-editor.md index 8b3c9edb0879ac..696e9ac1203edd 100644 --- a/docs/reference-guides/data/data-core-block-editor.md +++ b/docs/reference-guides/data/data-core-block-editor.md @@ -518,18 +518,6 @@ _Returns_ - `string[]`: Array of dragged block client ids. -### getDraggedBlocksOrigin - -Returns the origin of the dragged blocks (if any). - -_Parameters_ - -- _state_ `Object`: Global application state. - -_Returns_ - -- `string`: The origin of the dragged blocks. - ### getFirstMultiSelectedBlockClientId Returns the client ID of the first block in the multi-selection set, or null diff --git a/packages/block-editor/src/components/use-block-drop-zone/index.js b/packages/block-editor/src/components/use-block-drop-zone/index.js index 8a53b86c0b339f..b32af18cdbcc62 100644 --- a/packages/block-editor/src/components/use-block-drop-zone/index.js +++ b/packages/block-editor/src/components/use-block-drop-zone/index.js @@ -19,6 +19,7 @@ import { isPointContainedByRect, } from '../../utils/math'; import { store as blockEditorStore } from '../../store'; +import { unlock } from '../../experiments'; /** @typedef {import('../../utils/math').WPPoint} WPPoint */ /** @typedef {import('../use-on-block-drop/types').WPDropOperation} WPDropOperation */ @@ -172,7 +173,7 @@ export default function useBlockDropZone( { getBlocks, getBlockIndex, getDraggedBlocksOrigin, - } = useSelect( blockEditorStore ); + } = unlock( useSelect( blockEditorStore ) ); const { showInsertionPoint, hideInsertionPoint } = useDispatch( blockEditorStore ); diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js index 60712e6b8eb6e0..4d8ecb05ef598a 100644 --- a/packages/block-editor/src/store/private-selectors.js +++ b/packages/block-editor/src/store/private-selectors.js @@ -18,3 +18,13 @@ export function isBlockInterfaceHidden( state ) { export function getLastInsertedBlocksClientIds( state ) { return state?.lastBlockInserted?.clientIds; } + +/** + * Returns the origin of the dragged blocks (if any). + * + * @param {Object} state Global application state. + * @return {string} The origin of the dragged blocks. + */ +export function getDraggedBlocksOrigin( state ) { + return state.draggedBlocksOrigin; +} diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index c1cb7ac1580e47..5b70183acccf2a 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -1303,16 +1303,6 @@ export function getDraggedBlockClientIds( state ) { return state.draggedBlocks; } -/** - * Returns the origin of the dragged blocks (if any). - * - * @param {Object} state Global application state. - * @return {string} The origin of the dragged blocks. - */ -export function getDraggedBlocksOrigin( state ) { - return state.draggedBlocksOrigin; -} - /** * Returns whether the block is being dragged. * diff --git a/packages/block-editor/src/store/test/private-selectors.js b/packages/block-editor/src/store/test/private-selectors.js index c5df265f75db35..b7e0274f3c0520 100644 --- a/packages/block-editor/src/store/test/private-selectors.js +++ b/packages/block-editor/src/store/test/private-selectors.js @@ -4,6 +4,7 @@ import { isBlockInterfaceHidden, getLastInsertedBlocksClientIds, + getDraggedBlocksOrigin, } from '../private-selectors'; describe( 'private selectors', () => { @@ -49,4 +50,14 @@ describe( 'private selectors', () => { ] ); } ); } ); + + describe( 'getDraggedBlocksOrigin', () => { + it( 'returns the draggedBlocks origin', () => { + const theOrigin = 'inner-blocks'; + const state = { + draggedBlocksOrigin: theOrigin, + }; + expect( getDraggedBlocksOrigin( state ) ).toBe( theOrigin ); + } ); + } ); } ); diff --git a/packages/block-editor/src/store/test/selectors.js b/packages/block-editor/src/store/test/selectors.js index 9c2b3ec9249275..60d90d80b9d41e 100644 --- a/packages/block-editor/src/store/test/selectors.js +++ b/packages/block-editor/src/store/test/selectors.js @@ -72,7 +72,6 @@ const { __experimentalGetPatternTransformItems, wasBlockJustInserted, __experimentalGetGlobalBlocksByName, - getDraggedBlocksOrigin, } = selectors; describe( 'selectors', () => { @@ -2302,16 +2301,6 @@ describe( 'selectors', () => { } ); } ); - describe( 'getDraggedBlocksOrigin', () => { - it( 'returns the draggedBlocks origin', () => { - const theOrigin = 'inner-blocks'; - const state = { - draggedBlocksOrigin: theOrigin, - }; - expect( getDraggedBlocksOrigin( state ) ).toBe( theOrigin ); - } ); - } ); - describe( 'isBlockBeingDragged', () => { it( 'returns true if the given client id is one of the blocks being dragged', () => { const state = { From 4d3c52922e0b0a247ae3f857a15df095f91e9b06 Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Fri, 3 Feb 2023 10:48:14 +0000 Subject: [PATCH 11/16] Refactor to allow Draggables to whitelist target dropzones --- .../data/data-core-block-editor.md | 2 +- .../src/components/block-draggable/index.js | 6 ++-- .../list-view/use-list-view-drop-zone.js | 20 +++++++++++-- .../off-canvas-editor/block-contents.js | 2 +- .../src/components/off-canvas-editor/index.js | 3 +- .../use-list-view-drop-zone.js | 20 +++++++++++-- .../components/use-block-drop-zone/index.js | 11 ++++--- .../src/components/use-on-block-drop/index.js | 9 ++++-- packages/block-editor/src/store/actions.js | 6 ++-- .../src/store/private-selectors.js | 4 +-- packages/block-editor/src/store/reducer.js | 9 +++--- .../src/store/test/private-selectors.js | 10 +++---- .../block-editor/src/store/test/reducer.js | 29 ++++++++++--------- 13 files changed, 82 insertions(+), 49 deletions(-) diff --git a/docs/reference-guides/data/data-core-block-editor.md b/docs/reference-guides/data/data-core-block-editor.md index 696e9ac1203edd..fc2037333026d8 100644 --- a/docs/reference-guides/data/data-core-block-editor.md +++ b/docs/reference-guides/data/data-core-block-editor.md @@ -1573,7 +1573,7 @@ Returns an action object used in signalling that the user has begun to drag bloc _Parameters_ - _clientIds_ `string[]`: An array of client ids being dragged -- _origin_ `string`: the name of the origin of the drag blocks. +- _targets_ `Array`: the names of target dropzones into which this draggable can be dropped. _Returns_ diff --git a/packages/block-editor/src/components/block-draggable/index.js b/packages/block-editor/src/components/block-draggable/index.js index 47bba1e2aca6a9..c2fd52c23f6bcc 100644 --- a/packages/block-editor/src/components/block-draggable/index.js +++ b/packages/block-editor/src/components/block-draggable/index.js @@ -19,7 +19,7 @@ const BlockDraggable = ( { cloneClassname, onDragStart, onDragEnd, - dragOrigin, + targets = [], } ) => { const { srcRootClientId, isDraggable, icon } = useSelect( ( select ) => { @@ -60,7 +60,7 @@ const BlockDraggable = ( { type: 'block', srcClientIds: clientIds, srcRootClientId, - dragOrigin, + targets, }; return ( @@ -69,7 +69,7 @@ const BlockDraggable = ( { __experimentalTransferDataType="wp-blocks" transferData={ transferData } onDragStart={ ( event ) => { - startDraggingBlocks( clientIds, dragOrigin ); + startDraggingBlocks( clientIds, targets ); isDragging.current = true; startScrolling( event ); diff --git a/packages/block-editor/src/components/list-view/use-list-view-drop-zone.js b/packages/block-editor/src/components/list-view/use-list-view-drop-zone.js index 680beafd3c07cd..28550ceb17a188 100644 --- a/packages/block-editor/src/components/list-view/use-list-view-drop-zone.js +++ b/packages/block-editor/src/components/list-view/use-list-view-drop-zone.js @@ -17,6 +17,7 @@ import { } from '../../utils/math'; import useOnBlockDrop from '../use-on-block-drop'; import { store as blockEditorStore } from '../../store'; +import { unlock } from '../../experiments'; /** @typedef {import('../../utils/math').WPPoint} WPPoint */ @@ -179,26 +180,39 @@ function getListViewDropTarget( blocksData, position ) { /** * A react hook for implementing a drop zone in list view. * + * @param {string} dropZoneName the name of the drop zone. * @return {WPListViewDropZoneTarget} The drop target. */ -export default function useListViewDropZone() { +export default function useListViewDropZone( dropZoneName ) { const { getBlockRootClientId, getBlockIndex, getBlockCount, getDraggedBlockClientIds, canInsertBlocks, - } = useSelect( blockEditorStore ); + getDraggedBlocksTargets, + } = unlock( useSelect( blockEditorStore ) ); const [ target, setTarget ] = useState(); const { rootClientId: targetRootClientId, blockIndex: targetBlockIndex } = target || {}; - const onBlockDrop = useOnBlockDrop( targetRootClientId, targetBlockIndex ); + const onBlockDrop = useOnBlockDrop( targetRootClientId, targetBlockIndex, { + dropZoneName, + } ); const draggedBlockClientIds = getDraggedBlockClientIds(); const throttled = useThrottle( useCallback( ( event, currentTarget ) => { + const draggedBlocksTargets = getDraggedBlocksTargets(); + + if ( + draggedBlocksTargets?.length && + ! draggedBlocksTargets.includes( dropZoneName ) + ) { + // If drag targets are defined and the drop zone doesn't match, don't allow dropping. + return; + } const position = { x: event.clientX, y: event.clientY }; const isBlockDrag = !! draggedBlockClientIds?.length; diff --git a/packages/block-editor/src/components/off-canvas-editor/block-contents.js b/packages/block-editor/src/components/off-canvas-editor/block-contents.js index 2f5ae186641bc8..e0480170e215b9 100644 --- a/packages/block-editor/src/components/off-canvas-editor/block-contents.js +++ b/packages/block-editor/src/components/off-canvas-editor/block-contents.js @@ -125,7 +125,7 @@ const ListViewBlockContents = forwardRef( ) } { ( { draggable, onDragStart, onDragEnd } ) => ( { + const draggedBlocksTargets = getDraggedBlocksTargets(); + + if ( + draggedBlocksTargets?.length && + ! draggedBlocksTargets.includes( dropZoneName ) + ) { + // If drag targets are defined and the drop zone doesn't match, don't allow dropping. + return; + } const position = { x: event.clientX, y: event.clientY }; const isBlockDrag = !! draggedBlockClientIds?.length; diff --git a/packages/block-editor/src/components/use-block-drop-zone/index.js b/packages/block-editor/src/components/use-block-drop-zone/index.js index b32af18cdbcc62..fd6eb588924ec0 100644 --- a/packages/block-editor/src/components/use-block-drop-zone/index.js +++ b/packages/block-editor/src/components/use-block-drop-zone/index.js @@ -172,7 +172,7 @@ export default function useBlockDropZone( { getBlockListSettings, getBlocks, getBlockIndex, - getDraggedBlocksOrigin, + getDraggedBlocksTargets, } = unlock( useSelect( blockEditorStore ) ); const { showInsertionPoint, hideInsertionPoint } = @@ -185,14 +185,13 @@ export default function useBlockDropZone( { const throttled = useThrottle( useCallback( ( event, ownerDocument ) => { - const draggedBlocksOrigin = getDraggedBlocksOrigin(); + const draggedBlocksTargets = getDraggedBlocksTargets(); if ( - dropZoneName && - draggedBlocksOrigin && - dropZoneName !== draggedBlocksOrigin + draggedBlocksTargets?.length && + ! draggedBlocksTargets.includes( dropZoneName ) ) { - // If drag and drop names are available and they don't match, don't allow dropping. + // If drag targets are defined and the drop zone doesn't match, don't allow dropping. return; } diff --git a/packages/block-editor/src/components/use-on-block-drop/index.js b/packages/block-editor/src/components/use-on-block-drop/index.js index b3a8025f198db0..17958aea76e489 100644 --- a/packages/block-editor/src/components/use-on-block-drop/index.js +++ b/packages/block-editor/src/components/use-on-block-drop/index.js @@ -33,7 +33,7 @@ export function parseDropEvent( event ) { srcIndex: null, type: null, blocks: null, - dragOrigin: null, + targets: null, }; if ( ! event.dataTransfer ) { @@ -81,7 +81,7 @@ export function onBlockDrop( srcClientIds: sourceClientIds, type: dropType, blocks, - dragOrigin, + targets: draggableTargets, } = parseDropEvent( event ); // If the user is inserting a block. @@ -95,7 +95,10 @@ export function onBlockDrop( // If the user is moving a block. if ( dropType === 'block' ) { - if ( dropZoneName && dragOrigin && dropZoneName !== dragOrigin ) { + if ( + draggableTargets?.length && + ! draggableTargets.includes( dropZoneName ) + ) { return; } const sourceBlockIndex = getBlockIndex( sourceClientIds[ 0 ] ); diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js index 7feef366d2ed10..e22964043c2917 100644 --- a/packages/block-editor/src/store/actions.js +++ b/packages/block-editor/src/store/actions.js @@ -1291,14 +1291,14 @@ export function stopTyping() { * * @param {string[]} clientIds An array of client ids being dragged * - * @param {string} origin the name of the origin of the drag blocks. + * @param {Array} targets the names of target dropzones into which this draggable can be dropped. * @return {Object} Action object. */ -export function startDraggingBlocks( clientIds = [], origin ) { +export function startDraggingBlocks( clientIds = [], targets ) { return { type: 'START_DRAGGING_BLOCKS', clientIds, - origin, + targets, }; } diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js index 4d8ecb05ef598a..e48d3913caf4b7 100644 --- a/packages/block-editor/src/store/private-selectors.js +++ b/packages/block-editor/src/store/private-selectors.js @@ -25,6 +25,6 @@ export function getLastInsertedBlocksClientIds( state ) { * @param {Object} state Global application state. * @return {string} The origin of the dragged blocks. */ -export function getDraggedBlocksOrigin( state ) { - return state.draggedBlocksOrigin; +export function getDraggedBlocksTargets( state ) { + return state.draggedBlocksTargets; } diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 78fec99fedda1f..b4c09edba3d875 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1247,13 +1247,12 @@ export function draggedBlocks( state = [], action ) { return state; } -export function draggedBlocksOrigin( state = '', action ) { +export function draggedBlocksTargets( state = [], action ) { switch ( action.type ) { case 'START_DRAGGING_BLOCKS': - return action.origin; - + return action.targets; case 'STOP_DRAGGING_BLOCKS': - return ''; + return []; } return state; @@ -1898,5 +1897,5 @@ export default combineReducers( { lastBlockInserted, temporarilyEditingAsBlocks, blockVisibility, - draggedBlocksOrigin, + draggedBlocksTargets, } ); diff --git a/packages/block-editor/src/store/test/private-selectors.js b/packages/block-editor/src/store/test/private-selectors.js index b7e0274f3c0520..6bca596684bce1 100644 --- a/packages/block-editor/src/store/test/private-selectors.js +++ b/packages/block-editor/src/store/test/private-selectors.js @@ -4,7 +4,7 @@ import { isBlockInterfaceHidden, getLastInsertedBlocksClientIds, - getDraggedBlocksOrigin, + getDraggedBlocksTargets, } from '../private-selectors'; describe( 'private selectors', () => { @@ -51,13 +51,13 @@ describe( 'private selectors', () => { } ); } ); - describe( 'getDraggedBlocksOrigin', () => { + describe( 'getDraggedBlocksTargets', () => { it( 'returns the draggedBlocks origin', () => { - const theOrigin = 'inner-blocks'; + const targets = [ 'inner-blocks', 'list-view' ]; const state = { - draggedBlocksOrigin: theOrigin, + draggedBlocksTargets: targets, }; - expect( getDraggedBlocksOrigin( state ) ).toBe( theOrigin ); + expect( getDraggedBlocksTargets( state ) ).toBe( targets ); } ); } ); } ); diff --git a/packages/block-editor/src/store/test/reducer.js b/packages/block-editor/src/store/test/reducer.js index 7d6f327b0e2325..3eb029be4705fe 100644 --- a/packages/block-editor/src/store/test/reducer.js +++ b/packages/block-editor/src/store/test/reducer.js @@ -32,7 +32,7 @@ import { blockListSettings, lastBlockAttributesChange, lastBlockInserted, - draggedBlocksOrigin, + draggedBlocksTargets, } from '../reducer'; const noop = () => {}; @@ -2465,27 +2465,30 @@ describe( 'state', () => { } ); } ); - describe( 'draggedBlocksOrigin', () => { - it.each( [ '', 'inner-blocks' ] )( - `should store the dragged blocks' origin as "%s" when a user starts dragging blocks`, - ( theOrigin ) => { + describe( 'draggedBlocksTargets', () => { + it.each( [ [ '' ], [ 'inner-blocks', 'list-view' ] ] )( + `should store the dragged blocks' targets as "%s" when a user starts dragging blocks`, + ( targets ) => { const clientIds = [ 'block-1', 'block-2', 'block-3' ]; - const state = draggedBlocksOrigin( [], { + const state = draggedBlocksTargets( [], { type: 'START_DRAGGING_BLOCKS', clientIds, - origin: theOrigin, + targets, } ); - expect( state ).toBe( theOrigin ); + expect( state ).toEqual( targets ); } ); - it( `should set the state to an empty string when a user stops dragging blocks`, () => { - const state = draggedBlocksOrigin( [], { - type: 'STOP_DRAGGING_BLOCKS', - } ); + it( `should reset the state to an empty string when a user stops dragging blocks`, () => { + const state = draggedBlocksTargets( + [ 'inner-blocks', 'list-view' ], + { + type: 'STOP_DRAGGING_BLOCKS', + } + ); - expect( state ).toBe( '' ); + expect( state ).toEqual( [] ); } ); } ); From f4154efd7c5a78a0e435f7486044542eb3204236 Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Fri, 3 Feb 2023 10:58:45 +0000 Subject: [PATCH 12/16] Fix and augment drop tests --- .../use-on-block-drop/test/index.js | 58 ++++++++++++++++--- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/packages/block-editor/src/components/use-on-block-drop/test/index.js b/packages/block-editor/src/components/use-on-block-drop/test/index.js index 58428171e1bcb3..92690d7a00b619 100644 --- a/packages/block-editor/src/components/use-on-block-drop/test/index.js +++ b/packages/block-editor/src/components/use-on-block-drop/test/index.js @@ -26,7 +26,7 @@ describe( 'parseDropEvent', () => { srcClientIds: [ 'abc' ], srcIndex: 1, type: 'block', - dragOrigin: 'canvas', + targets: [ 'canvas' ], }; const event = { dataTransfer: { @@ -56,7 +56,7 @@ describe( 'parseDropEvent', () => { srcRootClientId: null, srcIndex: null, blocks: null, - dragOrigin: null, + targets: null, ...rawDataTransfer, } ); } ); @@ -68,7 +68,7 @@ describe( 'parseDropEvent', () => { srcClientIds: null, srcIndex: null, type: null, - dragOrigin: null, + targets: null, }; const event = { dataTransfer: { @@ -88,7 +88,7 @@ describe( 'parseDropEvent', () => { srcClientIds: null, srcIndex: null, type: null, - dragOrigin: null, + targets: null, }; const event = {}; @@ -303,12 +303,12 @@ describe( 'onBlockDrop', () => { ); } ); - it( 'does nothing if the block is dropped into a drop target that does not match origin of the drag', () => { + it( 'does nothing if the block is dropped into a drop zone that is not included in a list of valid targets', () => { const targetRootClientId = '1'; const targetBlockIndex = 0; const dropZoneName = 'canvas'; - const dragOrigin = 'list-view'; + const targets = [ 'list-view' ]; const getBlockIndex = jest.fn( () => 1 ); // Dragged block is being dropped as a descendant of itself. @@ -326,7 +326,51 @@ describe( 'onBlockDrop', () => { type: 'block', srcRootClientId: '0', srcClientIds: [ '5' ], - dragOrigin, + targets, + } ); + }, + }, + }; + + const eventHandler = onBlockDrop( + targetRootClientId, + targetBlockIndex, + getBlockIndex, + getClientIdsOfDescendants, + moveBlocks, + insertOrReplaceBlocks, + clearSelectedBlock, + dropZoneName + ); + eventHandler( event ); + + expect( moveBlocks ).not.toHaveBeenCalled(); + } ); + + it( 'does nothing if the block with targets is dropped into an unnamed dropzone', () => { + const targetRootClientId = '1'; + const targetBlockIndex = 0; + + const dropZoneName = ''; + const targets = [ 'list-view' ]; + + const getBlockIndex = jest.fn( () => 1 ); + // Dragged block is being dropped as a descendant of itself. + const getClientIdsOfDescendants = jest.fn( () => [ + targetRootClientId, + ] ); + const moveBlocks = jest.fn(); + const insertOrReplaceBlocks = jest.fn(); + const clearSelectedBlock = jest.fn(); + + const event = { + dataTransfer: { + getData() { + return JSON.stringify( { + type: 'block', + srcRootClientId: '0', + srcClientIds: [ '5' ], + targets, } ); }, }, From 028a8b50115f47349a98f98fbda3f2ef59d71dad Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Fri, 3 Feb 2023 11:10:30 +0000 Subject: [PATCH 13/16] Revert changes to file --- packages/block-editor/README.md | 1 - .../src/components/inner-blocks/index.js | 15 +++++---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index d0e8b5808dc785..19433e516f4f5d 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -830,7 +830,6 @@ _Parameters_ - _props_ `Object`: Optional. Props to pass to the element. Must contain the ref if one is defined. - _options_ `Object`: Optional. Inner blocks options. -- _options.dropZoneName_ `string`: Optional. The name of the drop zone. ### useSetting diff --git a/packages/block-editor/src/components/inner-blocks/index.js b/packages/block-editor/src/components/inner-blocks/index.js index b7eeb273fb0af9..d83f62cf2b45cd 100644 --- a/packages/block-editor/src/components/inner-blocks/index.js +++ b/packages/block-editor/src/components/inner-blocks/index.js @@ -154,19 +154,15 @@ const ForwardedInnerBlocks = forwardRef( ( props, ref ) => { * returns. Optionally, you can also pass any other props through this hook, and * they will be merged and returned. * - * @param {Object} props Optional. Props to pass to the element. Must contain - * the ref if one is defined. - * @param {Object} options Optional. Inner blocks options. + * @param {Object} props Optional. Props to pass to the element. Must contain + * the ref if one is defined. + * @param {Object} options Optional. Inner blocks options. * - * @param {string} options.dropZoneName Optional. The name of the drop zone. * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/inner-blocks/README.md */ export function useInnerBlocksProps( props = {}, options = {} ) { - const { - __unstableDisableLayoutClassNames, - __unstableDisableDropZone, - __unstableDropZoneName = 'inner-blocks', - } = options; + const { __unstableDisableLayoutClassNames, __unstableDisableDropZone } = + options; const { clientId, layout = null, @@ -208,7 +204,6 @@ export function useInnerBlocksProps( props = {}, options = {} ) { const blockDropZoneRef = useBlockDropZone( { rootClientId: clientId, - dropZoneName: __unstableDropZoneName, } ); const ref = useMergeRefs( [ From 6e1991d5def3ed52ce2f8432607aa2fd261254b1 Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Fri, 3 Feb 2023 11:58:50 +0000 Subject: [PATCH 14/16] Fix test --- packages/block-editor/src/store/test/actions.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/block-editor/src/store/test/actions.js b/packages/block-editor/src/store/test/actions.js index c0ec8656255e3b..a6c746bd9cd54a 100644 --- a/packages/block-editor/src/store/test/actions.js +++ b/packages/block-editor/src/store/test/actions.js @@ -802,13 +802,13 @@ describe( 'actions', () => { it( 'should optionally return a drag origin with START_DRAGGING_BLOCKS action', () => { const clientIds = [ 'block-1', 'block-2', 'block-3' ]; - expect( startDraggingBlocks( clientIds, 'inner-blocks' ) ).toEqual( - { - type: 'START_DRAGGING_BLOCKS', - clientIds, - origin: 'inner-blocks', - } - ); + expect( + startDraggingBlocks( clientIds, [ 'inner-blocks' ] ) + ).toEqual( { + type: 'START_DRAGGING_BLOCKS', + clientIds, + targets: [ 'inner-blocks' ], + } ); } ); } ); From acdf784f7d6f7271fd3c2fcf88ecaa18aff3f959 Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Mon, 6 Feb 2023 06:12:57 +0000 Subject: [PATCH 15/16] Add docs to BlockDraggable component --- .../src/components/block-draggable/README.md | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 packages/block-editor/src/components/block-draggable/README.md diff --git a/packages/block-editor/src/components/block-draggable/README.md b/packages/block-editor/src/components/block-draggable/README.md new file mode 100644 index 00000000000000..87a7eff8246ad0 --- /dev/null +++ b/packages/block-editor/src/components/block-draggable/README.md @@ -0,0 +1,71 @@ +# Block Draggable + +Block Draggable is a block-specific implementation of a `` which defines behaviour for dragged elements in a block editor context, including (but not limited to): + +- determining whether the given block(s) can be moved. +- setting information on the [`transferData` object](https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer). +- (optionally) setting the `target` dropzones with which this draggable is compatible. +- displaying a suitable "chip" during the drag operation. +- managing scrolling during the drag. + +Note that the majority of the behaviour is delegated to `Draggable` from `@wordpress/components`. + +## Usage + +```js +import { BlockDraggable } from '@wordpress/block-editor'; + +function MyComponent() { + return ; +} +``` + +## Props + +### clientIds + +- Type: `Array` +- Required: Yes + +Blocks IDs of candidates to be dragged. + +### targets + +- Type: `Array[string]` +- Required: No + +A list of dropzone names that this draggable considers valid drop targets. If provided then it will only be possible to drop the draggable in a _named_ dropzone that is included in the list. + +### `children` + +- Type: Function +- Required: No + +Component children as a function. The function receives the following arguments: + +- `draggable` - whether or not the block(s) are deemed to be draggable. +- `onDragStart` (optional) - an event handler to be passed as the `onDragStart` event prop of any child node. +- `onDragEnd` - an event handler to be passed as the `onDragEnd` event prop of any child node. + +See [`Draggable`](./packages/components/src/draggable/README.md) for more information. + +### cloneClassname + +- Type: `string` +- Required: No + +A className to be passed to the clone of the draggable. + +### `onDragStart` + +- Type: Function +- Required: No + +A function to be called on the `ondragstart` event. + +### `onDragEnd` + +- Type: Function +- Required: No + +A function to be called on the `ondragend` event. From 5bc777caa8b692993d3558ef59f3b6fac08762cd Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Mon, 6 Feb 2023 06:16:24 +0000 Subject: [PATCH 16/16] Document `dropZoneName` prop of drop zone hook --- .../src/components/use-block-drop-zone/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/block-editor/src/components/use-block-drop-zone/README.md b/packages/block-editor/src/components/use-block-drop-zone/README.md index 8aa7a98cf89d4a..d122aa8d0e2643 100644 --- a/packages/block-editor/src/components/use-block-drop-zone/README.md +++ b/packages/block-editor/src/components/use-block-drop-zone/README.md @@ -1,3 +1,12 @@ # useBlockDropZone `useBlockDropZone` is a React hook used to specify a drop zone for a block. This drop zone supports the drag and drop of media into the editor. + +## Props + +### dropZoneName + +- Type: `string` +- Required: No + +An optional name for the dropzone. Note that if a name is provided then only ``s that specify this name in their `targets` prop will be able to be dropped.