From 1fb193d2cd47b10fa5c6b6cecc5010e00c73ea73 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Thu, 3 May 2018 16:17:34 -0400 Subject: [PATCH] Writing Flow: Test horizontal navigation as handled Specifically for TinyMCE's overrides on inline boundary traversal --- editor/components/writing-flow/index.js | 38 ++++++++++++++++++- .../__snapshots__/writing-flow.test.js.snap | 22 +++++++++++ test/e2e/specs/writing-flow.test.js | 38 ++++++++++++++++++- 3 files changed, 96 insertions(+), 2 deletions(-) diff --git a/editor/components/writing-flow/index.js b/editor/components/writing-flow/index.js index 50242ef55ab98..b35f66ec6768a 100644 --- a/editor/components/writing-flow/index.js +++ b/editor/components/writing-flow/index.js @@ -53,6 +53,41 @@ const isTabbableTextField = overEvery( [ focus.tabbable.isTabbableIndex, ] ); +/** + * Returns true if the given node is a TinyMCE inline boundary node. + * + * @param {Node} node Node to test. + * + * @return {boolean} Whether node is a TinyMCE inline boundary node. + */ +function isInlineBoundary( node ) { + return node.getAttribute( 'data-mce-selected' ) === 'inline-boundary'; +} + +/** + * Returns true if it can be inferred that horizontal navigation has already + * been handled in the desired direction, or false otherwise. Specifically, + * this accounts for TinyMCE's inline boundary traversal, where its keydown + * event takes effect before the event propagates to WritingFlow. This avoids + * the caret from being moved outside the block when inline boundary occurs at + * the end of a RichText. + * + * @see tinymce/src/core/main/ts/keyboard/ArrowKeys.ts (executeKeydownOverride) + * + * @param {boolean} isReverse Whether to test in reverse. + * + * @return {boolean} Whether horizontal navigation has been handled. + */ +function isHorizontalNavigationHandled( isReverse ) { + const { isCollapsed, focusNode } = getSelection(); + if ( ! isCollapsed ) { + return false; + } + + const siblingNode = focusNode[ isReverse ? 'nextSibling' : 'previousSibling' ]; + return !! siblingNode && isInlineBoundary( siblingNode ); +} + class WritingFlow extends Component { constructor() { super( ...arguments ); @@ -228,7 +263,8 @@ class WritingFlow extends Component { placeCaretAtVerticalEdge( closestTabbable, isReverse, this.verticalRect ); event.preventDefault(); } - } else if ( isHorizontal && getSelection().isCollapsed && isHorizontalEdge( target, isReverse ) ) { + } else if ( isHorizontal && getSelection().isCollapsed && + ! isHorizontalNavigationHandled( isReverse ) && isHorizontalEdge( target, isReverse ) ) { const closestTabbable = this.getClosestTabbable( target, isReverse ); placeCaretAtHorizontalEdge( closestTabbable, isReverse ); event.preventDefault(); diff --git a/test/e2e/specs/__snapshots__/writing-flow.test.js.snap b/test/e2e/specs/__snapshots__/writing-flow.test.js.snap index 2cc2c7ca4421d..f1afe1f03ab12 100644 --- a/test/e2e/specs/__snapshots__/writing-flow.test.js.snap +++ b/test/e2e/specs/__snapshots__/writing-flow.test.js.snap @@ -13,3 +13,25 @@ exports[`adding blocks Should navigate with arrow keys 5`] = `"The Greeting:
Hello to the World! (Suffix)"`; + +exports[`adding blocks Should navigate with arrow keys 8`] = `"Bolded
"`; + +exports[`adding blocks Should navigate with arrow keys 9`] = `"Bolded Words
"`; + +exports[`adding blocks Should navigate with arrow keys 10`] = `"Bolded WordsAfter
"`; + +exports[`adding blocks Should navigate with arrow keys 11`] = `"The Greeting:
Hello to the World! (Suffixed)"`; + +exports[`adding blocks Should navigate with arrow keys 12`] = ` +" +

The Greeting:
Hello to the World! (Suffixed)

+ + + +

Bolded WordsAfter

+ + + +

Prefix: Hello

+" +`; diff --git a/test/e2e/specs/writing-flow.test.js b/test/e2e/specs/writing-flow.test.js index 8b9fe6dc3883f..03ac637bad261 100644 --- a/test/e2e/specs/writing-flow.test.js +++ b/test/e2e/specs/writing-flow.test.js @@ -7,7 +7,12 @@ import { times } from 'lodash'; * Internal dependencies */ import '../support/bootstrap'; -import { newPost, newDesktopBrowserPage, pressWithModifier } from '../support/utils'; +import { + newPost, + newDesktopBrowserPage, + pressWithModifier, + getHTMLFromCodeEditor, +} from '../support/utils'; describe( 'adding blocks', () => { beforeAll( async () => { @@ -111,5 +116,36 @@ describe( 'adding blocks', () => { await page.keyboard.up( 'Shift' ); await page.keyboard.type( ' (Suffix)' ); await expectParagraphToMatchSnapshot( 1 ); + + // Should arrow once to escape out of inline boundary (bold, etc), and + // escaping out should nullify any block traversal. + await page.keyboard.press( 'Enter' ); + await pressWithModifier( 'Mod', 'B' ); // Bold + await page.keyboard.type( 'Bolded' ); + await expectParagraphToMatchSnapshot( 2 ); + + // Pressing space while having escaped on right edge of inline boundary + // should continue text as bolded. + await page.keyboard.press( 'ArrowRight' ); + await page.keyboard.type( ' Words' ); + await expectParagraphToMatchSnapshot( 2 ); + + // But typing immediately after escaping should not be within. + await page.keyboard.press( 'ArrowRight' ); + await page.keyboard.type( 'After' ); + await expectParagraphToMatchSnapshot( 2 ); + + // Navigate back to previous block. Change "(Suffix)" to "(Suffixed)" + // + // "Bolded WordsAfter" = 17 characters + // + 2 inline boundaries + // + 1 horizontal block traversal + // + 1 into parenthesis + // = 21 + await promiseSequence( times( 21, () => () => page.keyboard.press( 'ArrowLeft' ) ) ); + await page.keyboard.type( 'ed' ); + await expectParagraphToMatchSnapshot( 1 ); + + expect( await getHTMLFromCodeEditor() ).toMatchSnapshot(); } ); } );