[FIX] web_editor: fix isolated link

Sometimes the link isolation was broken when
the editor rollbacked some browser behavior :
the content editable was reset on the all editable.
And thus the cursor was able to get out of the
isolated link creating issues.

task-2957000

X-original-commit: 0a04ef6c2617741b90c151c367c1731ed180ad70
Part-of: odoo/odoo#105066
This commit is contained in:
Sébastien Geelen (sge)
2022-11-07 10:06:30 +01:00
committed by David Monjoie
parent 6c0488d9dc
commit 1176e0cab0
2 changed files with 71 additions and 27 deletions
@@ -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();
}
@@ -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: '<p>a<a href="#/"><span>b</span></a></p>',
stepFunction: async editor => {
const a = await clickOnLink(editor);
window.chai.expect(a.isContentEditable).to.be.equal(true);
},
contentAfter: '<p>a<a href="#/"><span>b</span></a></p>',
});
// 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: '<p>a<a href="#/"><span>b</span></a></p>',
stepFunction: async editor => {
const a = await clickOnLink(editor);
window.chai.expect(a.isContentEditable).to.be.equal(false);
},
contentAfter: '<p>a<a href="#/"><span contenteditable="true">b</span></a></p>',
}, {
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: '<p>a<a href="#/">b[]</a>c</p>',
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: '<p>a<a href="#/" contenteditable="true" data-oe-zws-empty-inline="">[]\u200B</a>c</p>',
contentAfter: '<p>a[]c</p>',
});
});
it('should keep isolated link after a delete and typing', async () => {
await testEditor(BasicEditor, {
contentBefore: '<p>a<a href="#/">b[]</a>c</p>',
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: '<p>a<a href="#/">123[]</a>c</p>',
});
});
});
describe('existing link', () => {
it('should parse correctly a span inside a Link', async () => {
await testEditor(BasicEditor, {
@@ -472,32 +538,6 @@ describe('Link', () => {
contentAfter: '<p>a<a href="exist">b</a></p><p>c[]d</p>',
});
});
it('should restrict editing to link when clicked', async () => {
const initialContent = '<p>a<a href="#/"><span>b</span></a></p>';
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: '<p>a<a href="#/" contenteditable="true"><span>b</span></a></p>',
});
// 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: '<p>a<a href="#/"><span contenteditable="true">b</span></a></p>',
}, {
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: '<div><p>[]123</p><p><a href="#">abc</a></p></div>',