diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js b/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js index 82d3f3e7ec2..cd48dc940c0 100644 --- a/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js +++ b/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js @@ -1238,7 +1238,9 @@ export class OdooEditor extends EventTarget { } } if (sideEffect) { - this._activateContenteditable(); + if (!this._fixLinkMutatedElements) { + this._activateContenteditable(); + } this.historySetSelection(step); this.dispatchEvent(new Event('historyRevert')); } @@ -2047,6 +2049,7 @@ export class OdooEditor extends EventTarget { for (const element of this._fixLinkMutatedElements.wasContenteditableNull) { element.removeAttribute('contenteditable'); } + delete this._fixLinkMutatedElements; } } _activateContenteditable() { @@ -3419,6 +3422,7 @@ export class OdooEditor extends EventTarget { clean() { this.observerUnactive(); + this.resetContenteditableLink(); this.cleanForSave(); this.observerActive(); } diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/link.test.js b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/link.test.js index 48b81352c8b..948a715ecf8 100644 --- a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/link.test.js +++ b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/link.test.js @@ -2,6 +2,7 @@ import { URL_REGEX, URL_REGEX_WITH_INFOS } from '../../src/OdooEditor.js'; import { BasicEditor, click, + deleteBackward, insertText, insertParagraphBreak, insertLineBreak, @@ -369,6 +370,71 @@ describe('Link', () => { }); }); }); + describe('isolated link', () => { + const clickOnLink = async editor => { + const a = editor.editable.querySelector('a'); + await click(a, { clientX: a.getBoundingClientRect().left + 5 }); + return a; + }; + it('should restrict editing to link when clicked', async () => { + await testEditor(BasicEditor, { + contentBefore: '

ab

', + stepFunction: async editor => { + const a = await clickOnLink(editor); + window.chai.expect(a.isContentEditable).to.be.equal(true); + }, + contentAfter: '

ab

', + }); + // The following is a regression test, checking that the link + // remains non-editable whenever the editable zone is contained by + // the link. + await testEditor(BasicEditor, { + contentBefore: '

ab

', + stepFunction: async editor => { + const a = await clickOnLink(editor); + window.chai.expect(a.isContentEditable).to.be.equal(false); + }, + contentAfter: '

ab

', + }, { + isRootEditable: false, + getContentEditableAreas: function (editor) { + return editor.editable.querySelectorAll('a span'); + } + }); + }); + it('should keep isolated link after a keyboard delete', async () => { + await testEditor(BasicEditor, { + contentBefore: '

ab[]c

', + stepFunction: async editor => { + const a = await clickOnLink(editor); + console.log(a.closest('.odoo-editor-editable').outerHTML); + await deleteBackward(editor); + console.log(a.closest('.odoo-editor-editable').outerHTML); + window.chai.expect(a.parentElement.isContentEditable).to.be.equal(false); + }, + contentAfterEdit: '

a[]\u200Bc

', + contentAfter: '

a[]c

', + }); + }); + it('should keep isolated link after a delete and typing', async () => { + await testEditor(BasicEditor, { + contentBefore: '

ab[]c

', + stepFunction: async editor => { + const a = await clickOnLink(editor); + window.chai.expect(a.parentElement.isContentEditable).to.be.equal(false); + await deleteBackward(editor); + window.chai.expect(a.parentElement.isContentEditable).to.be.equal(false); + await insertText(editor, '1'); + window.chai.expect(a.parentElement.isContentEditable).to.be.equal(false); + await insertText(editor, '2'); + window.chai.expect(a.parentElement.isContentEditable).to.be.equal(false); + await insertText(editor, '3'); + window.chai.expect(a.parentElement.isContentEditable).to.be.equal(false); + }, + contentAfter: '

a123[]c

', + }); + }); + }); describe('existing link', () => { it('should parse correctly a span inside a Link', async () => { await testEditor(BasicEditor, { @@ -472,32 +538,6 @@ describe('Link', () => { contentAfter: '

ab

c[]d

', }); }); - it('should restrict editing to link when clicked', async () => { - const initialContent = '

ab

'; - const editFunction = editableLink => async editor => { - const a = editor.editable.querySelector('a'); - await click(a, { clientX: a.getBoundingClientRect().left + 5 }); - window.chai.expect(a.isContentEditable).to.be.equal(editableLink); - }; - await testEditor(BasicEditor, { - contentBefore: initialContent, - stepFunction: editFunction(true), - contentAfter: '

ab

', - }); - // The following is a regression test, checking that the link - // remains non-editable whenever the editable zone is contained by - // the link. - await testEditor(BasicEditor, { - contentBefore: initialContent, - stepFunction: editFunction(false), - contentAfter: '

ab

', - }, { - isRootEditable: false, - getContentEditableAreas: function (editor) { - return editor.editable.querySelectorAll('a span'); - } - }); - }); // it('should select and replace all text and add the next char in bold', async () => { // await testEditor(BasicEditor, { // contentBefore: '

[]123

abc

',