From 41ff1f224ff1dcb0baecec9206c3dc3a3d1d2dca Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Mon, 6 Aug 2018 20:26:19 -0400 Subject: [PATCH] DOM: Restore horizontal edge traversal implementation (#8461) * Add a failing test for removing an empty first line. * DOM: Restore horizontal edge traversal implementation Co-Authored-By: iseulde --- packages/dom/src/dom.js | 57 ++++++++++++++++--- .../splitting-merging.test.js.snap | 6 ++ test/e2e/specs/splitting-merging.test.js | 17 ++++++ 3 files changed, 71 insertions(+), 9 deletions(-) diff --git a/packages/dom/src/dom.js b/packages/dom/src/dom.js index 5c9049c984b75c..7cc843c32e6173 100644 --- a/packages/dom/src/dom.js +++ b/packages/dom/src/dom.js @@ -94,15 +94,54 @@ export function isHorizontalEdge( container, isReverse ) { range.collapse( ! isSelectionForward( selection ) ); } - const { endContainer, endOffset } = range; - range.selectNodeContents( container ); - range.setEnd( endContainer, endOffset ); - - // Check if the caret position is at the expected start/end position based - // on the value of `isReverse`. If so, consider the horizontal edge to be - // reached. - const caretOffset = range.toString().length; - return caretOffset === ( isReverse ? 0 : container.textContent.length ); + let node = range.startContainer; + + let extentOffset; + if ( isReverse ) { + // When in reverse, range node should be first. + extentOffset = 0; + } else if ( node.nodeValue ) { + // Otherwise, vary by node type. A text node has no children. Its range + // offset reflects its position in nodeValue. + // + // "If the startContainer is a Node of type Text, Comment, or + // CDATASection, then the offset is the number of characters from the + // start of the startContainer to the boundary point of the Range." + // + // See: https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset + // See: https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeValue + extentOffset = node.nodeValue.length; + } else { + // "For other Node types, the startOffset is the number of child nodes + // between the start of the startContainer and the boundary point of + // the Range." + // + // See: https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset + extentOffset = node.childNodes.length; + } + + // Offset of range should be at expected extent. + const position = isReverse ? 'start' : 'end'; + const offset = range[ `${ position }Offset` ]; + if ( offset !== extentOffset ) { + return false; + } + + // If confirmed to be at extent, traverse up through DOM, verifying that + // the node is at first or last child for reverse or forward respectively. + // Continue until container is reached. + const order = isReverse ? 'first' : 'last'; + while ( node !== container ) { + const parentNode = node.parentNode; + if ( parentNode[ `${ order }Child` ] !== node ) { + return false; + } + + node = parentNode; + } + + // If reached, range is assumed to be at edge. + return true; } /** diff --git a/test/e2e/specs/__snapshots__/splitting-merging.test.js.snap b/test/e2e/specs/__snapshots__/splitting-merging.test.js.snap index 33ffca291ab73e..c7ddb36297de94 100644 --- a/test/e2e/specs/__snapshots__/splitting-merging.test.js.snap +++ b/test/e2e/specs/__snapshots__/splitting-merging.test.js.snap @@ -1,5 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`splitting and merging blocks Should delete an empty first line 1`] = ` +" +

+" +`; + exports[`splitting and merging blocks Should merge into inline boundary position 1`] = ` "

Bar

diff --git a/test/e2e/specs/splitting-merging.test.js b/test/e2e/specs/splitting-merging.test.js index 25a408988a17e2..11c82b90b0fe39 100644 --- a/test/e2e/specs/splitting-merging.test.js +++ b/test/e2e/specs/splitting-merging.test.js @@ -70,4 +70,21 @@ describe( 'splitting and merging blocks', () => { expect( await getEditedPostContent() ).toMatchSnapshot(); } ); + + it( 'Should delete an empty first line', async () => { + // Regression Test: When a paragraph block has line break, and the first + // line has no text, pressing backspace at the start of the second line + // should remove the first. + // + // See: https://github.com/WordPress/gutenberg/issues/8388 + await insertBlock( 'Paragraph' ); + await page.keyboard.down( 'Shift' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.up( 'Shift' ); + + // Delete the soft line break. + await page.keyboard.press( 'Backspace' ); + + expect( await getEditedPostContent() ).toMatchSnapshot(); + } ); } );