Skip to content

Commit 291c19c

Browse files
committed
Writing Flow: Test horizontal navigation as handled
Specifically for TinyMCE's overrides on inline boundary traversal
1 parent 6dd05fc commit 291c19c

File tree

3 files changed

+96
-2
lines changed

3 files changed

+96
-2
lines changed

editor/components/writing-flow/index.js

+37-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,41 @@ const isTabbableTextField = overEvery( [
5353
focus.tabbable.isTabbableIndex,
5454
] );
5555

56+
/**
57+
* Returns true if the given node is a TinyMCE inline boundary node.
58+
*
59+
* @param {Node} node Node to test.
60+
*
61+
* @return {boolean} Whether node is a TinyMCE inline boundary node.
62+
*/
63+
function isInlineBoundary( node ) {
64+
return node.getAttribute( 'data-mce-selected' ) === 'inline-boundary';
65+
}
66+
67+
/**
68+
* Returns true if it can be inferred that horizontal navigation has already
69+
* been handled in the desired direction, or false otherwise. Specifically,
70+
* this accounts for TinyMCE's inline boundary traversal, where its keydown
71+
* event takes effect before the event propagates to WritingFlow. This avoids
72+
* the caret from being moved outside the block when inline boundary occurs at
73+
* the end of a RichText.
74+
*
75+
* @see tinymce/src/core/main/ts/keyboard/ArrowKeys.ts (executeKeydownOverride)
76+
*
77+
* @param {boolean} isReverse Whether to test in reverse.
78+
*
79+
* @return {boolean} Whether horizontal navigation has been handled.
80+
*/
81+
function isHorizontalNavigationHandled( isReverse ) {
82+
const { isCollapsed, focusNode } = getSelection();
83+
if ( ! isCollapsed ) {
84+
return false;
85+
}
86+
87+
const siblingNode = focusNode[ isReverse ? 'nextSibling' : 'previousSibling' ];
88+
return !! siblingNode && isInlineBoundary( siblingNode );
89+
}
90+
5691
class WritingFlow extends Component {
5792
constructor() {
5893
super( ...arguments );
@@ -217,7 +252,8 @@ class WritingFlow extends Component {
217252
placeCaretAtVerticalEdge( closestTabbable, isReverse, this.verticalRect );
218253
event.preventDefault();
219254
}
220-
} else if ( isHorizontal && getSelection().isCollapsed && isHorizontalEdge( target, isReverse ) ) {
255+
} else if ( isHorizontal && getSelection().isCollapsed &&
256+
! isHorizontalNavigationHandled( isReverse ) && isHorizontalEdge( target, isReverse ) ) {
221257
const closestTabbable = this.getClosestTabbable( target, isReverse );
222258
placeCaretAtHorizontalEdge( closestTabbable, isReverse );
223259
event.preventDefault();

test/e2e/specs/__snapshots__/writing-flow.test.js.snap

+22
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,25 @@ exports[`adding blocks Should navigate with arrow keys 5`] = `"The Greeting: <br
1313
exports[`adding blocks Should navigate with arrow keys 6`] = `"Prefix: Hello"`;
1414
1515
exports[`adding blocks Should navigate with arrow keys 7`] = `"The Greeting: <br>Hello to the World! (Suffix)"`;
16+
17+
exports[`adding blocks Should navigate with arrow keys 8`] = `"<span id=\\"_mce_caret\\" data-mce-bogus=\\"1\\"><strong data-mce-selected=\\"inline-boundary\\">Bolded</strong></span><br data-mce-bogus=\\"1\\">"`;
18+
19+
exports[`adding blocks Should navigate with arrow keys 9`] = `"<strong data-mce-selected=\\"inline-boundary\\">Bolded Words</strong><br data-mce-bogus=\\"1\\">"`;
20+
21+
exports[`adding blocks Should navigate with arrow keys 10`] = `"<strong>Bolded Words</strong>After<br data-mce-bogus=\\"1\\">"`;
22+
23+
exports[`adding blocks Should navigate with arrow keys 11`] = `"The Greeting: <br>Hello to the World! (Suffixed)"`;
24+
25+
exports[`adding blocks Should navigate with arrow keys 12`] = `
26+
"<!-- wp:paragraph -->
27+
<p>The Greeting: <br/>Hello to the World! (Suffixed)</p>
28+
<!-- /wp:paragraph -->
29+
30+
<!-- wp:paragraph -->
31+
<p><strong>Bolded Words</strong>After</p>
32+
<!-- /wp:paragraph -->
33+
34+
<!-- wp:paragraph -->
35+
<p>Prefix: Hello</p>
36+
<!-- /wp:paragraph -->"
37+
`;

test/e2e/specs/writing-flow.test.js

+37-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ import { times } from 'lodash';
77
* Internal dependencies
88
*/
99
import '../support/bootstrap';
10-
import { newPost, newDesktopBrowserPage, pressWithModifier } from '../support/utils';
10+
import {
11+
newPost,
12+
newDesktopBrowserPage,
13+
pressWithModifier,
14+
getHTMLFromCodeEditor,
15+
} from '../support/utils';
1116

1217
describe( 'adding blocks', () => {
1318
beforeAll( async () => {
@@ -111,5 +116,36 @@ describe( 'adding blocks', () => {
111116
await page.keyboard.up( 'Shift' );
112117
await page.keyboard.type( ' (Suffix)' );
113118
await expectParagraphToMatchSnapshot( 1 );
119+
120+
// Should arrow once to escape out of inline boundary (bold, etc), and
121+
// escaping out should nullify any block traversal.
122+
await page.keyboard.press( 'Enter' );
123+
await pressWithModifier( 'Mod', 'B' ); // Bold
124+
await page.keyboard.type( 'Bolded' );
125+
await expectParagraphToMatchSnapshot( 2 );
126+
127+
// Pressing space while having escaped on right edge of inline boundary
128+
// should continue text as bolded.
129+
await page.keyboard.press( 'ArrowRight' );
130+
await page.keyboard.type( ' Words' );
131+
await expectParagraphToMatchSnapshot( 2 );
132+
133+
// But typing immediately after escaping should not be within.
134+
await page.keyboard.press( 'ArrowRight' );
135+
await page.keyboard.type( 'After' );
136+
await expectParagraphToMatchSnapshot( 2 );
137+
138+
// Navigate back to previous block. Change "(Suffix)" to "(Suffixed)"
139+
//
140+
// "Bolded WordsAfter" = 17 characters
141+
// + 2 inline boundaries
142+
// + 1 horizontal block traversal
143+
// + 1 into parenthesis
144+
// = 21
145+
await promiseSequence( times( 21, () => () => page.keyboard.press( 'ArrowLeft' ) ) );
146+
await page.keyboard.type( 'ed' );
147+
await expectParagraphToMatchSnapshot( 1 );
148+
149+
expect( await getHTMLFromCodeEditor() ).toMatchSnapshot();
114150
} );
115151
} );

0 commit comments

Comments
 (0)