diff --git a/packages/editor/src/components/observe-typing/index.js b/packages/editor/src/components/observe-typing/index.js index 2530399dc9eb3..fa3f669da5410 100644 --- a/packages/editor/src/components/observe-typing/index.js +++ b/packages/editor/src/components/observe-typing/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { includes } from 'lodash'; +import { over, includes } from 'lodash'; /** * WordPress dependencies @@ -9,7 +9,15 @@ import { includes } from 'lodash'; import { Component } from '@wordpress/element'; import { withSelect, withDispatch } from '@wordpress/data'; import { isTextField } from '@wordpress/dom'; -import { UP, RIGHT, DOWN, LEFT, ENTER, BACKSPACE } from '@wordpress/keycodes'; +import { + UP, + RIGHT, + DOWN, + LEFT, + ENTER, + BACKSPACE, + ESCAPE, +} from '@wordpress/keycodes'; import { withSafeTimeout, compose } from '@wordpress/compose'; /** @@ -41,6 +49,12 @@ class ObserveTyping extends Component { this.stopTypingOnMouseMove = this.stopTypingOnMouseMove.bind( this ); this.startTypingInTextField = this.startTypingInTextField.bind( this ); this.stopTypingOnNonTextField = this.stopTypingOnNonTextField.bind( this ); + this.stopTypingOnEscapeKey = this.stopTypingOnEscapeKey.bind( this ); + + this.onKeyDown = over( [ + this.startTypingInTextField, + this.stopTypingOnEscapeKey, + ] ); this.lastMouseMove = null; } @@ -108,6 +122,17 @@ class ObserveTyping extends Component { } } + /** + * Unsets typing flag if user presses Escape while typing flag is active. + * + * @param {KeyboardEvent} event Keypress or keydown event to interpret. + */ + stopTypingOnEscapeKey( event ) { + if ( this.props.isTyping && event.keyCode === ESCAPE ) { + this.props.onStopTyping(); + } + } + /** * Handles a keypress or keydown event to infer intention to start typing. * @@ -165,7 +190,7 @@ class ObserveTyping extends Component {
{ children }
diff --git a/test/e2e/specs/navigable-toolbar.test.js b/test/e2e/specs/navigable-toolbar.test.js new file mode 100644 index 0000000000000..1bf4089a270fb --- /dev/null +++ b/test/e2e/specs/navigable-toolbar.test.js @@ -0,0 +1,63 @@ +/** + * External dependencies + */ +import { forEach } from 'lodash'; + +/** + * Internal dependencies + */ +import { newPost, pressWithModifier } from '../support/utils'; + +describe( 'block toolbar', () => { + forEach( { + unified: true, + contextual: false, + }, ( isUnifiedToolbar, label ) => { + beforeEach( async () => { + await newPost(); + + await page.evaluate( ( _isUnifiedToolbar ) => { + const { select, dispatch } = wp.data; + const isCurrentlyUnified = select( 'core/edit-post' ).isFeatureActive( 'fixedToolbar' ); + if ( isCurrentlyUnified !== _isUnifiedToolbar ) { + dispatch( 'core/edit-post' ).toggleFeature( 'fixedToolbar' ); + } + }, isUnifiedToolbar ); + } ); + + const isInRichTextEditable = () => page.evaluate( () => ( + document.activeElement.classList.contains( 'editor-rich-text__tinymce' ) + ) ); + + const isInBlockToolbar = () => page.evaluate( () => ( + !! document.activeElement.closest( '.editor-block-toolbar' ) + ) ); + + describe( label, () => { + it( 'navigates in and out of toolbar by keyboard (Alt+F10, Escape)', async () => { + // Assumes new post focus starts in title. Create first new + // block by ArrowDown. + await page.keyboard.press( 'ArrowDown' ); + + // [TEMPORARY]: A new paragraph is not technically a block yet + // until starting to type within it. + await page.keyboard.type( 'Example' ); + + // [TEMPORARY]: With non-unified toolbar, the toolbar will not + // be visible since the user has entered a "typing" mode. + // Future iterations should ensure Alt+F10 works in a block + // to focus the toolbar regardless of whether it is presently + // visible. + await page.keyboard.press( 'Escape' ); + + // Upward + await pressWithModifier( 'Alt', 'F10' ); + expect( await isInBlockToolbar() ).toBe( true ); + + // Downward + await page.keyboard.press( 'Escape' ); + expect( await isInRichTextEditable() ).toBe( true ); + } ); + } ); + } ); +} );