[FIX] web_editor: format and style are lost when copy content

Before this commit:

Format and style are lost when copying content that has some style or format
applied on it.

After this commit:

Now, able to copy content with its format and style.

Task-3263360

closes odoo/odoo#142984

X-original-commit: 8083b990845eeb5b1fa6aaaa21617f16d4d5a780
Signed-off-by: Geelen Sébastien (sge) <sge@odoo.com>
This commit is contained in:
dhba-odoo
2023-11-22 10:23:48 +00:00
committed by dhba
parent 4be8cac644
commit dc43b1fd01
2 changed files with 87 additions and 8 deletions
@@ -3783,6 +3783,19 @@ export class OdooEditor extends EventTarget {
rangeContent.lastChild.remove();
}
const commonAncestorElement = closestElement(range.commonAncestorContainer);
if (commonAncestorElement && !isBlock(rangeContent.firstChild)) {
// Get the list of ancestor elements starting from the provided
// commonAncestorElement up to the block-level element.
const blockEl = closestBlock(commonAncestorElement);
const ancestorsList = [commonAncestorElement, ...ancestors(commonAncestorElement, blockEl)];
// Wrap rangeContent with clones of their ancestors to keep the styles.
for (const ancestor of ancestorsList) {
const clone = ancestor.cloneNode();
clone.append(...rangeContent.childNodes);
rangeContent.appendChild(clone);
}
}
const dataHtmlElement = document.createElement('data');
dataHtmlElement.append(rangeContent);
const odooHtml = dataHtmlElement.innerHTML;
@@ -46,8 +46,8 @@ describe('Copy', () => {
const clipboardData = new DataTransfer();
await triggerEvent(editor.editable, 'copy', { clipboardData });
window.chai.expect(clipboardData.getData('text/plain')).to.be.equal('bcd');
window.chai.expect(clipboardData.getData('text/html')).to.be.equal('bcd');
window.chai.expect(clipboardData.getData('text/odoo-editor')).to.be.equal('bcd');
window.chai.expect(clipboardData.getData('text/html')).to.be.equal('<p>bcd</p>');
window.chai.expect(clipboardData.getData('text/odoo-editor')).to.be.equal('<p>bcd</p>');
},
});
await testEditor(BasicEditor, {
@@ -56,8 +56,8 @@ describe('Copy', () => {
const clipboardData = new DataTransfer();
await triggerEvent(editor.editable, 'copy', { clipboardData });
window.chai.expect(clipboardData.getData('text/plain')).to.be.equal('abc\nefg');
window.chai.expect(clipboardData.getData('text/html')).to.be.equal('abc<br>efg');
window.chai.expect(clipboardData.getData('text/odoo-editor')).to.be.equal('abc<br>efg');
window.chai.expect(clipboardData.getData('text/html')).to.be.equal('<p>abc<br>efg</p>');
window.chai.expect(clipboardData.getData('text/odoo-editor')).to.be.equal('<p>abc<br>efg</p>');
},
});
await testEditor(BasicEditor, {
@@ -71,6 +71,72 @@ describe('Copy', () => {
},
});
});
it('should wrap the selected text with clones of ancestors up to a block element to keep styles', async () => {
await testEditor(BasicEditor, {
contentBefore: '<p>[<span style="font-size: 16px;">Test</span> <span style="font-size: 48px;"><font style="color: rgb(255, 0, 0);">Test</font></span>]</p>',
stepFunction: async editor => {
const clipboardData = new DataTransfer();
triggerEvent(editor.editable, 'copy', { clipboardData });
window.chai.expect(clipboardData.getData('text/plain')).to.be.equal('Test Test');
window.chai.expect(clipboardData.getData('text/html')).to.be.equal('<p><span style="font-size: 16px;">Test</span> <span style="font-size: 48px;"><font style="color: rgb(255, 0, 0);">Test</font></span></p>');
window.chai.expect(clipboardData.getData('text/odoo-editor')).to.be.equal('<p><span style="font-size: 16px;">Test</span> <span style="font-size: 48px;"><font style="color: rgb(255, 0, 0);">Test</font></span></p>');
},
});
await testEditor(BasicEditor, {
contentBefore: '<p><strong><em><u><font class="text-o-color-1">hello [there]</font></u></em></strong></p>',
stepFunction: async editor => {
const clipboardData = new DataTransfer();
triggerEvent(editor.editable, 'copy', { clipboardData });
window.chai.expect(clipboardData.getData('text/plain')).to.be.equal('there');
window.chai.expect(clipboardData.getData('text/html')).to.be.equal('<p><strong><em><u><font class="text-o-color-1">there</font></u></em></strong></p>');
window.chai.expect(clipboardData.getData('text/odoo-editor')).to.be.equal('<p><strong><em><u><font class="text-o-color-1">there</font></u></em></strong></p>');
},
});
});
it('should copy the selection as a single list item', async () => {
await testEditor(BasicEditor, {
contentBefore: '<ul><li>[First]</li><li>Second</li>',
stepFunction: async editor => {
const clipboardData = new DataTransfer();
triggerEvent(editor.editable, 'copy', { clipboardData });
window.chai.expect(clipboardData.getData('text/plain')).to.be.equal('First');
window.chai.expect(clipboardData.getData('text/html')).to.be.equal('<li>First</li>');
window.chai.expect(clipboardData.getData('text/odoo-editor')).to.be.equal('<li>First</li>');
},
});
await testEditor(BasicEditor, {
contentBefore: '<ul><li>First [List]</li><li>Second</li>',
stepFunction: async editor => {
const clipboardData = new DataTransfer();
triggerEvent(editor.editable, 'copy', { clipboardData });
window.chai.expect(clipboardData.getData('text/plain')).to.be.equal('List');
window.chai.expect(clipboardData.getData('text/html')).to.be.equal('<li>List</li>');
window.chai.expect(clipboardData.getData('text/odoo-editor')).to.be.equal('<li>List</li>');
},
});
await testEditor(BasicEditor, {
contentBefore: '<ul><li><span style="font-size: 48px;"><font style="color: rgb(255, 0, 0);">[First]</font></span></li><li>Second</li>',
stepFunction: async editor => {
const clipboardData = new DataTransfer();
triggerEvent(editor.editable, 'copy', { clipboardData });
window.chai.expect(clipboardData.getData('text/plain')).to.be.equal('First');
window.chai.expect(clipboardData.getData('text/html')).to.be.equal('<li><span style="font-size: 48px;"><font style="color: rgb(255, 0, 0);">First</font></span></li>');
window.chai.expect(clipboardData.getData('text/odoo-editor')).to.be.equal('<li><span style="font-size: 48px;"><font style="color: rgb(255, 0, 0);">First</font></span></li>');
},
});
})
it('should copy the selection as a list with multiple list items', async () => {
await testEditor(BasicEditor, {
contentBefore: '<ul><li>[First</li><li>Second]</li>',
stepFunction: async editor => {
const clipboardData = new DataTransfer();
triggerEvent(editor.editable, 'copy', { clipboardData });
window.chai.expect(clipboardData.getData('text/plain')).to.be.equal('First\nSecond');
window.chai.expect(clipboardData.getData('text/html')).to.be.equal('<ul><li>First</li><li>Second</li></ul>');
window.chai.expect(clipboardData.getData('text/odoo-editor')).to.be.equal('<ul><li>First</li><li>Second</li></ul>');
},
});
});
});
});
describe('Cut', () => {
@@ -105,8 +171,8 @@ describe('Cut', () => {
const clipboardData = new DataTransfer();
await triggerEvent(editor.editable, 'cut', { clipboardData });
window.chai.expect(clipboardData.getData('text/plain')).to.be.equal('bcd');
window.chai.expect(clipboardData.getData('text/html')).to.be.equal('bcd');
window.chai.expect(clipboardData.getData('text/odoo-editor')).to.be.equal('bcd');
window.chai.expect(clipboardData.getData('text/html')).to.be.equal('<p>bcd</p>');
window.chai.expect(clipboardData.getData('text/odoo-editor')).to.be.equal('<p>bcd</p>');
},
contentAfter: '<p>a[]e</p>',
});
@@ -116,8 +182,8 @@ describe('Cut', () => {
const clipboardData = new DataTransfer();
await triggerEvent(editor.editable, 'cut', { clipboardData });
window.chai.expect(clipboardData.getData('text/plain')).to.be.equal('abc\nefg');
window.chai.expect(clipboardData.getData('text/html')).to.be.equal('abc<br>efg');
window.chai.expect(clipboardData.getData('text/odoo-editor')).to.be.equal('abc<br>efg');
window.chai.expect(clipboardData.getData('text/html')).to.be.equal('<p>abc<br>efg</p>');
window.chai.expect(clipboardData.getData('text/odoo-editor')).to.be.equal('<p>abc<br>efg</p>');
},
contentAfter: '<p>[]<br></p>',
});