From 7fe9baa8aaa8a432ee4ee3b47646516ffdaa4800 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 14 Jan 2021 10:42:08 +0100 Subject: [PATCH 01/25] Avoid focusing blocks when inserting them into the canvas --- .../developers/data/data-core-block-editor.md | 1 + .../src/components/block-list/block.js | 4 +-- .../src/components/block-list/index.js | 2 ++ .../use-focus-first-element.js | 4 +-- .../components/block-navigation/appender.js | 1 - .../components/button-block-appender/index.js | 9 +---- packages/block-editor/src/components/index.js | 5 +-- .../inserter/hooks/use-insertion-point.js | 34 ++++++------------- .../src/components/inserter/index.js | 24 +++++-------- .../src/components/inserter/library.js | 4 --- .../src/components/inserter/menu.js | 5 --- .../src/components/inserter/quick-inserter.js | 3 -- .../src/components/inserter/search-results.js | 2 -- .../index.js | 29 ++++------------ packages/block-editor/src/store/reducer.js | 13 +++---- packages/block-editor/src/store/selectors.js | 1 + .../src/components/visual-editor/index.js | 2 -- 17 files changed, 42 insertions(+), 101 deletions(-) rename packages/block-editor/src/components/{multi-select-scroll-into-view => selection-scroll-into-view}/index.js (69%) diff --git a/docs/designers-developers/developers/data/data-core-block-editor.md b/docs/designers-developers/developers/data/data-core-block-editor.md index ded41c1a63dfc6..b9531ae5edda0b 100644 --- a/docs/designers-developers/developers/data/data-core-block-editor.md +++ b/docs/designers-developers/developers/data/data-core-block-editor.md @@ -657,6 +657,7 @@ _Returns_ Returns the initial caret position for the selected block. This position is to used to position the caret properly when the selected block changes. +If the current block is not a RichText, having initial position set to 0 means "focus block" _Parameters_ diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index 8169972192ad1a..6338c767bf9274 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -376,7 +376,7 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => { const { clientId, rootClientId } = ownProps; const { getBlockIndex } = select( blockEditorStore ); const index = getBlockIndex( clientId, rootClientId ); - insertBlocks( blocks, index + 1, rootClientId ); + insertBlocks( blocks, index + 1, rootClientId, 0 ); }, onMerge( forward ) { const { clientId } = ownProps; @@ -409,7 +409,7 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => { [ ownProps.clientId ], blocks, indexToSelect, - initialPosition + initialPosition || 0 ); }, toggleSelection( selectionEnabled ) { diff --git a/packages/block-editor/src/components/block-list/index.js b/packages/block-editor/src/components/block-list/index.js index 0192290a585e9b..292243ea02c545 100644 --- a/packages/block-editor/src/components/block-list/index.js +++ b/packages/block-editor/src/components/block-list/index.js @@ -18,6 +18,7 @@ import useBlockDropZone from '../use-block-drop-zone'; import useInsertionPoint from './insertion-point'; import BlockPopover from './block-popover'; import { store as blockEditorStore } from '../../store'; +import { useScrollSelectionIntoView } from '../selection-scroll-into-view'; /** * If the block count exceeds the threshold, we disable the reordering animation @@ -32,6 +33,7 @@ export default function BlockList( { className } ) { const ref = useRef(); const [ blockNodes, setBlockNodes ] = useState( {} ); const insertionPoint = useInsertionPoint( ref ); + useScrollSelectionIntoView( ref ); return ( diff --git a/packages/block-editor/src/components/block-list/use-block-props/use-focus-first-element.js b/packages/block-editor/src/components/block-list/use-block-props/use-focus-first-element.js index eee3b88be62d00..b74bdf34039edf 100644 --- a/packages/block-editor/src/components/block-list/use-block-props/use-focus-first-element.js +++ b/packages/block-editor/src/components/block-list/use-block-props/use-focus-first-element.js @@ -45,7 +45,7 @@ function useInitialPosition( clientId ) { } // If there's no initial position, return 0 to focus the start. - return getSelectedBlocksInitialCaretPosition() || 0; + return getSelectedBlocksInitialCaretPosition(); }, [ clientId ] ); @@ -53,7 +53,7 @@ function useInitialPosition( clientId ) { /** * Transitions focus to the block or inner tabbable when the block becomes - * selected. + * selected and an initial position is set. * * @param {RefObject} ref React ref with the block element. * @param {string} clientId Block client ID. diff --git a/packages/block-editor/src/components/block-navigation/appender.js b/packages/block-editor/src/components/block-navigation/appender.js index bcb414ec46feb9..391b72daccd23b 100644 --- a/packages/block-editor/src/components/block-navigation/appender.js +++ b/packages/block-editor/src/components/block-navigation/appender.js @@ -67,7 +67,6 @@ export default function BlockNavigationAppender( { diff --git a/packages/block-editor/src/components/button-block-appender/index.js b/packages/block-editor/src/components/button-block-appender/index.js index a37acc0429df7f..eecd1bf51af174 100644 --- a/packages/block-editor/src/components/button-block-appender/index.js +++ b/packages/block-editor/src/components/button-block-appender/index.js @@ -17,20 +17,13 @@ import { Icon, plus } from '@wordpress/icons'; import Inserter from '../inserter'; function ButtonBlockAppender( - { - rootClientId, - className, - __experimentalSelectBlockOnInsert: selectBlockOnInsert, - onFocus, - tabIndex, - }, + { rootClientId, className, onFocus, tabIndex }, ref ) { return ( ); } @@ -162,7 +160,6 @@ class Inserter extends Component { clientId={ clientId } isAppender={ isAppender } showInserterHelpPanel={ showInserterHelpPanel } - __experimentalSelectBlockOnInsert={ selectBlockOnInsert } /> ); } @@ -239,12 +236,9 @@ export default compose( [ rootClientId, clientId, isAppender, - onSelectOrClose, - } = ownProps; - const { hasSingleBlockType, allowedBlockType, - __experimentalSelectBlockOnInsert: selectBlockOnInsert, + onSelectOrClose, } = ownProps; if ( ! hasSingleBlockType ) { @@ -281,21 +275,19 @@ export default compose( [ blockToInsert, getInsertionIndex(), rootClientId, - selectBlockOnInsert + false ); if ( onSelectOrClose ) { onSelectOrClose(); } - if ( ! selectBlockOnInsert ) { - const message = sprintf( - // translators: %s: the name of the block that has been added - __( '%s block added' ), - allowedBlockType.title - ); - speak( message ); - } + const message = sprintf( + // translators: %s: the name of the block that has been added + __( '%s block added' ), + allowedBlockType.title + ); + speak( message ); }, }; } ), diff --git a/packages/block-editor/src/components/inserter/library.js b/packages/block-editor/src/components/inserter/library.js index ab9dbbb900effb..51e462be471e83 100644 --- a/packages/block-editor/src/components/inserter/library.js +++ b/packages/block-editor/src/components/inserter/library.js @@ -20,7 +20,6 @@ function InserterLibrary( { isAppender, showInserterHelpPanel, showMostUsedBlocks = false, - __experimentalSelectBlockOnInsert, __experimentalInsertionIndex, onSelect = noop, } ) { @@ -43,9 +42,6 @@ function InserterLibrary( { isAppender={ isAppender } showInserterHelpPanel={ showInserterHelpPanel } showMostUsedBlocks={ showMostUsedBlocks } - __experimentalSelectBlockOnInsert={ - __experimentalSelectBlockOnInsert - } __experimentalInsertionIndex={ __experimentalInsertionIndex } /> ); diff --git a/packages/block-editor/src/components/inserter/menu.js b/packages/block-editor/src/components/inserter/menu.js index 9cd0346d30ba9c..0d44e991faae44 100644 --- a/packages/block-editor/src/components/inserter/menu.js +++ b/packages/block-editor/src/components/inserter/menu.js @@ -24,7 +24,6 @@ function InserterMenu( { rootClientId, clientId, isAppender, - __experimentalSelectBlockOnInsert, __experimentalInsertionIndex, onSelect, showInserterHelpPanel, @@ -44,7 +43,6 @@ function InserterMenu( { rootClientId, clientId, isAppender, - selectBlockOnInsert: __experimentalSelectBlockOnInsert, insertionIndex: __experimentalInsertionIndex, } ); const { showPatterns, hasReusableBlocks } = useSelect( @@ -189,9 +187,6 @@ function InserterMenu( { rootClientId={ rootClientId } clientId={ clientId } isAppender={ isAppender } - selectBlockOnInsert={ - __experimentalSelectBlockOnInsert - } showBlockDirectory /> ) } diff --git a/packages/block-editor/src/components/inserter/quick-inserter.js b/packages/block-editor/src/components/inserter/quick-inserter.js index ad3a804fc54dc8..3733cc12eecac6 100644 --- a/packages/block-editor/src/components/inserter/quick-inserter.js +++ b/packages/block-editor/src/components/inserter/quick-inserter.js @@ -30,7 +30,6 @@ export default function QuickInserter( { rootClientId, clientId, isAppender, - selectBlockOnInsert, } ) { const [ filterValue, setFilterValue ] = useState( '' ); const [ destinationRootClientId, onInsertBlocks ] = useInsertionPoint( { @@ -38,7 +37,6 @@ export default function QuickInserter( { rootClientId, clientId, isAppender, - selectBlockOnInsert, } ); const [ blockTypes ] = useBlockTypesState( destinationRootClientId, @@ -105,7 +103,6 @@ export default function QuickInserter( { rootClientId={ rootClientId } clientId={ clientId } isAppender={ isAppender } - selectBlockOnInsert={ selectBlockOnInsert } maxBlockPatterns={ showPatterns ? SHOWN_BLOCK_PATTERNS : 0 } maxBlockTypes={ SHOWN_BLOCK_TYPES } isDraggable={ false } diff --git a/packages/block-editor/src/components/inserter/search-results.js b/packages/block-editor/src/components/inserter/search-results.js index 00d34c814170be..4342b5881118de 100644 --- a/packages/block-editor/src/components/inserter/search-results.js +++ b/packages/block-editor/src/components/inserter/search-results.js @@ -32,7 +32,6 @@ function InserterSearchResults( { rootClientId, clientId, isAppender, - selectBlockOnInsert, maxBlockPatterns, maxBlockTypes, showBlockDirectory = false, @@ -45,7 +44,6 @@ function InserterSearchResults( { rootClientId, clientId, isAppender, - selectBlockOnInsert, } ); const [ blockTypes, diff --git a/packages/block-editor/src/components/multi-select-scroll-into-view/index.js b/packages/block-editor/src/components/selection-scroll-into-view/index.js similarity index 69% rename from packages/block-editor/src/components/multi-select-scroll-into-view/index.js rename to packages/block-editor/src/components/selection-scroll-into-view/index.js index e60d7798f0e787..5e9ff07f1ca6d8 100644 --- a/packages/block-editor/src/components/multi-select-scroll-into-view/index.js +++ b/packages/block-editor/src/components/selection-scroll-into-view/index.js @@ -16,26 +16,11 @@ import { getScrollContainer } from '@wordpress/dom'; import { getBlockDOMNode } from '../../utils/dom'; import { store as blockEditorStore } from '../../store'; -export function useScrollMultiSelectionIntoView( ref ) { - const selectionEnd = useSelect( ( select ) => { - const { - getBlockSelectionEnd, - hasMultiSelection, - isMultiSelecting, - } = select( blockEditorStore ); - - const blockSelectionEnd = getBlockSelectionEnd(); - - if ( - ! blockSelectionEnd || - isMultiSelecting() || - ! hasMultiSelection() - ) { - return; - } - - return blockSelectionEnd; - }, [] ); +export function useScrollSelectionIntoView( ref ) { + const selectionEnd = useSelect( + ( select ) => select( blockEditorStore ).getBlockSelectionEnd(), + [] + ); useEffect( () => { if ( ! selectionEnd ) { @@ -67,8 +52,8 @@ export function useScrollMultiSelectionIntoView( ref ) { * Scrolls the multi block selection end into view if not in view already. This * is important to do after selection by keyboard. */ -export default function MultiSelectScrollIntoView() { +export function MultiSelectScrollIntoView() { const ref = useRef(); - useScrollMultiSelectionIntoView( ref ); + useScrollSelectionIntoView( ref ); return
; } diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 5950a7bfc7ef8a..7500ac6aae3bb8 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1192,7 +1192,7 @@ function selectionHelper( state = {}, action ) { case 'REPLACE_INNER_BLOCKS': // REPLACE_INNER_BLOCKS and INSERT_BLOCKS should follow the same logic. case 'INSERT_BLOCKS': { // REPLACE_INNER_BLOCKS can be called with an empty array. - if ( ! action.updateSelection || ! action.blocks.length ) { + if ( ! action.blocks.length ) { return state; } @@ -1225,11 +1225,7 @@ function selectionHelper( state = {}, action ) { return state; } - const newState = { clientId: blockToSelect.clientId }; - if ( typeof action.initialPosition === 'number' ) { - newState.initialPosition = action.initialPosition; - } - return newState; + return { clientId: blockToSelect.clientId }; } } @@ -1372,6 +1368,11 @@ export function initialPosition( state, action ) { return state; } else if ( action.type === 'START_TYPING' ) { return state; + } else if ( + action.type === 'INSERT_BLOCKS' || + action.type === 'REPLACE_INNER_BLOCKS' + ) { + return action.updateSelection ? 0 : undefined; } // Reset the state by default (for any action not handled). diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 6062a0d1fd8c60..c1e14e2e35495f 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -692,6 +692,7 @@ export function getNextBlockClientId( state, startClientId ) { /** * Returns the initial caret position for the selected block. * This position is to used to position the caret properly when the selected block changes. + * If the current block is not a RichText, having initial position set to 0 means "focus block" * * @param {Object} state Global application state. * diff --git a/packages/edit-post/src/components/visual-editor/index.js b/packages/edit-post/src/components/visual-editor/index.js index 085e2b4233fb02..e81ba4e9179670 100644 --- a/packages/edit-post/src/components/visual-editor/index.js +++ b/packages/edit-post/src/components/visual-editor/index.js @@ -12,7 +12,6 @@ import { __unstableUseTypewriter as useTypewriter, __unstableUseClipboardHandler as useClipboardHandler, __unstableUseTypingObserver as useTypingObserver, - __unstableUseScrollMultiSelectionIntoView as useScrollMultiSelectionIntoView, __experimentalBlockSettingsMenuFirstItem, __experimentalUseResizeCanvas as useResizeCanvas, __unstableUseCanvasClickRedirect as useCanvasClickRedirect, @@ -53,7 +52,6 @@ export default function VisualEditor( { styles } ) { }; const resizedCanvasStyles = useResizeCanvas( deviceType ); - useScrollMultiSelectionIntoView( ref ); useBlockSelectionClearer( ref ); useTypewriter( ref ); useClipboardHandler( ref ); From 6f030433f843c20708d3a0938b850d9ccc7641fc Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Fri, 15 Jan 2021 09:56:18 +0100 Subject: [PATCH 02/25] Fix block patterns insertion --- .../components/inserter/hooks/use-insertion-point.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js index 8d1f10b1995ea8..afa0c75407490b 100644 --- a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js +++ b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js @@ -40,13 +40,13 @@ function useInsertionPoint( { onSelect, } ) { const { - selectedBlock, destinationRootClientId, destinationIndex, + getSelectedBlock, } = useSelect( ( select ) => { const { - getSelectedBlock, + getSelectedBlock: _getSelectedBlock, getBlockIndex, getBlockOrder, getBlockInsertionPoint, @@ -84,7 +84,7 @@ function useInsertionPoint( { } return { - selectedBlock: getSelectedBlock(), + getSelectedBlock: _getSelectedBlock, destinationRootClientId: _destinationRootClientId, destinationIndex: _destinationIndex, }; @@ -101,6 +101,8 @@ function useInsertionPoint( { const onInsertBlocks = useCallback( ( blocks, meta ) => { + const selectedBlock = getSelectedBlock(); + if ( ! isAppender && selectedBlock && @@ -137,7 +139,7 @@ function useInsertionPoint( { }, [ isAppender, - selectedBlock, + getSelectedBlock, replaceBlocks, insertBlocks, destinationRootClientId, From 76eb7e4d85be11fbdba2b2d33c5f2332c55a16b9 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Wed, 20 Jan 2021 08:51:17 +0100 Subject: [PATCH 03/25] Fix the behavior of the quick inserter --- .../src/components/inserter/hooks/use-insertion-point.js | 5 +++-- packages/block-editor/src/components/inserter/library.js | 1 + packages/block-editor/src/components/inserter/menu.js | 3 +++ .../block-editor/src/components/inserter/search-results.js | 2 ++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js index afa0c75407490b..ac16b0a208142a 100644 --- a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js +++ b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js @@ -38,6 +38,7 @@ function useInsertionPoint( { clientId, isAppender, onSelect, + shouldFocusBlock = true, } ) { const { destinationRootClientId, @@ -112,7 +113,7 @@ function useInsertionPoint( { selectedBlock.clientId, blocks, null, - undefined, + shouldFocusBlock ? 0 : undefined, meta ); } else { @@ -120,7 +121,7 @@ function useInsertionPoint( { blocks, destinationIndex, destinationRootClientId, - false, + shouldFocusBlock, meta ); } diff --git a/packages/block-editor/src/components/inserter/library.js b/packages/block-editor/src/components/inserter/library.js index 51e462be471e83..fc69a8f6d82e74 100644 --- a/packages/block-editor/src/components/inserter/library.js +++ b/packages/block-editor/src/components/inserter/library.js @@ -43,6 +43,7 @@ function InserterLibrary( { showInserterHelpPanel={ showInserterHelpPanel } showMostUsedBlocks={ showMostUsedBlocks } __experimentalInsertionIndex={ __experimentalInsertionIndex } + shouldFocusBlock={ false } /> ); } diff --git a/packages/block-editor/src/components/inserter/menu.js b/packages/block-editor/src/components/inserter/menu.js index 0d44e991faae44..60b1a5a033f434 100644 --- a/packages/block-editor/src/components/inserter/menu.js +++ b/packages/block-editor/src/components/inserter/menu.js @@ -28,6 +28,7 @@ function InserterMenu( { onSelect, showInserterHelpPanel, showMostUsedBlocks, + shouldFocusBlock = true, } ) { const [ filterValue, setFilterValue ] = useState( '' ); const [ hoveredItem, setHoveredItem ] = useState( null ); @@ -44,6 +45,7 @@ function InserterMenu( { clientId, isAppender, insertionIndex: __experimentalInsertionIndex, + shouldFocusBlock, } ); const { showPatterns, hasReusableBlocks } = useSelect( ( select ) => { @@ -188,6 +190,7 @@ function InserterMenu( { clientId={ clientId } isAppender={ isAppender } showBlockDirectory + shouldFocusBlock={ shouldFocusBlock } /> ) } { ! filterValue && ( showPatterns || hasReusableBlocks ) && ( diff --git a/packages/block-editor/src/components/inserter/search-results.js b/packages/block-editor/src/components/inserter/search-results.js index 4342b5881118de..0dae29a2305561 100644 --- a/packages/block-editor/src/components/inserter/search-results.js +++ b/packages/block-editor/src/components/inserter/search-results.js @@ -36,6 +36,7 @@ function InserterSearchResults( { maxBlockTypes, showBlockDirectory = false, isDraggable = true, + shouldFocusBlock = true, } ) { const debouncedSpeak = useDebounce( speak, 500 ); @@ -44,6 +45,7 @@ function InserterSearchResults( { rootClientId, clientId, isAppender, + shouldFocusBlock, } ); const [ blockTypes, From af7c4e80f328775696e8223a6e56c07210fbd83e Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 21 Jan 2021 15:12:25 +0100 Subject: [PATCH 04/25] Fix spoken inserter message --- .../inserter/hooks/use-insertion-point.js | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js index ac16b0a208142a..04e86b93a67381 100644 --- a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js +++ b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js @@ -1,9 +1,14 @@ +/** + * External dependencies + */ +import { castArray } from 'lodash'; + /** * WordPress dependencies */ import { useDispatch, useSelect } from '@wordpress/data'; import { isUnmodifiedDefaultBlock } from '@wordpress/blocks'; -import { _n } from '@wordpress/i18n'; +import { _n, sprintf } from '@wordpress/i18n'; import { speak } from '@wordpress/a11y'; import { useCallback } from '@wordpress/element'; @@ -125,12 +130,14 @@ function useInsertionPoint( { meta ); } - - // translators: %d: the name of the block that has been added - const message = _n( - '%d block added.', - '%d blocks added.', - blocks.length + const message = sprintf( + // translators: %d: the name of the block that has been added + _n( + '%d block added.', + '%d blocks added.', + castArray( blocks ).length + ), + castArray( blocks ).length ); speak( message ); From 25285b5dbb3c9aad64418e5dd2350c6fc604bc1b Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 25 Jan 2021 10:23:18 +0100 Subject: [PATCH 05/25] Stabilize some e2e tests --- packages/e2e-test-utils/src/inserter.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/e2e-test-utils/src/inserter.js b/packages/e2e-test-utils/src/inserter.js index 0b58f730f9bfbe..cc6b4bd192e8d2 100644 --- a/packages/e2e-test-utils/src/inserter.js +++ b/packages/e2e-test-utils/src/inserter.js @@ -54,6 +54,16 @@ export async function toggleGlobalBlockInserter() { ); } +/** + * Moves focus to the selected block. + */ +async function focusSelectedBlock() { + const selectedBlock = await page.waitForSelector( + '.block-editor-block-list__block.is-selected' + ); + await selectedBlock.focus(); +} + /** * Retrieves the document container by css class and checks to make sure the document's active element is within it */ @@ -135,6 +145,7 @@ export async function insertBlock( searchTerm ) { `//button//span[contains(text(), '${ searchTerm }')]` ); await insertButton.click(); + await focusSelectedBlock(); // We should wait until the inserter closes and the focus moves to the content. await waitForInserterCloseAndContentFocus(); } @@ -151,6 +162,7 @@ export async function insertPattern( searchTerm ) { `//div[@role = 'button']//div[contains(text(), '${ searchTerm }')]` ); await insertButton.click(); + await focusSelectedBlock(); // We should wait until the inserter closes and the focus moves to the content. await waitForInserterCloseAndContentFocus(); } @@ -168,6 +180,7 @@ export async function insertReusableBlock( searchTerm ) { `//button//span[contains(text(), '${ searchTerm }')]` ); await insertButton.click(); + await focusSelectedBlock(); // We should wait until the inserter closes and the focus moves to the content. await waitForInserterCloseAndContentFocus(); // We should wait until the block is loaded @@ -191,6 +204,7 @@ export async function insertBlockDirectoryBlock( searchTerm ) { '.block-directory-downloadable-blocks-list li:first-child button' ); await insertButton.click(); + await focusSelectedBlock(); // We should wait until the inserter closes and the focus moves to the content. await waitForInserterCloseAndContentFocus(); } From b736b7c2e2c2be6ba7560fb8e4e53fa295e484ab Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 25 Jan 2021 10:30:34 +0100 Subject: [PATCH 06/25] Fix unit tests --- .../block-editor/src/store/test/reducer.js | 33 ------------------- 1 file changed, 33 deletions(-) diff --git a/packages/block-editor/src/store/test/reducer.js b/packages/block-editor/src/store/test/reducer.js index b02b9260c03114..95e574fb69f950 100644 --- a/packages/block-editor/src/store/test/reducer.js +++ b/packages/block-editor/src/store/test/reducer.js @@ -2301,22 +2301,6 @@ describe( 'state', () => { expect( state.selectionEnd ).toEqual( expected ); } ); - it( 'should not select inserted block if updateSelection flag is false', () => { - const original = deepFreeze( { - selectionStart: { clientId: 'a' }, - selectionEnd: { clientId: 'a' }, - } ); - const action = { - type: 'INSERT_BLOCKS', - blocks: [ { clientId: 'ribs' } ], - updateSelection: false, - }; - const state = selection( original, action ); - - expect( state.selectionStart ).toBe( original.selectionStart ); - expect( state.selectionEnd ).toBe( original.selectionEnd ); - } ); - it( 'should not update the state if the block moved is already selected', () => { const original = deepFreeze( { selectionStart: { clientId: 'ribs' }, @@ -2475,23 +2459,6 @@ describe( 'state', () => { expect( state.selectionEnd ).toEqual( expected.selectionEnd ); } ); - it( 'should not update the selection on inner blocks replace if updateSelection is false', () => { - const original = deepFreeze( { - selectionStart: { clientId: 'chicken' }, - selectionEnd: { clientId: 'chicken' }, - } ); - const action = { - type: 'REPLACE_INNER_BLOCKS', - blocks: [ { clientId: 'another-block' } ], - rootClientId: 'parent', - updateSelection: false, - }; - const state = selection( original, action ); - - expect( state.selectionStart ).toEqual( original.selectionStart ); - expect( state.selectionEnd ).toEqual( original.selectionEnd ); - } ); - it( 'should keep the same selection on RESET_BLOCKS if the selected blocks continue to exist', () => { const original = deepFreeze( { selectionStart: { clientId: 'chicken' }, From 442d9e820b5a7015940c52ac430c598c0ba76155 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 25 Jan 2021 11:35:11 +0100 Subject: [PATCH 07/25] Fix more e2e tests --- .../src/components/block-list/block.js | 4 ++-- .../src/components/rich-text/index.js | 2 +- packages/block-editor/src/store/reducer.js | 11 ++++++++--- packages/e2e-test-utils/src/inserter.js | 15 +++++++++++---- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index 6338c767bf9274..f62a544031a259 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -398,7 +398,7 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => { } } }, - onReplace( blocks, indexToSelect, initialPosition ) { + onReplace( blocks, indexToSelect, initialPosition = 0 ) { if ( blocks.length && ! isUnmodifiedDefaultBlock( blocks[ blocks.length - 1 ] ) @@ -409,7 +409,7 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => { [ ownProps.clientId ], blocks, indexToSelect, - initialPosition || 0 + initialPosition ); }, toggleSelection( selectionEnabled ) { diff --git a/packages/block-editor/src/components/rich-text/index.js b/packages/block-editor/src/components/rich-text/index.js index ad34bf4c0c82be..a6c0066647ecd1 100644 --- a/packages/block-editor/src/components/rich-text/index.js +++ b/packages/block-editor/src/components/rich-text/index.js @@ -345,7 +345,7 @@ function RichTextWrapper( // If there are pasted blocks, move the caret to the end of the selected block // Otherwise, retain the default value. - const initialPosition = hasPastedBlocks ? -1 : null; + const initialPosition = hasPastedBlocks ? -1 : 0; onReplace( blocks, indexToSelect, initialPosition ); }, diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 7500ac6aae3bb8..eb203d58611d11 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1357,6 +1357,13 @@ export function isSelectionEnabled( state = true, action ) { * @return {?number} Initial position: -1 or undefined. */ export function initialPosition( state, action ) { + const keepStateAction = [ + 'REMOVE_BLOCKS', + 'START_TYPING', + 'MARK_AUTOMATIC_CHANGE', + 'MARK_AUTOMATIC_CHANGE_FINAL', + ]; + if ( action.type === 'REPLACE_BLOCKS' && typeof action.initialPosition === 'number' @@ -1364,9 +1371,7 @@ export function initialPosition( state, action ) { return action.initialPosition; } else if ( action.type === 'SELECT_BLOCK' ) { return action.initialPosition; - } else if ( action.type === 'REMOVE_BLOCKS' ) { - return state; - } else if ( action.type === 'START_TYPING' ) { + } else if ( keepStateAction.includes( action.type ) ) { return state; } else if ( action.type === 'INSERT_BLOCKS' || diff --git a/packages/e2e-test-utils/src/inserter.js b/packages/e2e-test-utils/src/inserter.js index cc6b4bd192e8d2..12aa2ae19487be 100644 --- a/packages/e2e-test-utils/src/inserter.js +++ b/packages/e2e-test-utils/src/inserter.js @@ -58,10 +58,17 @@ export async function toggleGlobalBlockInserter() { * Moves focus to the selected block. */ async function focusSelectedBlock() { - const selectedBlock = await page.waitForSelector( - '.block-editor-block-list__block.is-selected' - ); - await selectedBlock.focus(); + // Ideally there shouuld be a UI way to do this. (Focus the selected block) + await page.evaluate( () => { + wp.data + .dispatch( 'core/block-editor' ) + .selectBlock( + wp.data + .select( 'core/block-editor' ) + .getSelectedBlockClientId(), + 0 + ); + } ); } /** From 032f60ba138f628fb09e14882cb66d7faf7c07b0 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 8 Feb 2021 14:51:21 +0100 Subject: [PATCH 08/25] Implement ctrl+click in the inserter --- .../src/components/inserter-list-item/index.js | 2 +- .../src/components/inserter/hooks/use-block-types-state.js | 4 ++-- .../src/components/inserter/hooks/use-insertion-point.js | 7 ++++--- packages/block-editor/src/components/inserter/menu.js | 4 ++-- packages/block-editor/src/store/reducer.js | 2 ++ 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/block-editor/src/components/inserter-list-item/index.js b/packages/block-editor/src/components/inserter-list-item/index.js index 47e48ce8ddf29c..be9896e25ab1d9 100644 --- a/packages/block-editor/src/components/inserter-list-item/index.js +++ b/packages/block-editor/src/components/inserter-list-item/index.js @@ -83,7 +83,7 @@ function InserterListItem( { disabled={ item.isDisabled } onClick={ ( event ) => { event.preventDefault(); - onSelect( item ); + onSelect( item, event.metaKey ); onHover( null ); } } onFocus={ () => { diff --git a/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js b/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js index d54a4f019e1512..1184fe72f75a58 100644 --- a/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js +++ b/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js @@ -37,14 +37,14 @@ const useBlockTypesState = ( rootClientId, onInsert ) => { ); const onSelectItem = useCallback( - ( { name, initialAttributes, innerBlocks } ) => { + ( { name, initialAttributes, innerBlocks }, shouldFocusBlock ) => { const insertedBlock = createBlock( name, initialAttributes, createBlocksFromInnerBlocksTemplate( innerBlocks ) ); - onInsert( insertedBlock ); + onInsert( insertedBlock, undefined, shouldFocusBlock ); }, [ onInsert ] ); diff --git a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js index 04e86b93a67381..bc8ab413355a49 100644 --- a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js +++ b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js @@ -106,7 +106,7 @@ function useInsertionPoint( { } = useDispatch( blockEditorStore ); const onInsertBlocks = useCallback( - ( blocks, meta ) => { + ( blocks, meta, shouldForceFocusBlock = false ) => { const selectedBlock = getSelectedBlock(); if ( @@ -118,7 +118,7 @@ function useInsertionPoint( { selectedBlock.clientId, blocks, null, - shouldFocusBlock ? 0 : undefined, + shouldFocusBlock || shouldForceFocusBlock ? 0 : undefined, meta ); } else { @@ -126,7 +126,7 @@ function useInsertionPoint( { blocks, destinationIndex, destinationRootClientId, - shouldFocusBlock, + shouldFocusBlock || shouldForceFocusBlock, meta ); } @@ -153,6 +153,7 @@ function useInsertionPoint( { destinationRootClientId, destinationIndex, onSelect, + shouldFocusBlock, ] ); diff --git a/packages/block-editor/src/components/inserter/menu.js b/packages/block-editor/src/components/inserter/menu.js index 60b1a5a033f434..52d3834cb8384d 100644 --- a/packages/block-editor/src/components/inserter/menu.js +++ b/packages/block-editor/src/components/inserter/menu.js @@ -67,8 +67,8 @@ function InserterMenu( { ); const onInsert = useCallback( - ( blocks ) => { - onInsertBlocks( blocks ); + ( blocks, meta, shouldForceFocusBlock ) => { + onInsertBlocks( blocks, meta, shouldForceFocusBlock ); onSelect(); }, [ onInsertBlocks, onSelect ] diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index eb203d58611d11..568f683f14bf93 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1362,6 +1362,8 @@ export function initialPosition( state, action ) { 'START_TYPING', 'MARK_AUTOMATIC_CHANGE', 'MARK_AUTOMATIC_CHANGE_FINAL', + 'SHOW_INSERTION_POINT', + 'HIDE_INSERTION_POINT', ]; if ( From 162247d9dce89b7ced20cff837790e59b54fe065 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 9 Feb 2021 09:19:10 +0100 Subject: [PATCH 09/25] Add onKeyDown handler --- .../src/components/inserter-list-item/index.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/block-editor/src/components/inserter-list-item/index.js b/packages/block-editor/src/components/inserter-list-item/index.js index be9896e25ab1d9..c101712fbe7a9c 100644 --- a/packages/block-editor/src/components/inserter-list-item/index.js +++ b/packages/block-editor/src/components/inserter-list-item/index.js @@ -15,6 +15,7 @@ import { createBlock, createBlocksFromInnerBlocksTemplate, } from '@wordpress/blocks'; +import { ENTER } from '@wordpress/keycodes'; /** * Internal dependencies @@ -86,6 +87,14 @@ function InserterListItem( { onSelect( item, event.metaKey ); onHover( null ); } } + onKeyDown={ ( event ) => { + const { keyCode } = event; + if ( keyCode === ENTER ) { + event.preventDefault(); + onSelect( item, event.metaKey ); + onHover( null ); + } + } } onFocus={ () => { if ( isDragging.current ) { return; From 1afbc813f67f5d0d7eb07ba43da6911cb5f1f8cf Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 9 Feb 2021 10:03:06 +0100 Subject: [PATCH 10/25] Replace the selectionStart and selectionEnd edits with a more generic selection edit --- .../developers/data/data-core-block-editor.md | 1 + .../developers/data/data-core-editor.md | 16 +++++++ .../provider/test/use-block-sync.js | 47 ++++++++++++++++--- .../src/components/provider/use-block-sync.js | 23 ++++----- packages/block-editor/src/store/actions.js | 12 +++-- packages/block-editor/src/store/reducer.js | 5 +- packages/core-data/src/entities.js | 3 +- packages/core-data/src/entity-provider.js | 8 ++-- .../editor/src/components/provider/index.js | 16 +++---- .../src/components/provider/index.native.js | 3 +- packages/editor/src/store/actions.js | 8 +--- packages/editor/src/store/selectors.js | 24 +++++++++- 12 files changed, 118 insertions(+), 48 deletions(-) diff --git a/docs/designers-developers/developers/data/data-core-block-editor.md b/docs/designers-developers/developers/data/data-core-block-editor.md index b9531ae5edda0b..a34ebc602a8fc0 100644 --- a/docs/designers-developers/developers/data/data-core-block-editor.md +++ b/docs/designers-developers/developers/data/data-core-block-editor.md @@ -1309,6 +1309,7 @@ _Parameters_ - _selectionStart_ `WPBlockSelection`: The selection start. - _selectionEnd_ `WPBlockSelection`: The selection end. +- _initialPosition_ `(||undefined)`: Initial block position. _Returns_ diff --git a/docs/designers-developers/developers/data/data-core-editor.md b/docs/designers-developers/developers/data/data-core-editor.md index 621461421e085c..86e57d27a53e36 100644 --- a/docs/designers-developers/developers/data/data-core-editor.md +++ b/docs/designers-developers/developers/data/data-core-editor.md @@ -373,8 +373,22 @@ _Returns_ - `Array`: Block list. +# **getEditorSelection** + +Returns the current selection. + +_Parameters_ + +- _state_ `Object`: + +_Returns_ + +- `WPBlockSelection`: The selection end. + # **getEditorSelectionEnd** +> **Deprecated** since Gutenberg 10.0.0. + Returns the current selection end. _Parameters_ @@ -387,6 +401,8 @@ _Returns_ # **getEditorSelectionStart** +> **Deprecated** since Gutenberg 10.0.0. + Returns the current selection start. _Parameters_ diff --git a/packages/block-editor/src/components/provider/test/use-block-sync.js b/packages/block-editor/src/components/provider/test/use-block-sync.js index 18e6d3e4b6e036..22e5f4f5f2f293 100644 --- a/packages/block-editor/src/components/provider/test/use-block-sync.js +++ b/packages/block-editor/src/components/provider/test/use-block-sync.js @@ -256,7 +256,13 @@ describe( 'useBlockSync hook', () => { expect( onInput ).toHaveBeenCalledWith( [ { clientId: 'a', innerBlocks: [], attributes: { foo: 2 } } ], - { selectionEnd: {}, selectionStart: {} } + { + selection: { + selectionEnd: {}, + selectionStart: {}, + initialPosition: undefined, + }, + } ); expect( onChange ).not.toHaveBeenCalled(); } ); @@ -292,7 +298,13 @@ describe( 'useBlockSync hook', () => { expect( onChange ).toHaveBeenCalledWith( [ { clientId: 'a', innerBlocks: [], attributes: { foo: 2 } } ], - { selectionEnd: {}, selectionStart: {} } + { + selection: { + selectionEnd: {}, + selectionStart: {}, + initialPosition: undefined, + }, + } ); expect( onInput ).not.toHaveBeenCalled(); } ); @@ -395,7 +407,13 @@ describe( 'useBlockSync hook', () => { attributes: { foo: 2 }, }, ], - { selectionEnd: {}, selectionStart: {} } + { + selection: { + selectionEnd: {}, + selectionStart: {}, + initialPosition: undefined, + }, + } ); expect( onInput ).not.toHaveBeenCalled(); } ); @@ -433,8 +451,11 @@ describe( 'useBlockSync hook', () => { ]; expect( onChange1 ).toHaveBeenCalledWith( updatedBlocks1, { - selectionEnd: {}, - selectionStart: {}, + selection: { + initialPosition: undefined, + selectionEnd: {}, + selectionStart: {}, + }, } ); const newBlocks = [ @@ -469,7 +490,13 @@ describe( 'useBlockSync hook', () => { // The second callback should be called with the new change. expect( onChange2 ).toHaveBeenCalledWith( [ { clientId: 'b', innerBlocks: [], attributes: { foo: 3 } } ], - { selectionEnd: {}, selectionStart: {} } + { + selection: { + selectionEnd: {}, + selectionStart: {}, + initialPosition: undefined, + }, + } ); } ); @@ -526,7 +553,13 @@ describe( 'useBlockSync hook', () => { // Only the new callback should be called. expect( onChange2 ).toHaveBeenCalledWith( [ { clientId: 'b', innerBlocks: [], attributes: { foo: 3 } } ], - { selectionEnd: {}, selectionStart: {} } + { + selection: { + selectionEnd: {}, + selectionStart: {}, + initialPosition: undefined, + }, + } ); } ); } ); diff --git a/packages/block-editor/src/components/provider/use-block-sync.js b/packages/block-editor/src/components/provider/use-block-sync.js index ff28953513c6e4..07a6a147b335f0 100644 --- a/packages/block-editor/src/components/provider/use-block-sync.js +++ b/packages/block-editor/src/components/provider/use-block-sync.js @@ -55,10 +55,7 @@ import { store as blockEditorStore } from '../../store'; * is used to initalize the block-editor store * and for resetting the blocks to incoming * changes like undo. - * @param {Object} props.selectionStart The selection start vlaue from the - * controlling component. - * @param {Object} props.selectionEnd The selection end vlaue from the - * controlling component. + * @param {Object} props.selection The selection state responsible to restore the selection on undo/redo. * @param {onBlockUpdate} props.onChange Function to call when a persistent * change has been made in the block-editor blocks * for the given clientId. For example, after @@ -72,8 +69,7 @@ import { store as blockEditorStore } from '../../store'; export default function useBlockSync( { clientId = null, value: controlledBlocks, - selectionStart: controlledSelectionStart, - selectionEnd: controlledSelectionEnd, + selection: controlledSelection, onChange = noop, onInput = noop, } ) { @@ -151,10 +147,11 @@ export default function useBlockSync( { pendingChanges.current.outgoing = []; setControlledBlocks(); - if ( controlledSelectionStart && controlledSelectionEnd ) { + if ( controlledSelection ) { resetSelection( - controlledSelectionStart, - controlledSelectionEnd + controlledSelection.selectionStart, + controlledSelection.selectionEnd, + controlledSelection.initialPosition ); } } @@ -164,6 +161,7 @@ export default function useBlockSync( { const { getSelectionStart, getSelectionEnd, + getSelectedBlocksInitialCaretPosition, isLastBlockChangePersistent, __unstableIsLastBlockChangeIgnored, } = registry.select( blockEditorStore ); @@ -223,8 +221,11 @@ export default function useBlockSync( { ? onChangeRef.current : onInputRef.current; updateParent( blocks, { - selectionStart: getSelectionStart(), - selectionEnd: getSelectionEnd(), + selection: { + selectionStart: getSelectionStart(), + selectionEnd: getSelectionEnd(), + initialPosition: getSelectedBlocksInitialCaretPosition(), + }, } ); } previousAreBlocksDifferent = areBlocksDifferent; diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js index 82e69c2c5d1f79..5ccb08abbe3ce2 100644 --- a/packages/block-editor/src/store/actions.js +++ b/packages/block-editor/src/store/actions.js @@ -113,16 +113,22 @@ export function* validateBlocksToTemplate( blocks ) { * Returns an action object used in signalling that selection state should be * reset to the specified selection. * - * @param {WPBlockSelection} selectionStart The selection start. - * @param {WPBlockSelection} selectionEnd The selection end. + * @param {WPBlockSelection} selectionStart The selection start. + * @param {WPBlockSelection} selectionEnd The selection end. + * @param {0|-1|undefined} initialPosition Initial block position. * * @return {Object} Action object. */ -export function resetSelection( selectionStart, selectionEnd ) { +export function resetSelection( + selectionStart, + selectionEnd, + initialPosition +) { return { type: 'RESET_SELECTION', selectionStart, selectionEnd, + initialPosition, }; } diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 568f683f14bf93..f8c7ea835191ca 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1371,7 +1371,10 @@ export function initialPosition( state, action ) { typeof action.initialPosition === 'number' ) { return action.initialPosition; - } else if ( action.type === 'SELECT_BLOCK' ) { + } else if ( + action.type === 'SELECT_BLOCK' || + action.type === 'RESET_SELECTION' + ) { return action.initialPosition; } else if ( keepStateAction.includes( action.type ) ) { return state; diff --git a/packages/core-data/src/entities.js b/packages/core-data/src/entities.js index c711f191a7c9e1..6d3b0929a46144 100644 --- a/packages/core-data/src/entities.js +++ b/packages/core-data/src/entities.js @@ -162,8 +162,7 @@ function* loadPostTypeEntities() { label: postType.labels.singular_name, transientEdits: { blocks: true, - selectionStart: true, - selectionEnd: true, + selection: true, }, mergedEdits: { meta: true }, getTitle: ( record ) => diff --git a/packages/core-data/src/entity-provider.js b/packages/core-data/src/entity-provider.js index 1c772014a4051b..5957848ee66d7b 100644 --- a/packages/core-data/src/entity-provider.js +++ b/packages/core-data/src/entity-provider.js @@ -172,8 +172,8 @@ export function useEntityBlockEditor( kind, type, { id: _id } = {} ) { const onChange = useCallback( ( newBlocks, options ) => { - const { selectionStart, selectionEnd } = options; - const edits = { blocks: newBlocks, selectionStart, selectionEnd }; + const { selection } = options; + const edits = { blocks: newBlocks, selection }; const noChange = blocks === edits.blocks; if ( noChange ) { @@ -193,8 +193,8 @@ export function useEntityBlockEditor( kind, type, { id: _id } = {} ) { const onInput = useCallback( ( newBlocks, options ) => { - const { selectionStart, selectionEnd } = options; - const edits = { blocks: newBlocks, selectionStart, selectionEnd }; + const { selection } = options; + const edits = { blocks: newBlocks, selection }; editEntityRecord( kind, type, id, edits ); }, [ kind, type, id ] diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index a04ef2f4749343..894596819e68a8 100644 --- a/packages/editor/src/components/provider/index.js +++ b/packages/editor/src/components/provider/index.js @@ -33,16 +33,13 @@ function EditorProvider( { } return { postId: post.id, postType: post.type }; }, [ post.id, post.type ] ); - const { selectionEnd, selectionStart, isReady } = useSelect( ( select ) => { - const { - getEditorSelectionStart, - getEditorSelectionEnd, - __unstableIsEditorReady, - } = select( editorStore ); + const { selection, isReady } = useSelect( ( select ) => { + const { getEditorSelection, __unstableIsEditorReady } = select( + editorStore + ); return { isReady: __unstableIsEditorReady(), - selectionStart: getEditorSelectionStart(), - selectionEnd: getEditorSelectionEnd(), + selection: getEditorSelection(), }; }, [] ); const { id, type } = __unstableTemplate ?? post; @@ -112,8 +109,7 @@ function EditorProvider( { value={ blocks } onChange={ onChange } onInput={ onInput } - selectionStart={ selectionStart } - selectionEnd={ selectionEnd } + selection={ selection } settings={ editorSettings } useSubRegistry={ false } > diff --git a/packages/editor/src/components/provider/index.native.js b/packages/editor/src/components/provider/index.native.js index 76bb230c250bc2..d788b1a2447e3f 100644 --- a/packages/editor/src/components/provider/index.native.js +++ b/packages/editor/src/components/provider/index.native.js @@ -45,8 +45,7 @@ const postTypeEntities = [ ...postTypeEntity, transientEdits: { blocks: true, - selectionStart: true, - selectionEnd: true, + selection: true, }, mergedEdits: { meta: true, diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index 072f0c19e77c8d..4bfdf46e81c8c9 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -593,12 +593,8 @@ export function unlockPostAutosaving( lockName ) { * @yield {Object} Action object */ export function* resetEditorBlocks( blocks, options = {} ) { - const { - __unstableShouldCreateUndoLevel, - selectionStart, - selectionEnd, - } = options; - const edits = { blocks, selectionStart, selectionEnd }; + const { __unstableShouldCreateUndoLevel, selection } = options; + const edits = { blocks, selection }; if ( __unstableShouldCreateUndoLevel !== false ) { const { id, type } = yield controls.select( diff --git a/packages/editor/src/store/selectors.js b/packages/editor/src/store/selectors.js index b3bf751286809f..4323085e77b2fd 100644 --- a/packages/editor/src/store/selectors.js +++ b/packages/editor/src/store/selectors.js @@ -1229,9 +1229,14 @@ export function getEditorBlocks( state ) { * * @param {Object} state * @return {WPBlockSelection} The selection start. + * + * @deprecated since Gutenberg 10.0.0. */ export function getEditorSelectionStart( state ) { - return getEditedPostAttribute( state, 'selectionStart' ); + deprecated( "select('core/editor').getEditorSelectionStart", { + alternative: "select('core/editor').getEditorSelection", + } ); + return getEditedPostAttribute( state, 'selection' )?.selectionStart; } /** @@ -1239,9 +1244,24 @@ export function getEditorSelectionStart( state ) { * * @param {Object} state * @return {WPBlockSelection} The selection end. + * + * @deprecated since Gutenberg 10.0.0. */ export function getEditorSelectionEnd( state ) { - return getEditedPostAttribute( state, 'selectionEnd' ); + deprecated( "select('core/editor').getEditorSelectionStart", { + alternative: "select('core/editor').getEditorSelection", + } ); + return getEditedPostAttribute( state, 'selection' )?.selectionEnd; +} + +/** + * Returns the current selection. + * + * @param {Object} state + * @return {WPBlockSelection} The selection end. + */ +export function getEditorSelection( state ) { + return getEditedPostAttribute( state, 'selection' ); } /** From fae3bd602306c311ac47d7da6c9a0b9558ae24df Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 9 Feb 2021 11:07:50 +0100 Subject: [PATCH 11/25] Focus block on transform --- .../block-editor/src/components/block-switcher/index.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/block-switcher/index.js b/packages/block-editor/src/components/block-switcher/index.js index ef0d601c495c86..ec064054a33dde 100644 --- a/packages/block-editor/src/components/block-switcher/index.js +++ b/packages/block-editor/src/components/block-switcher/index.js @@ -84,7 +84,12 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => { const isTemplate = blocks.length === 1 && isTemplatePart( blocks[ 0 ] ); const onTransform = ( name ) => - replaceBlocks( clientIds, switchToBlockType( blocks, name ) ); + replaceBlocks( + clientIds, + switchToBlockType( blocks, name ), + undefined, + 0 + ); const hasPossibleBlockTransformations = !! possibleBlockTransformations.length; if ( ! hasBlockStyles && ! hasPossibleBlockTransformations ) { return ( From daaa98f442265007351d0a32ad3f9caad2d2bc15 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 9 Feb 2021 11:21:46 +0100 Subject: [PATCH 12/25] Fix non MacOS ctrl+Enter --- .../components/inserter-list-item/index.js | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/inserter-list-item/index.js b/packages/block-editor/src/components/inserter-list-item/index.js index c101712fbe7a9c..e2fb004fedbe26 100644 --- a/packages/block-editor/src/components/inserter-list-item/index.js +++ b/packages/block-editor/src/components/inserter-list-item/index.js @@ -23,6 +23,22 @@ import { ENTER } from '@wordpress/keycodes'; import BlockIcon from '../block-icon'; import InserterDraggableBlocks from '../inserter-draggable-blocks'; +/** + * Return true if platform is MacOS. + * + * @param {Object} _window window object by default; used for DI testing. + * + * @return {boolean} True if MacOS; false otherwise. + */ +function isAppleOS( _window = window ) { + const { platform } = _window.navigator; + + return ( + platform.indexOf( 'Mac' ) !== -1 || + [ 'iPad', 'iPhone' ].includes( platform ) + ); +} + function InserterListItem( { className, composite, @@ -84,14 +100,20 @@ function InserterListItem( { disabled={ item.isDisabled } onClick={ ( event ) => { event.preventDefault(); - onSelect( item, event.metaKey ); + onSelect( + item, + isAppleOS() ? event.metaKey : event.ctrlKey + ); onHover( null ); } } onKeyDown={ ( event ) => { const { keyCode } = event; if ( keyCode === ENTER ) { event.preventDefault(); - onSelect( item, event.metaKey ); + onSelect( + item, + isAppleOS() ? event.metaKey : event.ctrlKey + ); onHover( null ); } } } From 7e87f129a0ad664b207c5cefbd799bf9e9c9cf58 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 9 Feb 2021 13:33:35 +0100 Subject: [PATCH 13/25] Fix inner blocks insertions --- packages/block-editor/src/store/reducer.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index f8c7ea835191ca..1c18076186f25f 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1189,9 +1189,14 @@ function selectionHelper( state = {}, action ) { } return { clientId: action.clientId }; - case 'REPLACE_INNER_BLOCKS': // REPLACE_INNER_BLOCKS and INSERT_BLOCKS should follow the same logic. + case 'REPLACE_INNER_BLOCKS': { + if ( ! action.updateSelection || ! action.blocks.length ) { + return state; + } + + return { clientId: action.blocks[ 0 ].clientId }; + } case 'INSERT_BLOCKS': { - // REPLACE_INNER_BLOCKS can be called with an empty array. if ( ! action.blocks.length ) { return state; } From e4c7b71261a2a9f7ee942303a866bcc22fbe87d8 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 9 Feb 2021 15:05:38 +0100 Subject: [PATCH 14/25] Consider initialPosition null or undefined similarly --- .../block-list/use-block-props/use-focus-first-element.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/block-list/use-block-props/use-focus-first-element.js b/packages/block-editor/src/components/block-list/use-block-props/use-focus-first-element.js index b74bdf34039edf..da048d18acd90b 100644 --- a/packages/block-editor/src/components/block-list/use-block-props/use-focus-first-element.js +++ b/packages/block-editor/src/components/block-list/use-block-props/use-focus-first-element.js @@ -62,7 +62,7 @@ export function useFocusFirstElement( ref, clientId ) { const initialPosition = useInitialPosition( clientId ); useEffect( () => { - if ( initialPosition === undefined ) { + if ( initialPosition === undefined || initialPosition === null ) { return; } From 0ba720c61d48ce3177d8ac0ae7168afed67aa779 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 9 Feb 2021 15:09:32 +0100 Subject: [PATCH 15/25] Fix allowed blocks test --- .../specs/editor/plugins/inner-blocks-allowed-blocks.test.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/e2e-tests/specs/editor/plugins/inner-blocks-allowed-blocks.test.js b/packages/e2e-tests/specs/editor/plugins/inner-blocks-allowed-blocks.test.js index 65c94104b80c71..a3432bd03a1608 100644 --- a/packages/e2e-tests/specs/editor/plugins/inner-blocks-allowed-blocks.test.js +++ b/packages/e2e-tests/specs/editor/plugins/inner-blocks-allowed-blocks.test.js @@ -75,6 +75,11 @@ describe( 'Allowed Blocks Setting on InnerBlocks', () => { await insertBlock( 'Image' ); await closeGlobalBlockInserter(); await page.waitForSelector( '.product[data-number-of-children="2"]' ); + // This focus shouldn't be neessary but there's a bug in master right now + // Where if you open the inserter, don't do anything and click the "appender" on the canvas + // the appender is not opened right away. + // It needs to be investigated on its own. + await page.focus( appenderSelector ); await page.click( appenderSelector ); expect( await getAllBlockInserterItemTitles() ).toEqual( [ 'Gallery', From 9f617c34929b382066d4de69dc70dc6d52ee1cb8 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Wed, 10 Feb 2021 08:31:48 +0100 Subject: [PATCH 16/25] Chacnge selectBlock action default initialPosition value to retain backward compatibility --- packages/block-editor/src/store/actions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js index 5ccb08abbe3ce2..e56342a8fa1712 100644 --- a/packages/block-editor/src/store/actions.js +++ b/packages/block-editor/src/store/actions.js @@ -194,7 +194,7 @@ export function updateBlock( clientId, updates ) { * * @return {Object} Action object. */ -export function selectBlock( clientId, initialPosition = null ) { +export function selectBlock( clientId, initialPosition = 0 ) { return { type: 'SELECT_BLOCK', initialPosition, From 399d9faf9763596224b44471fd181a9254879205 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Wed, 10 Feb 2021 09:18:19 +0100 Subject: [PATCH 17/25] Try a less disruptive approach on the store APIs --- .../developers/data/data-core-block-editor.md | 8 +-- .../src/components/block-list/block.js | 4 +- .../src/components/block-switcher/index.js | 7 +-- .../inserter/hooks/use-insertion-point.js | 3 +- .../src/components/use-on-block-drop/index.js | 8 ++- packages/block-editor/src/store/actions.js | 49 ++++++++++++------- packages/block-editor/src/store/reducer.js | 12 ++--- 7 files changed, 51 insertions(+), 40 deletions(-) diff --git a/docs/designers-developers/developers/data/data-core-block-editor.md b/docs/designers-developers/developers/data/data-core-block-editor.md index a34ebc602a8fc0..f7042cda8c2609 100644 --- a/docs/designers-developers/developers/data/data-core-block-editor.md +++ b/docs/designers-developers/developers/data/data-core-block-editor.md @@ -1139,6 +1139,7 @@ _Parameters_ - _index_ `?number`: Index at which block should be inserted. - _rootClientId_ `?string`: Optional root client ID of block list on which to insert. - _updateSelection_ `?boolean`: If true block selection will be updated. If false, block selection will not change. Defaults to true. +- _initialPosition_ `(||null)`: Initial focus position. Setting it to null prevent focusing the inserted block. - _meta_ `?Object`: Optional Meta values to be passed to the action object. _Returns_ @@ -1272,7 +1273,7 @@ _Parameters_ - _clientIds_ `(string|Array)`: Block client ID(s) to replace. - _blocks_ `(Object|Array)`: Replacement block(s). - _indexToSelect_ `number`: Index of replacement block to select. -- _initialPosition_ `number`: Index of caret after in the selected block after the operation. +- _initialPosition_ `(||null)`: Index of caret after in the selected block after the operation. - _meta_ `?Object`: Optional Meta values to be passed to the action object. # **replaceInnerBlocks** @@ -1285,6 +1286,7 @@ _Parameters_ - _rootClientId_ `string`: Client ID of the block whose InnerBlocks will re replaced. - _blocks_ `Array`: Block objects to insert as new InnerBlocks - _updateSelection_ `?boolean`: If true block selection will be updated. If false, block selection will not change. Defaults to false. +- _initialPosition_ `(||null)`: Initial block position. _Returns_ @@ -1309,7 +1311,7 @@ _Parameters_ - _selectionStart_ `WPBlockSelection`: The selection start. - _selectionEnd_ `WPBlockSelection`: The selection end. -- _initialPosition_ `(||undefined)`: Initial block position. +- _initialPosition_ `(||null)`: Initial block position. _Returns_ @@ -1325,7 +1327,7 @@ reflects a reverse selection. _Parameters_ - _clientId_ `string`: Block client ID. -- _initialPosition_ `?number`: Optional initial position. Pass as -1 to reflect reverse selection. +- _initialPosition_ `(||null)`: Optional initial position. Pass as -1 to reflect reverse selection. _Returns_ diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index f62a544031a259..8169972192ad1a 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -376,7 +376,7 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => { const { clientId, rootClientId } = ownProps; const { getBlockIndex } = select( blockEditorStore ); const index = getBlockIndex( clientId, rootClientId ); - insertBlocks( blocks, index + 1, rootClientId, 0 ); + insertBlocks( blocks, index + 1, rootClientId ); }, onMerge( forward ) { const { clientId } = ownProps; @@ -398,7 +398,7 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => { } } }, - onReplace( blocks, indexToSelect, initialPosition = 0 ) { + onReplace( blocks, indexToSelect, initialPosition ) { if ( blocks.length && ! isUnmodifiedDefaultBlock( blocks[ blocks.length - 1 ] ) diff --git a/packages/block-editor/src/components/block-switcher/index.js b/packages/block-editor/src/components/block-switcher/index.js index ec064054a33dde..ef0d601c495c86 100644 --- a/packages/block-editor/src/components/block-switcher/index.js +++ b/packages/block-editor/src/components/block-switcher/index.js @@ -84,12 +84,7 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => { const isTemplate = blocks.length === 1 && isTemplatePart( blocks[ 0 ] ); const onTransform = ( name ) => - replaceBlocks( - clientIds, - switchToBlockType( blocks, name ), - undefined, - 0 - ); + replaceBlocks( clientIds, switchToBlockType( blocks, name ) ); const hasPossibleBlockTransformations = !! possibleBlockTransformations.length; if ( ! hasBlockStyles && ! hasPossibleBlockTransformations ) { return ( diff --git a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js index bc8ab413355a49..9b07d71c0f3412 100644 --- a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js +++ b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js @@ -126,7 +126,8 @@ function useInsertionPoint( { blocks, destinationIndex, destinationRootClientId, - shouldFocusBlock || shouldForceFocusBlock, + true, + shouldFocusBlock || shouldForceFocusBlock ? 0 : null, meta ); } 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 932d94d81b47f2..f2ee0ee1c0182c 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 @@ -79,7 +79,13 @@ export function onBlockDrop( // If the user is inserting a block if ( dropType === 'inserter' ) { clearSelectedBlock(); - insertBlocks( blocks, targetBlockIndex, targetRootClientId, false ); + insertBlocks( + blocks, + targetBlockIndex, + targetRootClientId, + true, + null + ); } // If the user is moving a block diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js index e56342a8fa1712..ceb8a3bcd60de2 100644 --- a/packages/block-editor/src/store/actions.js +++ b/packages/block-editor/src/store/actions.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { castArray, findKey, first, last, some } from 'lodash'; +import { castArray, findKey, first, isObject, last, some } from 'lodash'; /** * WordPress dependencies @@ -20,6 +20,7 @@ import { speak } from '@wordpress/a11y'; import { __, _n, sprintf } from '@wordpress/i18n'; import { controls } from '@wordpress/data'; import { create, insert, remove, toHTMLString } from '@wordpress/rich-text'; +import deprecated from '@wordpress/deprecated'; /** * Internal dependencies @@ -115,7 +116,7 @@ export function* validateBlocksToTemplate( blocks ) { * * @param {WPBlockSelection} selectionStart The selection start. * @param {WPBlockSelection} selectionEnd The selection end. - * @param {0|-1|undefined} initialPosition Initial block position. + * @param {0|-1|null} initialPosition Initial block position. * * @return {Object} Action object. */ @@ -188,8 +189,8 @@ export function updateBlock( clientId, updates ) { * value reflecting its selection directionality. An initialPosition of -1 * reflects a reverse selection. * - * @param {string} clientId Block client ID. - * @param {?number} initialPosition Optional initial position. Pass as -1 to + * @param {string} clientId Block client ID. + * @param {0|-1|null} initialPosition Optional initial position. Pass as -1 to * reflect reverse selection. * * @return {Object} Action object. @@ -353,7 +354,7 @@ function getBlocksWithDefaultStylesApplied( blocks, blockEditorSettings ) { * @param {(string|string[])} clientIds Block client ID(s) to replace. * @param {(Object|Object[])} blocks Replacement block(s). * @param {number} indexToSelect Index of replacement block to select. - * @param {number} initialPosition Index of caret after in the selected block after the operation. + * @param {0|-1|null} initialPosition Index of caret after in the selected block after the operation. * @param {?Object} meta Optional Meta values to be passed to the action object. * * @yield {Object} Action object. @@ -362,7 +363,7 @@ export function* replaceBlocks( clientIds, blocks, indexToSelect, - initialPosition, + initialPosition = 0, meta ) { clientIds = castArray( clientIds ); @@ -546,21 +547,30 @@ export function insertBlock( * Returns an action object used in signalling that an array of blocks should * be inserted, optionally at a specific index respective a root block list. * - * @param {Object[]} blocks Block objects to insert. - * @param {?number} index Index at which block should be inserted. - * @param {?string} rootClientId Optional root client ID of block list on which to insert. - * @param {?boolean} updateSelection If true block selection will be updated. If false, block selection will not change. Defaults to true. - * @param {?Object} meta Optional Meta values to be passed to the action object. - * - * @return {Object} Action object. + * @param {Object[]} blocks Block objects to insert. + * @param {?number} index Index at which block should be inserted. + * @param {?string} rootClientId Optional root client ID of block list on which to insert. + * @param {?boolean} updateSelection If true block selection will be updated. If false, block selection will not change. Defaults to true. + * @param {0|-1|null} initialPosition Initial focus position. Setting it to null prevent focusing the inserted block. + * @param {?Object} meta Optional Meta values to be passed to the action object. + * @return {Object} Action object. */ export function* insertBlocks( blocks, index, rootClientId, updateSelection = true, + initialPosition = 0, meta ) { + if ( isObject( initialPosition ) ) { + meta = initialPosition; + initialPosition = 0; + deprecated( "meta argument in wp.data.dispatch('core/block-editor')", { + hint: 'The meta argument is now the 6th argument of the function', + } ); + } + blocks = getBlocksWithDefaultStylesApplied( castArray( blocks ), yield controls.select( blockEditorStoreName, 'getSettings' ) @@ -585,6 +595,7 @@ export function* insertBlocks( rootClientId, time: Date.now(), updateSelection, + initialPosition: updateSelection ? initialPosition : undefined, meta, }; } @@ -908,22 +919,24 @@ export function removeBlock( clientId, selectPrevious ) { * Returns an action object used in signalling that the inner blocks with the * specified client ID should be replaced. * - * @param {string} rootClientId Client ID of the block whose InnerBlocks will re replaced. - * @param {Object[]} blocks Block objects to insert as new InnerBlocks - * @param {?boolean} updateSelection If true block selection will be updated. If false, block selection will not change. Defaults to false. - * + * @param {string} rootClientId Client ID of the block whose InnerBlocks will re replaced. + * @param {Object[]} blocks Block objects to insert as new InnerBlocks + * @param {?boolean} updateSelection If true block selection will be updated. If false, block selection will not change. Defaults to false. + * @param {0|-1|null} initialPosition Initial block position. * @return {Object} Action object. */ export function replaceInnerBlocks( rootClientId, blocks, - updateSelection = false + updateSelection = false, + initialPosition = 0 ) { return { type: 'REPLACE_INNER_BLOCKS', rootClientId, blocks, updateSelection, + initialPosition: updateSelection ? initialPosition : undefined, time: Date.now(), }; } diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 1c18076186f25f..cca8669784e703 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1189,15 +1189,9 @@ function selectionHelper( state = {}, action ) { } return { clientId: action.clientId }; - case 'REPLACE_INNER_BLOCKS': { - if ( ! action.updateSelection || ! action.blocks.length ) { - return state; - } - - return { clientId: action.blocks[ 0 ].clientId }; - } + case 'REPLACE_INNER_BLOCKS': case 'INSERT_BLOCKS': { - if ( ! action.blocks.length ) { + if ( ! action.updateSelection || ! action.blocks.length ) { return state; } @@ -1387,7 +1381,7 @@ export function initialPosition( state, action ) { action.type === 'INSERT_BLOCKS' || action.type === 'REPLACE_INNER_BLOCKS' ) { - return action.updateSelection ? 0 : undefined; + return action.initialPosition; } // Reset the state by default (for any action not handled). From 8128337e652d3a2e4f901d24828be475b7aa9691 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Wed, 10 Feb 2021 09:42:28 +0100 Subject: [PATCH 18/25] Fix unit tests --- packages/block-editor/src/store/actions.js | 4 ++-- packages/block-editor/src/store/test/actions.js | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js index ceb8a3bcd60de2..1ad76cc84146da 100644 --- a/packages/block-editor/src/store/actions.js +++ b/packages/block-editor/src/store/actions.js @@ -595,7 +595,7 @@ export function* insertBlocks( rootClientId, time: Date.now(), updateSelection, - initialPosition: updateSelection ? initialPosition : undefined, + initialPosition: updateSelection ? initialPosition : null, meta, }; } @@ -936,7 +936,7 @@ export function replaceInnerBlocks( rootClientId, blocks, updateSelection, - initialPosition: updateSelection ? initialPosition : undefined, + initialPosition: updateSelection ? initialPosition : null, time: Date.now(), }; } diff --git a/packages/block-editor/src/store/test/actions.js b/packages/block-editor/src/store/test/actions.js index 2e831c1f771c5c..0206db8dbc960d 100644 --- a/packages/block-editor/src/store/test/actions.js +++ b/packages/block-editor/src/store/test/actions.js @@ -200,6 +200,7 @@ describe( 'actions', () => { clientIds: [ 'chicken' ], blocks: [ block ], time: expect.any( Number ), + initialPosition: 0, } ); expect( replaceBlockGenerator.next().value ).toEqual( @@ -318,6 +319,7 @@ describe( 'actions', () => { clientIds: [ 'chicken' ], blocks, time: expect.any( Number ), + initialPosition: 0, } ); expect( replaceBlockGenerator.next().value ).toEqual( @@ -406,6 +408,7 @@ describe( 'actions', () => { rootClientId: 'testclientid', time: expect.any( Number ), updateSelection: true, + initialPosition: 0, }, } ); } ); @@ -493,6 +496,7 @@ describe( 'actions', () => { rootClientId: 'testrootid', time: expect.any( Number ), updateSelection: false, + initialPosition: null, }, } ); } ); @@ -549,6 +553,7 @@ describe( 'actions', () => { rootClientId: 'testrootid', time: expect.any( Number ), updateSelection: false, + initialPosition: null, }, } ); } ); @@ -613,6 +618,7 @@ describe( 'actions', () => { rootClientId: 'testrootid', time: expect.any( Number ), updateSelection: false, + initialPosition: null, }, } ); } ); @@ -683,6 +689,7 @@ describe( 'actions', () => { 5, 'testrootid', false, + 0, meta ); @@ -725,6 +732,7 @@ describe( 'actions', () => { rootClientId: 'testrootid', time: expect.any( Number ), updateSelection: false, + initialPosition: null, meta: { patternName: 'core/chicken-ribs-pattern' }, }, } ); @@ -1140,6 +1148,7 @@ describe( 'actions', () => { rootClientId: 'root', time: expect.any( Number ), updateSelection: false, + initialPosition: null, } ); } ); @@ -1150,6 +1159,7 @@ describe( 'actions', () => { rootClientId: 'root', time: expect.any( Number ), updateSelection: true, + initialPosition: 0, } ); } ); } ); From 9f511cc079fad135225bf1e4a7ebd00a276ca275 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Wed, 10 Feb 2021 10:31:06 +0100 Subject: [PATCH 19/25] Fix writing flow test --- packages/e2e-tests/specs/editor/various/writing-flow.test.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/e2e-tests/specs/editor/various/writing-flow.test.js b/packages/e2e-tests/specs/editor/various/writing-flow.test.js index cb6698bb3df4c1..af2fb84006fe43 100644 --- a/packages/e2e-tests/specs/editor/various/writing-flow.test.js +++ b/packages/e2e-tests/specs/editor/various/writing-flow.test.js @@ -558,6 +558,11 @@ describe( 'Writing Flow', () => { await page.keyboard.press( 'Enter' ); await clickBlockToolbarButton( 'Align' ); await clickButton( 'Wide width' ); + + // Focus the block. + await page.keyboard.press( 'Tab' ); + + // Select the previous block. await page.keyboard.press( 'ArrowUp' ); // Confirm correct setup. From 15746958dd9a84ba6274f494c694d8feafe39918 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 11 Feb 2021 10:31:12 +0100 Subject: [PATCH 20/25] Fix replacing default blocks --- .../src/components/inserter/hooks/use-insertion-point.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js index 9b07d71c0f3412..069b33f6bc0ac9 100644 --- a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js +++ b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js @@ -118,7 +118,7 @@ function useInsertionPoint( { selectedBlock.clientId, blocks, null, - shouldFocusBlock || shouldForceFocusBlock ? 0 : undefined, + shouldFocusBlock || shouldForceFocusBlock ? 0 : null, meta ); } else { From 0d0a6f5d741cb5943b523f83f6cb0f8f4db55d76 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 11 Feb 2021 10:36:17 +0100 Subject: [PATCH 21/25] Fix button block appender --- packages/block-editor/src/components/inserter/index.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/block-editor/src/components/inserter/index.js b/packages/block-editor/src/components/inserter/index.js index 619284ae49d328..0e197be4207bdb 100644 --- a/packages/block-editor/src/components/inserter/index.js +++ b/packages/block-editor/src/components/inserter/index.js @@ -271,12 +271,7 @@ export default compose( [ const blockToInsert = createBlock( allowedBlockType.name ); - insertBlock( - blockToInsert, - getInsertionIndex(), - rootClientId, - false - ); + insertBlock( blockToInsert, getInsertionIndex(), rootClientId ); if ( onSelectOrClose ) { onSelectOrClose(); From 12cf64f4fa7dd7bca65f98f73cf799735b732d5a Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 11 Feb 2021 10:55:14 +0100 Subject: [PATCH 22/25] Fix Buttons block insertion --- .../inner-blocks/use-inner-block-template-sync.js | 13 +++++++++++-- packages/block-editor/src/store/reducer.js | 1 + packages/block-editor/src/store/selectors.js | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/inner-blocks/use-inner-block-template-sync.js b/packages/block-editor/src/components/inner-blocks/use-inner-block-template-sync.js index fea2784ebded4e..40728f2ed85356 100644 --- a/packages/block-editor/src/components/inner-blocks/use-inner-block-template-sync.js +++ b/packages/block-editor/src/components/inner-blocks/use-inner-block-template-sync.js @@ -40,8 +40,12 @@ export default function useInnerBlockTemplateSync( templateLock, templateInsertUpdatesSelection ) { + const getSelectedBlocksInitialCaretPosition = useSelect( + ( select ) => + select( blockEditorStore ).getSelectedBlocksInitialCaretPosition, + [] + ); const { replaceInnerBlocks } = useDispatch( blockEditorStore ); - const innerBlocks = useSelect( ( select ) => select( blockEditorStore ).getBlocks( clientId ), [ clientId ] @@ -69,7 +73,12 @@ export default function useInnerBlockTemplateSync( nextBlocks, innerBlocks.length === 0 && templateInsertUpdatesSelection && - nextBlocks.length !== 0 + nextBlocks.length !== 0, + // This ensures the "initialPosition" doesn't change when applying the template + // If we're supposed to focus the block, we'll focus the first inner block + // otherwise, we won't apply any auto-focus. + // This ensures for instance that the focus stays in the inserter when inserting the "buttons" block. + getSelectedBlocksInitialCaretPosition() ); } } diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index cca8669784e703..4443b3c2a89e39 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1363,6 +1363,7 @@ export function initialPosition( state, action ) { 'MARK_AUTOMATIC_CHANGE_FINAL', 'SHOW_INSERTION_POINT', 'HIDE_INSERTION_POINT', + 'UPDATE_BLOCK_LIST_SETTINGS', ]; if ( diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index c1e14e2e35495f..15c611de763e81 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -699,7 +699,7 @@ export function getNextBlockClientId( state, startClientId ) { * @return {?Object} Selected block. */ export function getSelectedBlocksInitialCaretPosition( state ) { - return state.initialPosition; + return state.initialPosition === undefined ? null : state.initialPosition; } /** From 228eef423064c4fc20dec72e528f68daf7dcc6ac Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 11 Feb 2021 11:05:17 +0100 Subject: [PATCH 23/25] Fix unit tests --- .../src/components/provider/test/use-block-sync.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/block-editor/src/components/provider/test/use-block-sync.js b/packages/block-editor/src/components/provider/test/use-block-sync.js index 22e5f4f5f2f293..c45b1e02742f90 100644 --- a/packages/block-editor/src/components/provider/test/use-block-sync.js +++ b/packages/block-editor/src/components/provider/test/use-block-sync.js @@ -260,7 +260,7 @@ describe( 'useBlockSync hook', () => { selection: { selectionEnd: {}, selectionStart: {}, - initialPosition: undefined, + initialPosition: null, }, } ); @@ -302,7 +302,7 @@ describe( 'useBlockSync hook', () => { selection: { selectionEnd: {}, selectionStart: {}, - initialPosition: undefined, + initialPosition: null, }, } ); @@ -411,7 +411,7 @@ describe( 'useBlockSync hook', () => { selection: { selectionEnd: {}, selectionStart: {}, - initialPosition: undefined, + initialPosition: null, }, } ); @@ -452,7 +452,7 @@ describe( 'useBlockSync hook', () => { expect( onChange1 ).toHaveBeenCalledWith( updatedBlocks1, { selection: { - initialPosition: undefined, + initialPosition: null, selectionEnd: {}, selectionStart: {}, }, @@ -494,7 +494,7 @@ describe( 'useBlockSync hook', () => { selection: { selectionEnd: {}, selectionStart: {}, - initialPosition: undefined, + initialPosition: null, }, } ); @@ -557,7 +557,7 @@ describe( 'useBlockSync hook', () => { selection: { selectionEnd: {}, selectionStart: {}, - initialPosition: undefined, + initialPosition: null, }, } ); From 70bb53038e5e831f915277641f62661a16a1ee6f Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 11 Feb 2021 11:21:05 +0100 Subject: [PATCH 24/25] Simplify the initialPosition reducer --- .../developers/data/data-core-block-editor.md | 2 +- packages/block-editor/src/store/reducer.js | 33 ++++++------------- packages/block-editor/src/store/selectors.js | 4 +-- 3 files changed, 13 insertions(+), 26 deletions(-) diff --git a/docs/designers-developers/developers/data/data-core-block-editor.md b/docs/designers-developers/developers/data/data-core-block-editor.md index f7042cda8c2609..05bd3906353ffc 100644 --- a/docs/designers-developers/developers/data/data-core-block-editor.md +++ b/docs/designers-developers/developers/data/data-core-block-editor.md @@ -665,7 +665,7 @@ _Parameters_ _Returns_ -- `?Object`: Selected block. +- `(||null)`: Initial position. # **getSelectionEnd** diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 4443b3c2a89e39..2b14fa612c96f6 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1353,39 +1353,26 @@ export function isSelectionEnabled( state = true, action ) { * @param {boolean} state Current state. * @param {Object} action Dispatched action. * - * @return {?number} Initial position: -1 or undefined. + * @return {number|null} Initial position: 0, -1 or null. */ -export function initialPosition( state, action ) { - const keepStateAction = [ - 'REMOVE_BLOCKS', - 'START_TYPING', - 'MARK_AUTOMATIC_CHANGE', - 'MARK_AUTOMATIC_CHANGE_FINAL', - 'SHOW_INSERTION_POINT', - 'HIDE_INSERTION_POINT', - 'UPDATE_BLOCK_LIST_SETTINGS', - ]; - +export function initialPosition( state = null, action ) { if ( action.type === 'REPLACE_BLOCKS' && - typeof action.initialPosition === 'number' + action.initialPosition !== undefined ) { return action.initialPosition; } else if ( - action.type === 'SELECT_BLOCK' || - action.type === 'RESET_SELECTION' - ) { - return action.initialPosition; - } else if ( keepStateAction.includes( action.type ) ) { - return state; - } else if ( - action.type === 'INSERT_BLOCKS' || - action.type === 'REPLACE_INNER_BLOCKS' + [ + 'SELECT_BLOCK', + 'RESET_SELECTION', + 'INSERT_BLOCKS', + 'REPLACE_INNER_BLOCKS', + ].includes( action.type ) ) { return action.initialPosition; } - // Reset the state by default (for any action not handled). + return state; } export function blocksMode( state = {}, action ) { diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 15c611de763e81..f1e4197f67a23a 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -696,10 +696,10 @@ export function getNextBlockClientId( state, startClientId ) { * * @param {Object} state Global application state. * - * @return {?Object} Selected block. + * @return {0|-1|null} Initial position. */ export function getSelectedBlocksInitialCaretPosition( state ) { - return state.initialPosition === undefined ? null : state.initialPosition; + return state.initialPosition; } /** From 8321b3a1f6e72a7b837018dc7564399310136171 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 11 Feb 2021 14:36:18 +0100 Subject: [PATCH 25/25] Fix remaining e2e tests --- packages/e2e-test-utils/src/inserter.js | 6 ++++++ .../e2e-tests/specs/widgets/adding-widgets.test.js | 10 +++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/e2e-test-utils/src/inserter.js b/packages/e2e-test-utils/src/inserter.js index 12aa2ae19487be..188ba464d1831c 100644 --- a/packages/e2e-test-utils/src/inserter.js +++ b/packages/e2e-test-utils/src/inserter.js @@ -211,6 +211,12 @@ export async function insertBlockDirectoryBlock( searchTerm ) { '.block-directory-downloadable-blocks-list li:first-child button' ); await insertButton.click(); + await page.waitForFunction( + () => + ! document.body.querySelector( + '.block-directory-downloadable-blocks-list li:first-child button.is-busy' + ) + ); await focusSelectedBlock(); // We should wait until the inserter closes and the focus moves to the content. await waitForInserterCloseAndContentFocus(); diff --git a/packages/e2e-tests/specs/widgets/adding-widgets.test.js b/packages/e2e-tests/specs/widgets/adding-widgets.test.js index 27f1c046dfc51e..6d9b6ba37c8221 100644 --- a/packages/e2e-tests/specs/widgets/adding-widgets.test.js +++ b/packages/e2e-tests/specs/widgets/adding-widgets.test.js @@ -6,6 +6,7 @@ import { deactivatePlugin, activatePlugin, activateTheme, + pressKeyWithModifier, } from '@wordpress/e2e-test-utils'; /** @@ -105,7 +106,8 @@ describe( 'Widgets screen', () => { // firstWidgetArea // ); - await addParagraphBlock.click(); + await addParagraphBlock.focus(); + await pressKeyWithModifier( 'primary', 'Enter' ); const addedParagraphBlockInFirstWidgetArea = await firstWidgetArea.$( '[data-block][data-type="core/paragraph"][aria-label^="Empty block"]' @@ -125,7 +127,8 @@ describe( 'Widgets screen', () => { await expectInsertionPointIndicatorToBeBelowLastBlock( firstWidgetArea ); - await addParagraphBlock.click(); + await addParagraphBlock.focus(); + await pressKeyWithModifier( 'primary', 'Enter' ); await page.keyboard.type( 'Second Paragraph' ); @@ -311,7 +314,8 @@ describe( 'Widgets screen', () => { ); const addParagraphBlock = await getParagraphBlockInGlobalInserter(); - await addParagraphBlock.click(); + await addParagraphBlock.focus(); + await pressKeyWithModifier( 'primary', 'Enter' ); const firstParagraphBlock = await firstWidgetArea.$( '[data-block][data-type="core/paragraph"][aria-label^="Empty block"]'