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 a3e522b080af8b..3cd7b82917424e 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 @@ -79,23 +79,38 @@ export function useFocusFirstElement( clientId ) { return; } - // Find all tabbables within node. - const textInputs = focus.tabbable - .find( ref.current ) - .filter( ( node ) => isTextField( node ) ); + let target = ref.current; + let candidates; // 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 ) || ref.current; + + // Find all text fields or placeholders within the block. + candidates = focus.tabbable + .find( target ) + .filter( ( node ) => isTextField( node ) || node.shadowRoot ); + + target = ( isReverse ? last : first )( candidates ) || target; if ( ! isInsideRootBlock( ref.current, target ) ) { ref.current.focus(); return; } - placeCaretAtHorizontalEdge( target, isReverse ); + if ( target.shadowRoot ) { + // We must wait for the placeholder content to load. + setTimeout( () => { + // Find all text fields within the placeholder. + candidates = focus.tabbable + .find( target.shadowRoot ) + .filter( ( node ) => isTextField( node ) ); + target = ( isReverse ? last : first )( candidates ) || target; + placeCaretAtHorizontalEdge( target, isReverse ); + } ); + } else { + placeCaretAtHorizontalEdge( target, isReverse ); + } }, [ initialPosition ] ); return ref; diff --git a/packages/block-editor/src/components/embedded-admin-context/index.js b/packages/block-editor/src/components/embedded-admin-context/index.js index 6e14d36c322850..61b87b32efef1d 100644 --- a/packages/block-editor/src/components/embedded-admin-context/index.js +++ b/packages/block-editor/src/components/embedded-admin-context/index.js @@ -79,6 +79,12 @@ export default function EmbeddedAdminContext( props ) { clearTimeout( timeoutId ); }; }, [] ); + const content = ( + + { props.children } + + ); + return (
- { shadow && - createPortal( - - { props.children } - , - shadow - ) } + { shadow && createPortal( content, shadow ) }
); } diff --git a/packages/dom/src/tabbable.js b/packages/dom/src/tabbable.js index a6810e05af39bc..5e5200a0f1758c 100644 --- a/packages/dom/src/tabbable.js +++ b/packages/dom/src/tabbable.js @@ -148,7 +148,8 @@ function filterTabbable( focusables ) { } /** - * @param {Element} context + * @param {Element|Document|ShadowRoot} context + * * @return {Element[]} Tabbable elements within the context. */ export function find( context ) { diff --git a/packages/e2e-tests/specs/editor/various/embedding.test.js b/packages/e2e-tests/specs/editor/various/embedding.test.js index 51dee5fc52f669..d393f21f1d660c 100644 --- a/packages/e2e-tests/specs/editor/various/embedding.test.js +++ b/packages/e2e-tests/specs/editor/various/embedding.test.js @@ -167,6 +167,12 @@ async function insertEmbed( URL ) { `//*[contains(@class, "components-autocomplete__result") and contains(@class, "is-selected") and contains(text(), 'Embed')]` ); await page.keyboard.press( 'Enter' ); + await page.evaluate( + () => + new Promise( ( resolve ) => { + setTimeout( resolve ); + } ) + ); await page.keyboard.type( URL ); await page.keyboard.press( 'Enter' ); }