Skip to content

Commit

Permalink
fix selection after mousedown if contenteditable div contains only on…
Browse files Browse the repository at this point in the history
…e new line char (#2365)
  • Loading branch information
AlexKamaev committed May 17, 2018
1 parent ce022b4 commit 5c70900
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 15 deletions.
60 changes: 48 additions & 12 deletions src/client/core/utils/content-editable.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,24 +110,24 @@ function isNodeAfterNodeBlockWithBreakLine (parent, node) {
return false;
}

export function getFirstVisibleTextNode (el) {
function getFirstTextNode (el, onlyVisible) {
var children = el.childNodes;
var childrenLength = domUtils.getChildNodesLength(children);
var curNode = null;
var child = null;
var isNotContentEditableElement = null;

if (!childrenLength && isVisibleTextNode(el))
if (!childrenLength && checkTextNodeVisibility(el, onlyVisible))
return el;

for (var i = 0; i < childrenLength; i++) {
curNode = children[i];
isNotContentEditableElement = domUtils.isElementNode(curNode) && !domUtils.isContentEditableElement(curNode);

if (isVisibleTextNode(curNode))
if (checkTextNodeVisibility(curNode, onlyVisible))
return curNode;
else if (domUtils.isRenderedNode(curNode) && hasVisibleChildren(curNode) && !isNotContentEditableElement) {
child = getFirstVisibleTextNode(curNode);
child = getFirstTextNode(curNode, onlyVisible);

if (child)
return child;
Expand All @@ -137,6 +137,10 @@ export function getFirstVisibleTextNode (el) {
return child;
}

export function getFirstVisibleTextNode (el) {
return getFirstTextNode(el, true);
}

export function getLastTextNode (el, onlyVisible) {
var children = el.childNodes;
var childrenLength = domUtils.getChildNodesLength(children);
Expand Down Expand Up @@ -210,8 +214,15 @@ export function isInvisibleTextNode (node) {
return firstVisibleIndex === nodeValue.length && lastVisibleIndex === 0;
}

function checkTextNodeVisibility (node, visibility) {
if (!domUtils.isTextNode(node))
return false;

return isInvisibleTextNode(node) !== visibility;
}

function isVisibleTextNode (node) {
return domUtils.isTextNode(node) && !isInvisibleTextNode(node);
return checkTextNodeVisibility(node, true);
}

function isSkippableNode (node) {
Expand Down Expand Up @@ -532,17 +543,42 @@ export function getFirstVisiblePosition (el) {
}

export function getLastVisiblePosition (el) {
var lastVisibleTextChild = domUtils.isTextNode(el) ? el : getLastTextNode(el, true);
var curDocument = domUtils.findDocument(el);
var range = curDocument.createRange();
var firstVisibleTextChild = el;
var lastVisibleTextChild = el;
var isTextNode = domUtils.isTextNode(el);

if (lastVisibleTextChild) {
range.selectNodeContents(lastVisibleTextChild);
if (!isTextNode) {
firstVisibleTextChild = getFirstTextNode(el, false);
lastVisibleTextChild = getLastTextNode(el, true);
}

return calculatePositionByNodeAndOffset(el, { node: lastVisibleTextChild, offset: range.endOffset });
if (!lastVisibleTextChild)
return 0;

var curDocument = domUtils.findDocument(el);
var range = curDocument.createRange();

var isSingleTextNode = lastVisibleTextChild === firstVisibleTextChild;
var isNewLineChar = lastVisibleTextChild.nodeValue === String.fromCharCode(10);

if (isSingleTextNode && isNewLineChar && hasWhiteSpacePreStyle(lastVisibleTextChild, el))
return 0;

range.selectNodeContents(lastVisibleTextChild);

return calculatePositionByNodeAndOffset(el, { node: lastVisibleTextChild, offset: range.endOffset });
}

function hasWhiteSpacePreStyle (el, container) {
const whiteSpacePreStyles = ['pre', 'pre-wrap', 'pre-line'];

while (el !== container) {
el = el.parentNode;
if (arrayUtils.indexOf(whiteSpacePreStyles, styleUtils.get(el, 'white-space')) > -1)
return true;
}

return 0;
return false;
}

function getContentEditableNodes (target) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
var hammerhead = window.getTestCafeModule('hammerhead');
var browserUtils = hammerhead.utils.browser;

var testCafeCore = window.getTestCafeModule('testCafeCore');
var domUtils = testCafeCore.get('./utils/dom');
var testCafeCore = window.getTestCafeModule('testCafeCore');
var domUtils = testCafeCore.get('./utils/dom');

var testCafeAutomation = window.getTestCafeModule('testCafeAutomation');
var TypeOptions = testCafeAutomation.get('../../test-run/commands/options').TypeOptions;
Expand Down Expand Up @@ -142,4 +141,56 @@ $(document).ready(function () {
});
});
}

asyncTest('selection after mousedown should ignore single new line character', function () {

function testWithWhiteSpaceStyle (whiteSpace, expectedText, expectedOffset) {
var editor = document.createElement('div');
var span = document.createElement('span');
var type = new TypeAutomation(editor, 'Hello World', {});

editor.className = TEST_ELEMENT_CLASS;
editor.style.whiteSpace = whiteSpace;
editor.contentEditable = true;
span.innerHTML = String.fromCharCode(10);

editor.appendChild(span);
document.body.appendChild(editor);

var onSelectionChange = function () {
equal(document.getSelection().anchorOffset, expectedOffset);
document.removeEventListener('selectionchange', onSelectionChange, true);
};

document.addEventListener('selectionchange', onSelectionChange, true);

return type
.run()
.then(function () {
equal(editor.textContent, expectedText, 'white-space: ' + whiteSpace);
removeTestElements();
return;
});
}

var newLineFirst = '\nHello' + String.fromCharCode(160) + 'World';
var newLineLast = 'Hello' + String.fromCharCode(160) + 'World\n';

testWithWhiteSpaceStyle('normal', newLineFirst, 1)
.then(function () {
return testWithWhiteSpaceStyle('nowrap', newLineFirst, 1);
})
.then(function () {
return testWithWhiteSpaceStyle('pre', newLineLast, 0);
})
.then(function () {
return testWithWhiteSpaceStyle('pre-wrap', newLineLast, 0);
})
.then(function () {
return testWithWhiteSpaceStyle('pre-line', newLineLast, 0);
})
.then(function () {
startNext();
});
});
});

0 comments on commit 5c70900

Please sign in to comment.