Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix shift+tab behavior to move to toolbar when the preceding block has a form element #51548

Merged
merged 2 commits into from
Jun 22, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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