diff --git a/packages/block-editor/src/components/block-list/block-wrapper.js b/packages/block-editor/src/components/block-list/block-wrapper.js
new file mode 100644
index 00000000000000..bcd5d4c38929f5
--- /dev/null
+++ b/packages/block-editor/src/components/block-list/block-wrapper.js
@@ -0,0 +1,238 @@
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+import { first, last, omit } from 'lodash';
+import { animated } from 'react-spring/web.cjs';
+
+/**
+ * WordPress dependencies
+ */
+import {
+ useRef,
+ useEffect,
+ useLayoutEffect,
+ useContext,
+ forwardRef,
+} from '@wordpress/element';
+import { focus, isTextField, placeCaretAtHorizontalEdge } from '@wordpress/dom';
+import { BACKSPACE, DELETE, ENTER } from '@wordpress/keycodes';
+import { __, sprintf } from '@wordpress/i18n';
+import { useSelect, useDispatch } from '@wordpress/data';
+
+/**
+ * Internal dependencies
+ */
+import { isInsideRootBlock } from '../../utils/dom';
+import useMovingAnimation from './moving-animation';
+import { Context, BlockNodes } from './root-container';
+import { BlockContext } from './block';
+
+const BlockComponent = forwardRef(
+ ( { children, tagName = 'div', __unstableIsHtml, ...props }, wrapper ) => {
+ const onSelectionStart = useContext( Context );
+ const [ , setBlockNodes ] = useContext( BlockNodes );
+ const {
+ clientId,
+ rootClientId,
+ isSelected,
+ isFirstMultiSelected,
+ isLastMultiSelected,
+ isMultiSelecting,
+ isNavigationMode,
+ isPartOfMultiSelection,
+ enableAnimation,
+ index,
+ className,
+ isLocked,
+ name,
+ mode,
+ blockTitle,
+ } = useContext( BlockContext );
+ const { initialPosition } = useSelect(
+ ( select ) => {
+ if ( ! isSelected ) {
+ return {};
+ }
+
+ return {
+ initialPosition: select(
+ 'core/block-editor'
+ ).getSelectedBlocksInitialCaretPosition(),
+ };
+ },
+ [ isSelected ]
+ );
+ const { removeBlock, insertDefaultBlock } = useDispatch(
+ 'core/block-editor'
+ );
+ const fallbackRef = useRef();
+
+ wrapper = wrapper || fallbackRef;
+
+ // Provide the selected node, or the first and last nodes of a multi-
+ // selection, so it can be used to position the contextual block toolbar.
+ // We only provide what is necessary, and remove the nodes again when they
+ // are no longer selected.
+ useLayoutEffect( () => {
+ if ( isSelected || isFirstMultiSelected || isLastMultiSelected ) {
+ const node = wrapper.current;
+ setBlockNodes( ( nodes ) => ( {
+ ...nodes,
+ [ clientId ]: node,
+ } ) );
+ return () => {
+ setBlockNodes( ( nodes ) => omit( nodes, clientId ) );
+ };
+ }
+ }, [ isSelected, isFirstMultiSelected, isLastMultiSelected ] );
+
+ // translators: %s: Type of block (i.e. Text, Image etc)
+ const blockLabel = sprintf( __( 'Block: %s' ), blockTitle );
+
+ // Handing the focus of the block on creation and update
+
+ /**
+ * When a block becomes selected, transition focus to an inner tabbable.
+ *
+ * @param {boolean} ignoreInnerBlocks Should not focus inner blocks.
+ */
+ const focusTabbable = ( ignoreInnerBlocks ) => {
+ // Focus is captured by the wrapper node, so while focus transition
+ // should only consider tabbables within editable display, since it
+ // may be the wrapper itself or a side control which triggered the
+ // focus event, don't unnecessary transition to an inner tabbable.
+ if ( wrapper.current.contains( document.activeElement ) ) {
+ return;
+ }
+
+ // Find all tabbables within node.
+ const textInputs = focus.tabbable
+ .find( wrapper.current )
+ .filter( isTextField )
+ // Exclude inner blocks
+ .filter(
+ ( node ) =>
+ ! ignoreInnerBlocks ||
+ isInsideRootBlock( wrapper.current, node )
+ );
+
+ // If reversed (e.g. merge via backspace), use the last in the set of
+ // tabbables.
+ const isReverse = -1 === initialPosition;
+ const target =
+ ( isReverse ? last : first )( textInputs ) || wrapper.current;
+
+ placeCaretAtHorizontalEdge( target, isReverse );
+ };
+
+ // Focus the selected block's wrapper or inner input on mount and update
+ const isMounting = useRef( true );
+
+ useEffect( () => {
+ if ( ! isMultiSelecting && ! isNavigationMode && isSelected ) {
+ focusTabbable( ! isMounting.current );
+ }
+
+ isMounting.current = false;
+ }, [ isSelected, isMultiSelecting, isNavigationMode ] );
+
+ // Block Reordering animation
+ const animationStyle = useMovingAnimation(
+ wrapper,
+ isSelected || isPartOfMultiSelection,
+ isSelected || isFirstMultiSelected,
+ enableAnimation,
+ index
+ );
+
+ /**
+ * Interprets keydown event intent to remove or insert after block if key
+ * event occurs on wrapper node. This can occur when the block has no text
+ * fields of its own, particularly after initial insertion, to allow for
+ * easy deletion and continuous writing flow to add additional content.
+ *
+ * @param {KeyboardEvent} event Keydown event.
+ */
+ const onKeyDown = ( event ) => {
+ const { keyCode, target } = event;
+
+ if ( props.onKeyDown ) {
+ props.onKeyDown( event );
+ }
+
+ if (
+ keyCode !== ENTER &&
+ keyCode !== BACKSPACE &&
+ keyCode !== DELETE
+ ) {
+ return;
+ }
+
+ if ( target !== wrapper.current || isTextField( target ) ) {
+ return;
+ }
+
+ event.preventDefault();
+
+ if ( keyCode === ENTER ) {
+ insertDefaultBlock( {}, rootClientId, index + 1 );
+ } else {
+ removeBlock( clientId );
+ }
+ };
+
+ const onMouseLeave = ( { which, buttons } ) => {
+ // The primary button must be pressed to initiate selection. Fall back
+ // to `which` if the standard `buttons` property is falsy. There are
+ // cases where Firefox might always set `buttons` to `0`.
+ // See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
+ // See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which
+ if ( ( buttons || which ) === 1 ) {
+ onSelectionStart( clientId );
+ }
+ };
+
+ const htmlSuffix =
+ mode === 'html' && ! __unstableIsHtml ? '-visual' : '';
+ const blockElementId = `block-${ clientId }${ htmlSuffix }`;
+ const Animated = animated[ tagName ];
+
+ return (
+
+ { children }
+
+ );
+ }
+);
+
+const elements = [ 'p', 'div' ];
+
+const ExtendedBlockComponent = elements.reduce( ( acc, element ) => {
+ acc[ element ] = forwardRef( ( props, ref ) => {
+ return ;
+ } );
+ return acc;
+}, BlockComponent );
+
+export const Block = ExtendedBlockComponent;
diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js
index fd273612ebca8f..a18d1b4e68b025 100644
--- a/packages/block-editor/src/components/block-list/block.js
+++ b/packages/block-editor/src/components/block-list/block.js
@@ -2,30 +2,20 @@
* External dependencies
*/
import classnames from 'classnames';
-import { first, last, omit } from 'lodash';
-import { animated } from 'react-spring/web.cjs';
/**
* WordPress dependencies
*/
-import {
- useRef,
- useEffect,
- useLayoutEffect,
- useState,
- useContext,
-} from '@wordpress/element';
-import { focus, isTextField, placeCaretAtHorizontalEdge } from '@wordpress/dom';
-import { BACKSPACE, DELETE, ENTER } from '@wordpress/keycodes';
+import { useState, createContext, useMemo } from '@wordpress/element';
import {
getBlockType,
getSaveElement,
isReusableBlock,
isUnmodifiedDefaultBlock,
getUnregisteredTypeHandlerName,
+ hasBlockSupport,
} from '@wordpress/blocks';
import { withFilters } from '@wordpress/components';
-import { __, sprintf } from '@wordpress/i18n';
import { withDispatch, withSelect, useSelect } from '@wordpress/data';
import { withViewportMatch } from '@wordpress/viewport';
import { compose, pure, ifCondition } from '@wordpress/compose';
@@ -38,15 +28,16 @@ import BlockInvalidWarning from './block-invalid-warning';
import BlockCrashWarning from './block-crash-warning';
import BlockCrashBoundary from './block-crash-boundary';
import BlockHtml from './block-html';
-import { isInsideRootBlock } from '../../utils/dom';
-import useMovingAnimation from './moving-animation';
-import { Context, BlockNodes } from './root-container';
+import { Block } from './block-wrapper';
+
+export const BlockContext = createContext();
function BlockListBlock( {
mode,
isFocusMode,
isLocked,
clientId,
+ rootClientId,
isSelected,
isMultiSelected,
isPartOfMultiSelection,
@@ -59,166 +50,43 @@ function BlockListBlock( {
name,
isValid,
attributes,
- initialPosition,
wrapperProps,
setAttributes,
onReplace,
onInsertBlocksAfter,
onMerge,
- onRemove,
- onInsertDefaultBlockAfter,
toggleSelection,
- animateOnChange,
+ index,
enableAnimation,
isNavigationMode,
isMultiSelecting,
hasSelectedUI = true,
} ) {
- const onSelectionStart = useContext( Context );
- const [ , setBlockNodes ] = useContext( BlockNodes );
- // In addition to withSelect, we should favor using useSelect in this component going forward
- // to avoid leaking new props to the public API (editor.BlockListBlock filter)
+ // In addition to withSelect, we should favor using useSelect in this
+ // component going forward to avoid leaking new props to the public API
+ // (editor.BlockListBlock filter)
const { isDraggingBlocks } = useSelect( ( select ) => {
return {
isDraggingBlocks: select( 'core/block-editor' ).isDraggingBlocks(),
};
}, [] );
- // Reference of the wrapper
- const wrapper = useRef( null );
-
- // Provide the selected node, or the first and last nodes of a multi-
- // selection, so it can be used to position the contextual block toolbar.
- // We only provide what is necessary, and remove the nodes again when they
- // are no longer selected.
- useLayoutEffect( () => {
- if ( isSelected || isFirstMultiSelected || isLastMultiSelected ) {
- const node = wrapper.current;
- setBlockNodes( ( nodes ) => ( { ...nodes, [ clientId ]: node } ) );
- return () => {
- setBlockNodes( ( nodes ) => omit( nodes, clientId ) );
- };
- }
- }, [ isSelected, isFirstMultiSelected, isLastMultiSelected ] );
-
// Handling the error state
const [ hasError, setErrorState ] = useState( false );
const onBlockError = () => setErrorState( true );
const blockType = getBlockType( name );
- // translators: %s: Type of block (i.e. Text, Image etc)
- const blockLabel = sprintf( __( 'Block: %s' ), blockType.title );
-
- // Handing the focus of the block on creation and update
-
- /**
- * When a block becomes selected, transition focus to an inner tabbable.
- *
- * @param {boolean} ignoreInnerBlocks Should not focus inner blocks.
- */
- const focusTabbable = ( ignoreInnerBlocks ) => {
- // Focus is captured by the wrapper node, so while focus transition
- // should only consider tabbables within editable display, since it
- // may be the wrapper itself or a side control which triggered the
- // focus event, don't unnecessary transition to an inner tabbable.
- if ( wrapper.current.contains( document.activeElement ) ) {
- return;
- }
-
- // Find all tabbables within node.
- const textInputs = focus.tabbable
- .find( wrapper.current )
- .filter( isTextField )
- // Exclude inner blocks
- .filter(
- ( node ) =>
- ! ignoreInnerBlocks ||
- isInsideRootBlock( wrapper.current, node )
- );
-
- // If reversed (e.g. merge via backspace), use the last in the set of
- // tabbables.
- const isReverse = -1 === initialPosition;
- const target = ( isReverse ? last : first )( textInputs );
-
- if ( ! target ) {
- wrapper.current.focus();
- return;
- }
-
- placeCaretAtHorizontalEdge( target, isReverse );
- };
-
- // Focus the selected block's wrapper or inner input on mount and update
- const isMounting = useRef( true );
-
- useEffect( () => {
- if ( ! isMultiSelecting && ! isNavigationMode && isSelected ) {
- focusTabbable( ! isMounting.current );
- }
-
- isMounting.current = false;
- }, [ isSelected, isMultiSelecting, isNavigationMode ] );
-
- // Block Reordering animation
- const animationStyle = useMovingAnimation(
- wrapper,
- isSelected || isPartOfMultiSelection,
- isSelected || isFirstMultiSelected,
- enableAnimation,
- animateOnChange
+ const lightBlockWrapper = hasBlockSupport(
+ blockType,
+ 'lightBlockWrapper',
+ false
);
-
- // Other event handlers
-
- /**
- * Interprets keydown event intent to remove or insert after block if key
- * event occurs on wrapper node. This can occur when the block has no text
- * fields of its own, particularly after initial insertion, to allow for
- * easy deletion and continuous writing flow to add additional content.
- *
- * @param {KeyboardEvent} event Keydown event.
- */
- const onKeyDown = ( event ) => {
- const { keyCode, target } = event;
-
- switch ( keyCode ) {
- case ENTER:
- if ( target === wrapper.current ) {
- // Insert default block after current block if enter and event
- // not already handled by descendant.
- onInsertDefaultBlockAfter();
- event.preventDefault();
- }
- break;
- case BACKSPACE:
- case DELETE:
- if ( target === wrapper.current ) {
- // Remove block on backspace.
- onRemove( clientId );
- event.preventDefault();
- }
- break;
- }
- };
-
- const onMouseLeave = ( { which, buttons } ) => {
- // The primary button must be pressed to initiate selection. Fall back
- // to `which` if the standard `buttons` property is falsy. There are
- // cases where Firefox might always set `buttons` to `0`.
- // See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
- // See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which
- if ( ( buttons || which ) === 1 ) {
- onSelectionStart( clientId );
- }
- };
-
const isUnregisteredBlock = name === getUnregisteredTypeHandlerName();
const isDragging =
isDraggingBlocks && ( isSelected || isPartOfMultiSelection );
// Determine whether the block has props to apply to the wrapper.
- if ( blockType.getEditWrapperProps ) {
+ if ( ! lightBlockWrapper && blockType.getEditWrapperProps ) {
wrapperProps = {
...wrapperProps,
...blockType.getEditWrapperProps( attributes ),
@@ -228,7 +96,8 @@ function BlockListBlock( {
const isAligned = wrapperProps && wrapperProps[ 'data-align' ];
// The wp-block className is important for editor styles.
- // Generate the wrapper class names handling the different states of the block.
+ // Generate the wrapper class names handling the different states of the
+ // block.
const wrapperClassName = classnames(
'wp-block block-editor-block-list__block',
{
@@ -248,8 +117,6 @@ function BlockListBlock( {
className
);
- const blockElementId = `block-${ clientId }`;
-
// We wrap the BlockEdit component in a div that hides it when editing in
// HTML mode. This allows us to render all of the ancillary pieces
// (InspectorControls, etc.) which are inside `BlockEdit` but not
@@ -280,47 +147,59 @@ function BlockListBlock( {
blockEdit =
{ blockEdit }
;
}
+ const value = {
+ clientId,
+ rootClientId,
+ isSelected,
+ isFirstMultiSelected,
+ isLastMultiSelected,
+ isMultiSelecting,
+ isNavigationMode,
+ isPartOfMultiSelection,
+ enableAnimation,
+ index,
+ className: wrapperClassName,
+ isLocked,
+ name,
+ mode,
+ blockTitle: blockType.title,
+ };
+ const memoizedValue = useMemo( () => value, Object.values( value ) );
+
return (
-
+
- { isValid && blockEdit }
- { isValid && mode === 'html' && (
-
+ { isValid && lightBlockWrapper && (
+ <>
+ { blockEdit }
+ { mode === 'html' && (
+
+
+
+ ) }
+ >
+ ) }
+ { isValid && ! lightBlockWrapper && (
+
+ { blockEdit }
+ { mode === 'html' && (
+
+ ) }
+
+ ) }
+ { ! isValid && (
+
+
+ { getSaveElement( blockType, attributes ) }
+
) }
- { ! isValid && [
- ,
-
- { getSaveElement( blockType, attributes ) }
-
,
- ] }
- { !! hasError && }
-
+ { !! hasError && (
+
+
+
+ ) }
+
);
}
@@ -335,7 +214,6 @@ const applyWithSelect = withSelect(
isTyping,
getBlockMode,
isSelectionEnabled,
- getSelectedBlocksInitialCaretPosition,
getSettings,
hasSelectedInnerBlock,
getTemplateLock,
@@ -355,8 +233,9 @@ const applyWithSelect = withSelect(
);
// The fallback to `{}` is a temporary fix.
- // This function should never be called when a block is not present in the state.
- // It happens now because the order in withSelect rendering is not correct.
+ // This function should never be called when a block is not present in
+ // the state. It happens now because the order in withSelect rendering
+ // is not correct.
const { name, attributes, isValid } = block || {};
return {
@@ -369,21 +248,20 @@ const applyWithSelect = withSelect(
getLastMultiSelectedBlockClientId() === clientId,
// We only care about this prop when the block is selected
- // Thus to avoid unnecessary rerenders we avoid updating the prop if the block is not selected.
+ // Thus to avoid unnecessary rerenders we avoid updating the prop if
+ // the block is not selected.
isTypingWithinBlock:
( isSelected || isAncestorOfSelectedBlock ) && isTyping(),
mode: getBlockMode( clientId ),
isSelectionEnabled: isSelectionEnabled(),
- initialPosition: isSelected
- ? getSelectedBlocksInitialCaretPosition()
- : null,
isLocked: !! templateLock,
isFocusMode: focusMode && isLargeViewport,
isNavigationMode: isNavigationMode(),
isRTL,
- // Users of the editor.BlockListBlock filter used to be able to access the block prop
+ // Users of the editor.BlockListBlock filter used to be able to
+ // access the block prop.
// Ideally these blocks would rely on the clientId prop only.
// This is kept for backward compatibility reasons.
block,
@@ -401,8 +279,6 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => {
const {
updateBlockAttributes,
insertBlocks,
- insertDefaultBlock,
- removeBlock,
mergeBlocks,
replaceBlocks,
toggleSelection,
@@ -418,21 +294,12 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, { select } ) => {
const { rootClientId } = ownProps;
insertBlocks( blocks, index, rootClientId );
},
- onInsertDefaultBlockAfter() {
- const { clientId, rootClientId } = ownProps;
- const { getBlockIndex } = select( 'core/block-editor' );
- const index = getBlockIndex( clientId, rootClientId );
- insertDefaultBlock( {}, rootClientId, index + 1 );
- },
onInsertBlocksAfter( blocks ) {
const { clientId, rootClientId } = ownProps;
const { getBlockIndex } = select( 'core/block-editor' );
const index = getBlockIndex( clientId, rootClientId );
insertBlocks( blocks, index + 1, rootClientId );
},
- onRemove( clientId ) {
- removeBlock( clientId );
- },
onMerge( forward ) {
const { clientId } = ownProps;
const { getPreviousBlockClientId, getNextBlockClientId } = select(
@@ -474,7 +341,8 @@ export default compose(
applyWithSelect,
applyWithDispatch,
// block is sometimes not mounted at the right time, causing it be undefined
- // see issue for more info https://github.com/WordPress/gutenberg/issues/17013
+ // see issue for more info
+ // https://github.com/WordPress/gutenberg/issues/17013
ifCondition( ( { block } ) => !! block ),
withFilters( 'editor.BlockListBlock' )
)( BlockListBlock );
diff --git a/packages/block-editor/src/components/block-list/index.js b/packages/block-editor/src/components/block-list/index.js
index 8bf433d083fe45..02fc6d6806fb04 100644
--- a/packages/block-editor/src/components/block-list/index.js
+++ b/packages/block-editor/src/components/block-list/index.js
@@ -107,7 +107,7 @@ function BlockList( {
// This prop is explicitely computed and passed down
// to avoid being impacted by the async mode
// otherwise there might be a small delay to trigger the animation.
- animateOnChange={ index }
+ index={ index }
enableAnimation={ enableAnimation }
hasSelectedUI={
__experimentalUIParts.hasSelectedUI
diff --git a/packages/block-editor/src/components/block-list/insertion-point.js b/packages/block-editor/src/components/block-list/insertion-point.js
index 1ce81ac1199a24..7aa46530ad3878 100644
--- a/packages/block-editor/src/components/block-list/insertion-point.js
+++ b/packages/block-editor/src/components/block-list/insertion-point.js
@@ -127,12 +127,11 @@ export default function InsertionPoint( {
const isReverse = clientY < targetRect.top + targetRect.height / 2;
const blockNode = getBlockDOMNode( inserterClientId );
const container = isReverse ? containerRef.current : blockNode;
- const closest = getClosestTabbable( blockNode, true, container );
+ const closest =
+ getClosestTabbable( blockNode, true, container ) || blockNode;
const rect = new window.DOMRect( clientX, clientY, 0, 16 );
- if ( closest ) {
- placeCaretAtVerticalEdge( closest, isReverse, rect, false );
- }
+ placeCaretAtVerticalEdge( closest, isReverse, rect, false );
}
// Hide the inserter above the selected block and during multi-selection.
diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js
index 3bf65a4b0e4cb4..2a3ed6f0402ace 100644
--- a/packages/block-editor/src/components/index.js
+++ b/packages/block-editor/src/components/index.js
@@ -60,6 +60,7 @@ export { default as __experimentalBlockSettingsMenuFirstItem } from './block-set
export { default as __experimentalInserterMenuExtension } from './inserter-menu-extension';
export { default as BlockInspector } from './block-inspector';
export { default as BlockList } from './block-list';
+export { Block as __experimentalBlock } from './block-list/block-wrapper';
export { default as BlockMover } from './block-mover';
export { default as BlockPreview } from './block-preview';
export { default as BlockSelectionClearer } from './block-selection-clearer';
diff --git a/packages/block-editor/src/components/observe-typing/index.js b/packages/block-editor/src/components/observe-typing/index.js
index ae7cddfeb80aec..a4f948c13c3575 100644
--- a/packages/block-editor/src/components/observe-typing/index.js
+++ b/packages/block-editor/src/components/observe-typing/index.js
@@ -17,6 +17,7 @@ import {
ENTER,
BACKSPACE,
ESCAPE,
+ TAB,
} from '@wordpress/keycodes';
import { withSafeTimeout } from '@wordpress/compose';
@@ -111,7 +112,10 @@ function ObserveTyping( { children, setTimeout: setSafeTimeout } ) {
* @param {KeyboardEvent} event Keypress or keydown event to interpret.
*/
function stopTypingOnEscapeKey( event ) {
- if ( isTyping && event.keyCode === ESCAPE ) {
+ if (
+ isTyping &&
+ ( event.keyCode === ESCAPE || event.keyCode === TAB )
+ ) {
stopTyping();
}
}
diff --git a/packages/block-editor/src/utils/dom.js b/packages/block-editor/src/utils/dom.js
index 3fb25425678ab8..a12ffc276b16eb 100644
--- a/packages/block-editor/src/utils/dom.js
+++ b/packages/block-editor/src/utils/dom.js
@@ -91,7 +91,7 @@ export function getBlockClientId( node ) {
node = node.parentElement;
}
- const blockNode = node.closest( '.wp-block' );
+ const blockNode = node.closest( '.block-editor-block-list__block' );
if ( ! blockNode ) {
return;
diff --git a/packages/block-library/src/paragraph/edit.js b/packages/block-library/src/paragraph/edit.js
index b195980c746e6c..456c2e02820357 100644
--- a/packages/block-library/src/paragraph/edit.js
+++ b/packages/block-library/src/paragraph/edit.js
@@ -16,6 +16,7 @@ import {
RichText,
withFontSizes,
__experimentalUseColors,
+ __experimentalBlock as Block,
} from '@wordpress/block-editor';
import { createBlock } from '@wordpress/blocks';
import { compose } from '@wordpress/compose';
@@ -151,7 +152,7 @@ function ParagraphBlock( {
{
await insertBlock( blockTitle );
expect(
await getInnerHTML(
- `[data-type="${ blockName }"] [data-type="core/paragraph"] p`
+ `[data-type="${ blockName }"] [data-type="core/paragraph"]`
)
).toEqual( blockTitle );
} );
diff --git a/packages/e2e-tests/specs/editor/plugins/block-variations.js b/packages/e2e-tests/specs/editor/plugins/block-variations.js
index e75414add5eb43..2c8eb7596397c1 100644
--- a/packages/e2e-tests/specs/editor/plugins/block-variations.js
+++ b/packages/e2e-tests/specs/editor/plugins/block-variations.js
@@ -74,10 +74,7 @@ describe( 'Block variations', () => {
);
expect( successMessageBlock ).toBeDefined();
expect(
- await successMessageBlock.$eval(
- 'p.has-vivid-green-cyan-background-color',
- ( node ) => node.innerText
- )
+ await successMessageBlock.evaluate( ( node ) => node.innerText )
).toBe( 'This is a success message!' );
} );
test( 'Pick the additional variation in the inserted Columns block', async () => {
diff --git a/packages/e2e-tests/specs/editor/plugins/cpt-locking.test.js b/packages/e2e-tests/specs/editor/plugins/cpt-locking.test.js
index 5026baf4fbe616..bd3dcbbdad25e1 100644
--- a/packages/e2e-tests/specs/editor/plugins/cpt-locking.test.js
+++ b/packages/e2e-tests/specs/editor/plugins/cpt-locking.test.js
@@ -76,7 +76,7 @@ describe( 'cpt locking', () => {
it( 'should not error when deleting the cotents of a paragraph', async () => {
await page.click(
- '.block-editor-block-list__block[data-type="core/paragraph"] p'
+ '.block-editor-block-list__block[data-type="core/paragraph"]'
);
const textToType = 'Paragraph';
await page.keyboard.type( 'Paragraph' );
diff --git a/packages/e2e-tests/specs/editor/plugins/hooks-api.test.js b/packages/e2e-tests/specs/editor/plugins/hooks-api.test.js
index 07f018c528cf24..e015de70257c7a 100644
--- a/packages/e2e-tests/specs/editor/plugins/hooks-api.test.js
+++ b/packages/e2e-tests/specs/editor/plugins/hooks-api.test.js
@@ -34,7 +34,7 @@ describe( 'Using Hooks API', () => {
await clickBlockAppender();
await page.keyboard.type( 'First paragraph' );
const paragraphContent = await page.$eval(
- 'div[data-type="core/paragraph"] p',
+ 'p[data-type="core/paragraph"]',
( element ) => element.textContent
);
expect( paragraphContent ).toEqual( 'First paragraph' );
diff --git a/packages/e2e-tests/specs/editor/various/block-mover.test.js b/packages/e2e-tests/specs/editor/various/block-mover.test.js
index 09f6ba41a2a82a..3b936f212aba0b 100644
--- a/packages/e2e-tests/specs/editor/various/block-mover.test.js
+++ b/packages/e2e-tests/specs/editor/various/block-mover.test.js
@@ -18,6 +18,10 @@ describe( 'block mover', () => {
// Select a block so the block mover is rendered.
await page.focus( '.block-editor-block-list__block' );
+ // Move the mouse to show the block toolbar
+ await page.mouse.move( 0, 0 );
+ await page.mouse.move( 10, 10 );
+
const blockMover = await page.$$( '.block-editor-block-mover' );
// There should be a block mover.
expect( blockMover ).toHaveLength( 1 );
@@ -31,6 +35,10 @@ describe( 'block mover', () => {
// Select a block so the block mover has the possibility of being rendered.
await page.focus( '.block-editor-block-list__block' );
+ // Move the mouse to show the block toolbar
+ await page.mouse.move( 0, 0 );
+ await page.mouse.move( 10, 10 );
+
// Ensure no block mover exists when only one block exists on the page.
const blockMover = await page.$$( '.block-editor-block-mover' );
expect( blockMover ).toHaveLength( 0 );
diff --git a/packages/e2e-tests/specs/editor/various/editor-modes.test.js b/packages/e2e-tests/specs/editor/various/editor-modes.test.js
index f6f6a9ae10997b..15d767cd6cc2c9 100644
--- a/packages/e2e-tests/specs/editor/various/editor-modes.test.js
+++ b/packages/e2e-tests/specs/editor/various/editor-modes.test.js
@@ -18,7 +18,7 @@ describe( 'Editing modes (visual/HTML)', () => {
it( 'should switch between visual and HTML modes', async () => {
// This block should be in "visual" mode by default.
let visualBlock = await page.$$(
- '.block-editor-block-list__layout .block-editor-block-list__block .rich-text'
+ '.block-editor-block-list__layout .block-editor-block-list__block.rich-text'
);
expect( visualBlock ).toHaveLength( 1 );
@@ -52,7 +52,7 @@ describe( 'Editing modes (visual/HTML)', () => {
// This block should be in "visual" mode by default.
visualBlock = await page.$$(
- '.block-editor-block-list__layout .block-editor-block-list__block .rich-text'
+ '.block-editor-block-list__layout .block-editor-block-list__block.rich-text'
);
expect( visualBlock ).toHaveLength( 1 );
} );
diff --git a/packages/e2e-tests/specs/editor/various/keyboard-navigable-blocks.test.js b/packages/e2e-tests/specs/editor/various/keyboard-navigable-blocks.test.js
index 815528f8c0e7ba..7226e0750e5726 100644
--- a/packages/e2e-tests/specs/editor/various/keyboard-navigable-blocks.test.js
+++ b/packages/e2e-tests/specs/editor/various/keyboard-navigable-blocks.test.js
@@ -31,9 +31,6 @@ const tabThroughParagraphBlock = async ( paragraphText ) => {
await tabThroughBlockMoverControl();
await tabThroughBlockToolbar();
- await page.keyboard.press( 'Tab' );
- await expect( await getActiveLabel() ).toBe( 'Block: Paragraph' );
-
await page.keyboard.press( 'Tab' );
await expect( await getActiveLabel() ).toBe( 'Paragraph block' );
await expect(
diff --git a/packages/e2e-tests/specs/editor/various/preview.test.js b/packages/e2e-tests/specs/editor/various/preview.test.js
index 3a495a1189dfdc..e973c1b44f87d6 100644
--- a/packages/e2e-tests/specs/editor/various/preview.test.js
+++ b/packages/e2e-tests/specs/editor/various/preview.test.js
@@ -14,7 +14,6 @@ import {
publishPost,
saveDraft,
clickOnMoreMenuItem,
- pressKeyWithModifier,
} from '@wordpress/e2e-test-utils';
/** @typedef {import('puppeteer').Page} Page */
@@ -278,13 +277,13 @@ describe( 'Preview with Custom Fields enabled', () => {
// Return to editor and modify the title and content.
await editorPage.bringToFront();
await editorPage.click( '.editor-post-title__input' );
- await pressKeyWithModifier( 'primary', 'a' );
- await editorPage.keyboard.press( 'Delete' );
- await editorPage.keyboard.type( 'title 2' );
+ await editorPage.keyboard.press( 'End' );
+ await editorPage.keyboard.press( 'Backspace' );
+ await editorPage.keyboard.type( '2' );
await editorPage.keyboard.press( 'Tab' );
- await pressKeyWithModifier( 'primary', 'a' );
- await editorPage.keyboard.press( 'Delete' );
- await editorPage.keyboard.type( 'content 2' );
+ await editorPage.keyboard.press( 'End' );
+ await editorPage.keyboard.press( 'Backspace' );
+ await editorPage.keyboard.type( '2' );
// Open the preview page.
await waitForPreviewNavigation( previewPage );
diff --git a/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js b/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js
index b453bbc0aeeeed..794e8018691f61 100644
--- a/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js
+++ b/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js
@@ -181,7 +181,7 @@ describe( 'Reusable blocks', () => {
// Check that its content is up to date
const text = await page.$eval(
- '.block-editor-block-list__block[data-type="core/paragraph"] p',
+ '.block-editor-block-list__block[data-type="core/paragraph"]',
( element ) => element.innerText
);
expect( text ).toMatch( 'Oh! Hello there!' );
diff --git a/packages/e2e-tests/specs/editor/various/undo.test.js b/packages/e2e-tests/specs/editor/various/undo.test.js
index ed70af98bdaa2a..c37cba5b95f750 100644
--- a/packages/e2e-tests/specs/editor/various/undo.test.js
+++ b/packages/e2e-tests/specs/editor/various/undo.test.js
@@ -22,9 +22,16 @@ const getSelection = async () => {
return {};
}
- const editables = Array.from(
- selectedBlock.querySelectorAll( '[contenteditable]' )
- );
+ let editables;
+
+ if ( selectedBlock.getAttribute( 'contenteditable' ) ) {
+ editables = [ selectedBlock ];
+ } else {
+ editables = Array.from(
+ selectedBlock.querySelectorAll( '[contenteditable]' )
+ );
+ }
+
const editableIndex = editables.indexOf( document.activeElement );
const selection = window.getSelection();
diff --git a/packages/e2e-tests/specs/experiments/navigation.test.js b/packages/e2e-tests/specs/experiments/navigation.test.js
index aabda57d9d4087..fc11a057968fa2 100644
--- a/packages/e2e-tests/specs/experiments/navigation.test.js
+++ b/packages/e2e-tests/specs/experiments/navigation.test.js
@@ -76,6 +76,7 @@ async function mockCreatePageResponse( title, slug ) {
/**
* Interacts with the LinkControl to perform a search and select a returned suggestion
+ *
* @param {string} url What will be typed in the search input
* @param {string} label What the resulting label will be in the creating Navigation Link Block after the block is created.
* @param {string} type What kind of suggestion should be clicked, ie. 'url', 'create', or 'entity'