Skip to content

Commit

Permalink
Fix shift+tab behavior to move to toolbar when the preceding block ha…
Browse files Browse the repository at this point in the history
…s a form element

There was a bug in the tab navigation that allowed tabbing between form elements within a block. Shift + tab in this instance should move to the preceding tabbable element within the current block. However, it wasn't checking if the preceding form element was _within_ the current block, so when at the block boundary, a shift+tab would move to the preceding block's form element rather than the block toolbar.

This changes the behavior to constrain the form elements _within the selected block_ and only handle shift+tab or tab if the element being tabbed to is within the current block.
  • Loading branch information
jeryj committed Jun 15, 2023
1 parent 45e47b1 commit 8afcdaa
Showing 1 changed file with 41 additions and 5 deletions.
46 changes: 41 additions & 5 deletions packages/block-editor/src/components/writing-flow/use-tab-nav.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,17 +116,53 @@ export default function useTabNav() {
return;
}

// We want to constrain the tabbing to the block and its child blocks.
// If the preceding form element is within a different block,
// such as two sibling image blocks in the placeholder state,
// we want shift + tab from the first form element to move to the image
// block toolbar and not the previous image block's form element.
// TODO: Should this become a utility function?
/**
* Determine whether an element is part of or is the selected block.
*
* @param {Object} selectedBlockElement
* @param {Object} element
* @return {boolean} Whether the element is part of or is the selected block.
*/
const isElementPartOfSelectedBlock = (
selectedBlockElement,
element
) => {
// Check if the element is or is within the selected block by finding the
// closest element with a data-block attribute and seeing if
// it matches our current selected block ID
const elementBlockId = element
.closest( '[data-block]' )
?.getAttribute( 'data-block' );
const isElementSameBlock =
elementBlockId === getSelectedBlockClientId();

// Check if the element is a child of the selected block. This could be a
// child block in a group or column block, etc.
const isElementChildOfBlock =
selectedBlockElement.contains( element );

return isElementSameBlock || isElementChildOfBlock;
};

const nextTabbable = focus.tabbable[ direction ]( event.target );
// Allow tabbing from the block wrapper to a form element,
// and between form elements rendered in a block,
// and between form elements rendered in a block and its child blocks,
// such as inside a placeholder. Form elements are generally
// meant to be UI rather than part of the content. Ideally
// these are not rendered in the content and perhaps in the
// future they can be rendered in an iframe or shadow DOM.
if (
( isFormElement( event.target ) ||
event.target.getAttribute( 'data-block' ) ===
getSelectedBlockClientId() ) &&
isFormElement( focus.tabbable[ direction ]( event.target ) )
isFormElement( nextTabbable ) &&
isElementPartOfSelectedBlock(
event.target.closest( '[data-block]' ),
nextTabbable
)
) {
return;
}
Expand Down

0 comments on commit 8afcdaa

Please sign in to comment.