diff --git a/packages/block-editor/src/components/iframe/index.js b/packages/block-editor/src/components/iframe/index.js index de482c5f059dc8..673562fde34f58 100644 --- a/packages/block-editor/src/components/iframe/index.js +++ b/packages/block-editor/src/components/iframe/index.js @@ -273,13 +273,16 @@ function Iframe( { src={ src } title={ __( 'Editor canvas' ) } onKeyDown={ ( event ) => { + if ( props.onKeyDown ) { + props.onKeyDown( event ); + } // If the event originates from inside the iframe, it means // it bubbled through the portal, but only with React // events. We need to to bubble native events as well, // though by doing so we also trigger another React event, // so we need to stop the propagation of this event to avoid // duplication. - if ( + else if ( event.currentTarget.ownerDocument !== event.target.ownerDocument ) { diff --git a/test/e2e/specs/site-editor/navigation.spec.js b/test/e2e/specs/site-editor/navigation.spec.js new file mode 100644 index 00000000000000..bf84efc1b16071 --- /dev/null +++ b/test/e2e/specs/site-editor/navigation.spec.js @@ -0,0 +1,80 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +test.describe( 'Site editor navigation', () => { + test.beforeAll( async ( { requestUtils } ) => { + await requestUtils.activateTheme( 'emptytheme' ); + } ); + + test.afterAll( async ( { requestUtils } ) => { + await requestUtils.activateTheme( 'twentytwentyone' ); + } ); + + test( 'Can use keyboard to navigate the site editor', async ( { + admin, + page, + pageUtils, + } ) => { + await admin.visitSiteEditor(); + + // Navigate to the iframe + await pageUtils.pressKeys( 'Tab', { times: 3 } ); + // Open the command palette via button press + await expect( page.getByLabel( 'Open command palette' ) ).toBeFocused(); + await pageUtils.pressKeys( 'Enter' ); + await expect( + page.getByPlaceholder( 'Search for commands' ) + ).toBeFocused(); + // Return focus to the button + await pageUtils.pressKeys( 'Escape' ); + await expect( page.getByLabel( 'Open command palette' ) ).toBeFocused(); + // Open it again with Command + K + await pageUtils.pressKeys( 'primary+k' ); + await expect( + page.getByPlaceholder( 'Search for commands' ) + ).toBeFocused(); + await pageUtils.pressKeys( 'Escape' ); + await expect( page.getByLabel( 'Open command palette' ) ).toBeFocused(); + // Go to the Pages button + await pageUtils.pressKeys( 'Tab', { times: 4 } ); + await expect( + page.getByRole( 'button', { name: 'Pages' } ) + ).toBeFocused(); + await pageUtils.pressKeys( 'Enter' ); + // We should be in the Pages sidebar + await expect( + page.getByRole( 'button', { name: 'Back', exact: true } ) + ).toBeFocused(); + await pageUtils.pressKeys( 'Enter' ); + // Go back to the main navigation + await expect( + page.getByRole( 'button', { name: 'Pages' } ) + ).toBeFocused(); + await pageUtils.pressKeys( 'Tab', { times: 6 } ); + // Getting the actual iframe as a cleaner locator was suprisingly tricky, + // so we're using a css selector with .is-focused which should be present when the iframe has focus. + await expect( + page.locator( 'iframe[name="editor-canvas"].is-focused' ) + ).toBeFocused(); + + // Enter into editing mode + await pageUtils.pressKeys( 'Enter' ); + + // We should now be in editing mode + await pageUtils.pressKeys( 'Shift+Tab', { times: 9 } ); + + // Open the sidebar again + await expect( + page.getByRole( 'button', { + name: 'Open Navigation', + exact: true, + } ) + ).toBeFocused(); + await pageUtils.pressKeys( 'Enter' ); + await expect( + page.getByLabel( 'Go to the Dashboard' ).first() + ).toBeFocused(); + } ); +} );