Skip to content

Commit

Permalink
Fix canvas iframe button accessibility and silent tab stops (#59317)
Browse files Browse the repository at this point in the history
* Improve labeling of the canvas iframe when it has role button.

* Avoid unexpected tab stops in view mode.

* Improve test to make sure there's no silent tab stops.

Co-authored-by: afercia <[email protected]>
Co-authored-by: carolinan <[email protected]>
Co-authored-by: SergeyBiryukov <[email protected]>
Co-authored-by: abhi3315 <[email protected]>
Co-authored-by: benridane <[email protected]>
  • Loading branch information
6 people authored Feb 27, 2024
1 parent 1befd5a commit 98552b2
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 10 deletions.
11 changes: 8 additions & 3 deletions packages/block-editor/src/components/iframe/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ function Iframe( {
shouldZoom = false,
readonly,
forwardedRef: ref,
title = __( 'Editor canvas' ),
...props
} ) {
const { resolvedAssets, isPreviewMode, isZoomOutMode } = useSelect(
Expand Down Expand Up @@ -295,9 +296,13 @@ function Iframe( {
}
}, [ scale, frameSize, marginFromScaling, iframeDocument ] );

// Make sure to not render the before and after focusable div elements in view
// mode. They're only needed to capture focus in edit mode.
const shouldRenderFocusCaptureElements = tabIndex >= 0 && ! isPreviewMode;

return (
<>
{ tabIndex >= 0 && before }
{ shouldRenderFocusCaptureElements && before }
{ /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */ }
<iframe
{ ...props }
Expand All @@ -313,7 +318,7 @@ function Iframe( {
// mode. Also preload the styles to avoid a flash of unstyled
// content.
src={ src }
title={ __( 'Editor canvas' ) }
title={ title }
onKeyDown={ ( event ) => {
if ( props.onKeyDown ) {
props.onKeyDown( event );
Expand Down Expand Up @@ -358,7 +363,7 @@ function Iframe( {
iframeDocument.documentElement
) }
</iframe>
{ tabIndex >= 0 && after }
{ shouldRenderFocusCaptureElements && after }
</>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,11 @@ function EditorCanvas( { enableResizing, settings, children, ...props } ) {
}
}, [ canvasMode ] );

const viewModeProps = {
'aria-label': __( 'Editor Canvas' ),
// In view mode, make the canvas iframe be perceived and behave as a button
// to switch to edit mode, with a meaningful label and no title attribute.
const viewModeIframeProps = {
'aria-label': __( 'Edit' ),
title: null,
role: 'button',
tabIndex: 0,
onFocus: () => setIsFocused( true ),
Expand Down Expand Up @@ -115,7 +118,7 @@ function EditorCanvas( { enableResizing, settings, children, ...props } ) {
}
),
...props,
...( canvasMode === 'view' ? viewModeProps : {} ),
...( canvasMode === 'view' ? viewModeIframeProps : {} ),
} }
>
{ children }
Expand Down
31 changes: 27 additions & 4 deletions test/e2e/specs/site-editor/navigation.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,35 @@ test.describe( 'Site editor navigation', () => {
page.getByRole( 'button', { name: 'Pages' } )
).toBeFocused();

// Test: Can navigate into the iframe using the keyboard
await editorNavigationUtils.tabToLabel( 'Editor Canvas' );
const editorCanvasButton = page.getByRole( 'button', {
name: 'Editor Canvas',
// Navigate to the Saved button first, as it precedes the editor iframe.
await editorNavigationUtils.tabToLabel( 'Saved' );
const savedButton = page.getByRole( 'button', {
name: 'Saved',
} );
await expect( savedButton ).toBeFocused();

// Get the iframe when it has a role=button and Edit label.
const editorCanvasRegion = page.getByRole( 'region', {
name: 'Editor content',
} );
const editorCanvasButton = editorCanvasRegion.getByRole( 'button', {
name: 'Edit',
} );

// Test that there are no tab stops between the Saved button and the
// focusable iframe with role=button.
await pageUtils.pressKeys( 'Tab' );
await expect( editorCanvasButton ).toBeFocused();

// Tab to the Pages item to move focus back in the UI.
await editorNavigationUtils.tabToLabel( 'Pages' );
await expect(
page.getByRole( 'button', { name: 'Pages' } )
).toBeFocused();

// Test again can navigate into the iframe using the keyboard.
await editorNavigationUtils.tabToLabel( 'Edit' );

// Enter into the site editor frame
await pageUtils.pressKeys( 'Enter' );
// Focus should be on the iframe without the button role
Expand Down

0 comments on commit 98552b2

Please sign in to comment.