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..05bd3906353ffc 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_
@@ -664,7 +665,7 @@ _Parameters_
_Returns_
-- `?Object`: Selected block.
+- `(||null)`: Initial position.
# **getSelectionEnd**
@@ -1138,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_
@@ -1271,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**
@@ -1284,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_
@@ -1308,6 +1311,7 @@ _Parameters_
- _selectionStart_ `WPBlockSelection`: The selection start.
- _selectionEnd_ `WPBlockSelection`: The selection end.
+- _initialPosition_ `(||null)`: Initial block position.
_Returns_
@@ -1323,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/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/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..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
@@ -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.
@@ -62,7 +62,7 @@ export function useFocusFirstElement( ref, clientId ) {
const initialPosition = useInitialPosition( clientId );
useEffect( () => {
- if ( initialPosition === undefined ) {
+ if ( initialPosition === undefined || initialPosition === null ) {
return;
}
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 (
+ 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/components/inserter-list-item/index.js b/packages/block-editor/src/components/inserter-list-item/index.js
index 47e48ce8ddf29c..e2fb004fedbe26 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
@@ -22,6 +23,22 @@ import {
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,
@@ -83,9 +100,23 @@ function InserterListItem( {
disabled={ item.isDisabled }
onClick={ ( event ) => {
event.preventDefault();
- onSelect( item );
+ onSelect(
+ item,
+ isAppleOS() ? event.metaKey : event.ctrlKey
+ );
onHover( null );
} }
+ onKeyDown={ ( event ) => {
+ const { keyCode } = event;
+ if ( keyCode === ENTER ) {
+ event.preventDefault();
+ onSelect(
+ item,
+ isAppleOS() ? event.metaKey : event.ctrlKey
+ );
+ onHover( null );
+ }
+ } }
onFocus={ () => {
if ( isDragging.current ) {
return;
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 0fffe4ec0a90d8..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
@@ -28,8 +28,6 @@ import { store as blockEditorStore } from '../../../store';
* block with this ID.
* @property {boolean=} isAppender Whether the inserter is an appender
* or not.
- * @property {boolean=} selectBlockOnInsert Whether the block should be
- * selected on insert.
* @property {Function=} onSelect Called after insertion.
*/
@@ -44,17 +42,17 @@ function useInsertionPoint( {
insertionIndex,
clientId,
isAppender,
- selectBlockOnInsert,
onSelect,
+ shouldFocusBlock = true,
} ) {
const {
- selectedBlock,
destinationRootClientId,
destinationIndex,
+ getSelectedBlock,
} = useSelect(
( select ) => {
const {
- getSelectedBlock,
+ getSelectedBlock: _getSelectedBlock,
getBlockIndex,
getBlockOrder,
getBlockInsertionPoint,
@@ -92,7 +90,7 @@ function useInsertionPoint( {
}
return {
- selectedBlock: getSelectedBlock(),
+ getSelectedBlock: _getSelectedBlock,
destinationRootClientId: _destinationRootClientId,
destinationIndex: _destinationIndex,
};
@@ -108,7 +106,9 @@ function useInsertionPoint( {
} = useDispatch( blockEditorStore );
const onInsertBlocks = useCallback(
- ( blocks, meta ) => {
+ ( blocks, meta, shouldForceFocusBlock = false ) => {
+ const selectedBlock = getSelectedBlock();
+
if (
! isAppender &&
selectedBlock &&
@@ -118,7 +118,7 @@ function useInsertionPoint( {
selectedBlock.clientId,
blocks,
null,
- null,
+ shouldFocusBlock || shouldForceFocusBlock ? 0 : null,
meta
);
} else {
@@ -126,23 +126,21 @@ function useInsertionPoint( {
blocks,
destinationIndex,
destinationRootClientId,
- selectBlockOnInsert,
+ true,
+ shouldFocusBlock || shouldForceFocusBlock ? 0 : null,
meta
);
}
-
- if ( ! selectBlockOnInsert ) {
- const message = sprintf(
- // translators: %d: the name of the block that has been added
- _n(
- '%d block added.',
- '%d blocks added.',
- castArray( 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
- );
- speak( message );
- }
+ ),
+ castArray( blocks ).length
+ );
+ speak( message );
if ( onSelect ) {
onSelect();
@@ -150,13 +148,13 @@ function useInsertionPoint( {
},
[
isAppender,
- selectedBlock,
+ getSelectedBlock,
replaceBlocks,
insertBlocks,
destinationRootClientId,
destinationIndex,
- selectBlockOnInsert,
onSelect,
+ shouldFocusBlock,
]
);
diff --git a/packages/block-editor/src/components/inserter/index.js b/packages/block-editor/src/components/inserter/index.js
index 02032bc6e214e9..0e197be4207bdb 100644
--- a/packages/block-editor/src/components/inserter/index.js
+++ b/packages/block-editor/src/components/inserter/index.js
@@ -132,7 +132,6 @@ class Inserter extends Component {
clientId,
isAppender,
showInserterHelpPanel,
- __experimentalSelectBlockOnInsert: selectBlockOnInsert,
// This prop is experimental to give some time for the quick inserter to mature
// Feel free to make them stable after a few releases.
@@ -148,7 +147,6 @@ class Inserter extends Component {
rootClientId={ rootClientId }
clientId={ clientId }
isAppender={ isAppender }
- selectBlockOnInsert={ selectBlockOnInsert }
/>
);
}
@@ -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 ) {
@@ -277,25 +271,18 @@ export default compose( [
const blockToInsert = createBlock( allowedBlockType.name );
- insertBlock(
- blockToInsert,
- getInsertionIndex(),
- rootClientId,
- selectBlockOnInsert
- );
+ insertBlock( blockToInsert, getInsertionIndex(), rootClientId );
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..fc69a8f6d82e74 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,10 +42,8 @@ function InserterLibrary( {
isAppender={ isAppender }
showInserterHelpPanel={ showInserterHelpPanel }
showMostUsedBlocks={ showMostUsedBlocks }
- __experimentalSelectBlockOnInsert={
- __experimentalSelectBlockOnInsert
- }
__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 9cd0346d30ba9c..52d3834cb8384d 100644
--- a/packages/block-editor/src/components/inserter/menu.js
+++ b/packages/block-editor/src/components/inserter/menu.js
@@ -24,11 +24,11 @@ function InserterMenu( {
rootClientId,
clientId,
isAppender,
- __experimentalSelectBlockOnInsert,
__experimentalInsertionIndex,
onSelect,
showInserterHelpPanel,
showMostUsedBlocks,
+ shouldFocusBlock = true,
} ) {
const [ filterValue, setFilterValue ] = useState( '' );
const [ hoveredItem, setHoveredItem ] = useState( null );
@@ -44,8 +44,8 @@ function InserterMenu( {
rootClientId,
clientId,
isAppender,
- selectBlockOnInsert: __experimentalSelectBlockOnInsert,
insertionIndex: __experimentalInsertionIndex,
+ shouldFocusBlock,
} );
const { showPatterns, hasReusableBlocks } = useSelect(
( select ) => {
@@ -67,8 +67,8 @@ function InserterMenu( {
);
const onInsert = useCallback(
- ( blocks ) => {
- onInsertBlocks( blocks );
+ ( blocks, meta, shouldForceFocusBlock ) => {
+ onInsertBlocks( blocks, meta, shouldForceFocusBlock );
onSelect();
},
[ onInsertBlocks, onSelect ]
@@ -189,10 +189,8 @@ function InserterMenu( {
rootClientId={ rootClientId }
clientId={ clientId }
isAppender={ isAppender }
- selectBlockOnInsert={
- __experimentalSelectBlockOnInsert
- }
showBlockDirectory
+ shouldFocusBlock={ shouldFocusBlock }
/>
) }
{ ! filterValue && ( showPatterns || hasReusableBlocks ) && (
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..0dae29a2305561 100644
--- a/packages/block-editor/src/components/inserter/search-results.js
+++ b/packages/block-editor/src/components/inserter/search-results.js
@@ -32,11 +32,11 @@ function InserterSearchResults( {
rootClientId,
clientId,
isAppender,
- selectBlockOnInsert,
maxBlockPatterns,
maxBlockTypes,
showBlockDirectory = false,
isDraggable = true,
+ shouldFocusBlock = true,
} ) {
const debouncedSpeak = useDebounce( speak, 500 );
@@ -45,7 +45,7 @@ function InserterSearchResults( {
rootClientId,
clientId,
isAppender,
- selectBlockOnInsert,
+ shouldFocusBlock,
} );
const [
blockTypes,
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..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
@@ -256,7 +256,13 @@ describe( 'useBlockSync hook', () => {
expect( onInput ).toHaveBeenCalledWith(
[ { clientId: 'a', innerBlocks: [], attributes: { foo: 2 } } ],
- { selectionEnd: {}, selectionStart: {} }
+ {
+ selection: {
+ selectionEnd: {},
+ selectionStart: {},
+ initialPosition: null,
+ },
+ }
);
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: null,
+ },
+ }
);
expect( onInput ).not.toHaveBeenCalled();
} );
@@ -395,7 +407,13 @@ describe( 'useBlockSync hook', () => {
attributes: { foo: 2 },
},
],
- { selectionEnd: {}, selectionStart: {} }
+ {
+ selection: {
+ selectionEnd: {},
+ selectionStart: {},
+ initialPosition: null,
+ },
+ }
);
expect( onInput ).not.toHaveBeenCalled();
} );
@@ -433,8 +451,11 @@ describe( 'useBlockSync hook', () => {
];
expect( onChange1 ).toHaveBeenCalledWith( updatedBlocks1, {
- selectionEnd: {},
- selectionStart: {},
+ selection: {
+ initialPosition: null,
+ 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: null,
+ },
+ }
);
} );
@@ -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: null,
+ },
+ }
);
} );
} );
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/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/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/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 82e69c2c5d1f79..1ad76cc84146da 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
@@ -113,16 +114,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|null} 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,
};
}
@@ -182,13 +189,13 @@ 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.
*/
-export function selectBlock( clientId, initialPosition = null ) {
+export function selectBlock( clientId, initialPosition = 0 ) {
return {
type: 'SELECT_BLOCK',
initialPosition,
@@ -347,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.
@@ -356,7 +363,7 @@ export function* replaceBlocks(
clientIds,
blocks,
indexToSelect,
- initialPosition,
+ initialPosition = 0,
meta
) {
clientIds = castArray( clientIds );
@@ -540,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' )
@@ -579,6 +595,7 @@ export function* insertBlocks(
rootClientId,
time: Date.now(),
updateSelection,
+ initialPosition: updateSelection ? initialPosition : null,
meta,
};
}
@@ -902,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 : null,
time: Date.now(),
};
}
diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js
index 5950a7bfc7ef8a..2b14fa612c96f6 100644
--- a/packages/block-editor/src/store/reducer.js
+++ b/packages/block-editor/src/store/reducer.js
@@ -1189,9 +1189,8 @@ 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':
case 'INSERT_BLOCKS': {
- // REPLACE_INNER_BLOCKS can be called with an empty array.
if ( ! action.updateSelection || ! action.blocks.length ) {
return state;
}
@@ -1225,11 +1224,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 };
}
}
@@ -1358,23 +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 ) {
+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' ) {
+ } else if (
+ [
+ 'SELECT_BLOCK',
+ 'RESET_SELECTION',
+ 'INSERT_BLOCKS',
+ 'REPLACE_INNER_BLOCKS',
+ ].includes( action.type )
+ ) {
return action.initialPosition;
- } else if ( action.type === 'REMOVE_BLOCKS' ) {
- return state;
- } else if ( action.type === 'START_TYPING' ) {
- return state;
}
- // 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 e338ee258fd7fb..ae383dff603312 100644
--- a/packages/block-editor/src/store/selectors.js
+++ b/packages/block-editor/src/store/selectors.js
@@ -692,10 +692,11 @@ 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.
*
- * @return {?Object} Selected block.
+ * @return {0|-1|null} Initial position.
*/
export function getSelectedBlocksInitialCaretPosition( state ) {
return state.initialPosition;
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,
} );
} );
} );
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' },
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/e2e-test-utils/src/inserter.js b/packages/e2e-test-utils/src/inserter.js
index 33ffa10a7c293c..29a5d5d8444091 100644
--- a/packages/e2e-test-utils/src/inserter.js
+++ b/packages/e2e-test-utils/src/inserter.js
@@ -54,6 +54,23 @@ export async function toggleGlobalBlockInserter() {
);
}
+/**
+ * Moves focus to the selected block.
+ */
+async function focusSelectedBlock() {
+ // 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
+ );
+ } );
+}
+
/**
* Retrieves the document container by css class and checks to make sure the document's active element is within it
*/
@@ -135,6 +152,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 +169,7 @@ export async function insertPattern( searchTerm ) {
`//div[@role = 'option']//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 +187,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 +211,13 @@ 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/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',
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.
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"]'
diff --git a/packages/edit-post/src/components/visual-editor/index.js b/packages/edit-post/src/components/visual-editor/index.js
index d28ce6fcf122b5..dc402828d43d64 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,
@@ -52,7 +51,6 @@ export default function VisualEditor( { styles } ) {
};
const resizedCanvasStyles = useResizeCanvas( deviceType );
- useScrollMultiSelectionIntoView( ref );
useBlockSelectionClearer( ref );
useTypewriter( ref );
useClipboardHandler( ref );
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' );
}
/**