diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index aac68e2e6faa88..d5a70b040639d4 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -400,6 +400,15 @@ function BlockListBlock( { className ); + // By default, a block is focusable, which enables it to be set as the + // selected block when the user interacts within its content. Through + // assignment of its wrapper props, however, a block can opt-out of this + // behavior to effectively treat the block as a "pass-through", one which + // is not intended to be directly selected under normal circumstances. + // Often this occurs in the context of deep block nesting, where focus + // selection instead occurs by one of the ancestor blocks. + let isFocusable = true; + // Determine whether the block has props to apply to the wrapper. let blockWrapperProps = wrapperProps; if ( blockType.getEditWrapperProps ) { @@ -407,6 +416,11 @@ function BlockListBlock( { ...blockWrapperProps, ...blockType.getEditWrapperProps( attributes ), }; + + isFocusable = ! ( 'tabIndex' in blockWrapperProps ) || ( + isFinite( blockWrapperProps.tabIndex ) && + Number( blockWrapperProps.tabIndex ) !== -1 + ); } const blockElementId = `block-${ clientId }`; @@ -452,7 +466,7 @@ function BlockListBlock( { className={ wrapperClassName } data-type={ name } onTouchStart={ onTouchStart } - onFocus={ onFocus } + onFocus={ isFocusable ? onFocus : null } onClick={ onTouchStop } onKeyDown={ deleteOrInsertAfterWrapper } tabIndex="0" diff --git a/packages/block-editor/src/components/block-list/style.scss b/packages/block-editor/src/components/block-list/style.scss index 8ee3558ddf6398..45e6c948d64390 100644 --- a/packages/block-editor/src/components/block-list/style.scss +++ b/packages/block-editor/src/components/block-list/style.scss @@ -316,6 +316,16 @@ } } + // Disable clickthrough overlay for blocks which aren't focusable, since + // overlay depends on dismissal by selection. This effectively treats the + // block as a "pass-through", one which is not intended to be selected + // through standard focus interactions. + &[tabindex="-1"], + &:not([tabindex]) { + > .block-editor-block-list__block-edit .block-editor-inner-blocks.has-overlay::after { + display: none; + } + } // Alignments &[data-align="left"], diff --git a/packages/block-library/src/column/index.js b/packages/block-library/src/column/index.js index 250ce0bad65a42..2f6b98614f2c72 100644 --- a/packages/block-library/src/column/index.js +++ b/packages/block-library/src/column/index.js @@ -27,13 +27,20 @@ export const settings = { }, getEditWrapperProps( attributes ) { const { width } = attributes; + + // A column should act as a "pass-through", meaning that it cannot be + // selected by typical focus interactions. A block becomes selected by + // virtue of its focus handler, and by nullifying its tabIndex, it will + // no longer handle focus events. + const props = { tabIndex: undefined }; + if ( Number.isFinite( width ) ) { - return { - style: { - flexBasis: width + '%', - }, + props.style = { + flexBasis: width + '%', }; } + + return props; }, edit, save,