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 canvas iframe button accessibility and silent tab stops #59317

Merged
merged 3 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
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 @@ -283,9 +284,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 @@ -301,7 +306,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 @@ -346,7 +351,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
Loading