[FIX] web_editor: properly paste blocks at the beginning of the editable

Pasting blocks at the beginning of the editable pasted the content in
the wrong place (after the first node instead of before it).

task-2879892

X-original-commit: f810b11204a3e227d7ac98429f21b2ddf93de215
Part-of: odoo/odoo#95331
This commit is contained in:
Antoine Guenet
2022-07-05 16:20:49 +02:00
parent 60d1d69b19
commit b3acaa7b52
2 changed files with 96 additions and 7 deletions
@@ -123,18 +123,35 @@ function insert(editor, data, {mode = "text"}) {
let lastChildNode = false;
if (containerLastChild.hasChildNodes()) {
let tempCurrentNode = currentNode;
for (const n of [...containerLastChild.childNodes]) {
tempCurrentNode.after(n);
tempCurrentNode = n;
const children = [...containerLastChild.childNodes];
if (insertBefore) {
children.reverse();
}
for (const child of children) {
tempCurrentNode[insertBefore ? 'before' : 'after'](child);
tempCurrentNode = child;
}
if (insertBefore) {
currentNode = tempCurrentNode;
lastChildNode = children[0];
} else {
lastChildNode = tempCurrentNode;
}
lastChildNode = tempCurrentNode;
}
if (containerFirstChild.hasChildNodes()) {
for (const n of [...containerFirstChild.childNodes]) {
currentNode.after(n);
const children = [...containerFirstChild.childNodes];
if (insertBefore) {
children.reverse();
}
for (const n of children) {
currentNode[insertBefore ? 'before' : 'after'](n);
currentNode = n;
}
if (insertBefore) {
currentNode = children[0];
insertBefore = false;
}
}
// If all the Html have been isolated, We force a split of the parent element
@@ -96,6 +96,15 @@ describe('Copy and paste', () => {
});
describe('Simple text', () => {
describe('range collapsed', async () => {
it('should paste a text at the beginning of a p', async () => {
await testEditor(BasicEditor, {
contentBefore: '<p>[]abcd</p>',
stepFunction: async editor => {
await pasteHtml(editor, 'x');
},
contentAfter: '<p>x[]abcd</p>',
});
});
it('should paste a text in a p', async () => {
await testEditor(BasicEditor, {
contentBefore: '<p>ab[]cd</p>',
@@ -231,6 +240,15 @@ describe('Copy and paste', () => {
describe('Simple html span', () => {
const simpleHtmlCharX = '<span style="color: rgb(0, 0, 0); font-family: -apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, Roboto, &quot;Helvetica Neue&quot;, Arial, &quot;Noto Sans&quot;, sans-serif, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;, &quot;Segoe UI Symbol&quot;, &quot;Noto Color Emoji&quot;; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;">x</span>';
describe('range collapsed', async () => {
it('should paste a text at the beginning of a p', async () => {
await testEditor(BasicEditor, {
contentBefore: '<p>[]abcd</p>',
stepFunction: async editor => {
await pasteHtml(editor, simpleHtmlCharX);
},
contentAfter: '<p>x[]abcd</p>',
});
});
it('should paste a text in a p', async () => {
await testEditor(BasicEditor, {
contentBefore: '<p>ab[]cd</p>',
@@ -345,6 +363,15 @@ describe('Copy and paste', () => {
describe('Simple html p', () => {
const simpleHtmlCharX = '<p>x</p>';
describe('range collapsed', async () => {
it('should paste a text at the beginning of a p', async () => {
await testEditor(BasicEditor, {
contentBefore: '<p>[]abcd</p>',
stepFunction: async editor => {
await pasteHtml(editor, simpleHtmlCharX);
},
contentAfter: '<p>x[]abcd</p>',
});
});
it('should paste a text in a p', async () => {
await testEditor(BasicEditor, {
contentBefore: '<p>ab[]cd</p>',
@@ -459,6 +486,15 @@ describe('Copy and paste', () => {
describe('Complex html span', () => {
const complexHtmlData = '<span style="color: rgb(0, 0, 0); font-family: -apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, Roboto, &quot;Helvetica Neue&quot;, Arial, &quot;Noto Sans&quot;, sans-serif, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;, &quot;Segoe UI Symbol&quot;, &quot;Noto Color Emoji&quot;; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;">1</span><b style="box-sizing: border-box; font-weight: bolder; color: rgb(0, 0, 0); font-family: -apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, Roboto, &quot;Helvetica Neue&quot;, Arial, &quot;Noto Sans&quot;, sans-serif, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;, &quot;Segoe UI Symbol&quot;, &quot;Noto Color Emoji&quot;; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">23</b><span style="color: rgb(0, 0, 0); font-family: -apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, Roboto, &quot;Helvetica Neue&quot;, Arial, &quot;Noto Sans&quot;, sans-serif, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;, &quot;Segoe UI Symbol&quot;, &quot;Noto Color Emoji&quot;; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;"><span> </span>4</span>';
describe('range collapsed', async () => {
it('should paste a text at the beginning of a p', async () => {
await testEditor(BasicEditor, {
contentBefore: '<p>[]abcd</p>',
stepFunction: async editor => {
await pasteHtml(editor, complexHtmlData);
},
contentAfter: '<p>1<b>23</b>&nbsp;4[]abcd</p>',
});
});
it('should paste a text in a p', async () => {
await testEditor(BasicEditor, {
contentBefore: '<p>ab[]cd</p>',
@@ -573,6 +609,15 @@ describe('Copy and paste', () => {
describe('Complex html p', () => {
const complexHtmlData = '<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; color: rgb(0, 0, 0); font-family: -apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, Roboto, &quot;Helvetica Neue&quot;, Arial, &quot;Noto Sans&quot;, sans-serif, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;, &quot;Segoe UI Symbol&quot;, &quot;Noto Color Emoji&quot;; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">12</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; color: rgb(0, 0, 0); font-family: -apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, Roboto, &quot;Helvetica Neue&quot;, Arial, &quot;Noto Sans&quot;, sans-serif, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;, &quot;Segoe UI Symbol&quot;, &quot;Noto Color Emoji&quot;; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">34</p>';
describe('range collapsed', async () => {
it('should paste a text at the beginning of a p', async () => {
await testEditor(BasicEditor, {
contentBefore: '<p>[]abcd</p>',
stepFunction: async editor => {
await pasteHtml(editor, complexHtmlData);
},
contentAfter: '<p>12</p><p>34[]abcd</p>',
});
});
it('should paste a text in a p', async () => {
await testEditor(BasicEditor, {
contentBefore: '<p>ab[]cd</p>',
@@ -700,9 +745,18 @@ describe('Copy and paste', () => {
});
});
});
describe('Complex html 3 p', () => {
describe('Complex html 3 p', () => {
const complexHtmlData = '<p>1<i>X</i>2</p><p>3<i>X</i>4</p><p>5<i>X</i>6</p>';
describe('range collapsed', async () => {
it('should paste a text at the beginning of a p', async () => {
await testEditor(BasicEditor, {
contentBefore: '<p>[]abcd</p>',
stepFunction: async editor => {
await pasteHtml(editor, complexHtmlData);
},
contentAfter: '<p>1<i>X</i>2</p><p>3<i>X</i>4</p><p>5<i>X</i>6[]abcd</p>',
});
});
it('should paste a text in a p', async () => {
await testEditor(BasicEditor, {
contentBefore: '<p>ab[]cd</p>',
@@ -830,6 +884,15 @@ describe('Copy and paste', () => {
describe('Complex html p+i', () => {
const complexHtmlData = '<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; color: rgb(0, 0, 0); font-family: -apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, Roboto, &quot;Helvetica Neue&quot;, Arial, &quot;Noto Sans&quot;, sans-serif, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;, &quot;Segoe UI Symbol&quot;, &quot;Noto Color Emoji&quot;; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">12</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; color: rgb(0, 0, 0); font-family: -apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, Roboto, &quot;Helvetica Neue&quot;, Arial, &quot;Noto Sans&quot;, sans-serif, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;, &quot;Segoe UI Symbol&quot;, &quot;Noto Color Emoji&quot;; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><i style="box-sizing: border-box;">ii</i></p>';
describe('range collapsed', async () => {
it('should paste a text at the beginning of a p', async () => {
await testEditor(BasicEditor, {
contentBefore: '<p>[]abcd</p>',
stepFunction: async editor => {
await pasteHtml(editor, complexHtmlData);
},
contentAfter: '<p>12</p><p><i>ii</i>[]abcd</p>',
});
});
it('should paste a text in a p', async () => {
await testEditor(BasicEditor, {
contentBefore: '<p>ab[]cd</p>',
@@ -955,6 +1018,15 @@ describe('Copy and paste', () => {
describe('Complex html 3p+b', () => {
const complexHtmlData = '<p>1<b>23</b></p><p>zzz</p><p>45<b>6</b>7</p>';
describe('range collapsed', async () => {
it('should paste a text at the beginning of a p', async () => {
await testEditor(BasicEditor, {
contentBefore: '<p>[]abcd</p>',
stepFunction: async editor => {
await pasteHtml(editor, complexHtmlData);
},
contentAfter: '<p>1<b>23</b></p><p>zzz</p><p>45<b>6</b>7[]abcd</p>',
});
});
it('should paste a text in a p', async () => {
await testEditor(BasicEditor, {
contentBefore: '<p>ab[]cd</p>',