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 48f7dcd6079..05bcb906913 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 @@ -2988,8 +2988,8 @@ export class OdooEditor extends EventTarget { } } if (sel.rangeCount) { - const closestStartContainer = closestElement(sel.getRangeAt(0).startContainer, '*'); - const selectionStartStyle = getComputedStyle(closestStartContainer); + const closestStartContainer = closestElement(sel.getRangeAt(0).startContainer); + const selectionStartStyle = closestStartContainer && getComputedStyle(closestStartContainer); // queryCommandState does not take stylesheets into account for (const format of ['bold', 'italic', 'underline', 'strikeThrough', 'switchDirection']) { diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/utils.js b/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/utils.js index eeabdf815a4..2b52064733d 100644 --- a/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/utils.js +++ b/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/utils.js @@ -285,22 +285,27 @@ export function getFurthestUneditableParent(node, parentLimit) { return nonEditableElement; } /** - * Returns the closest HTMLElement of the provided Node - * if a 'selector' is provided, Returns the closest HTMLElement that match the selector + * Returns the closest HTMLElement of the provided Node. If the predicate is a + * string, returns the closest HTMLElement that match the predicate selector. If + * the predicate is a function, returns the closest element that matches the + * predicate. Any returned element will be contained within the editable. * * @param {Node} node - * @param {string} [selector=undefined] - * @returns {HTMLElement} + * @param {string | Function} [predicate='*'] + * @returns {HTMLElement|null} */ -export function closestElement(node, selector) { - let element = node; - while (element && element.nodeType !== Node.ELEMENT_NODE) { - element = element.parentNode; +export function closestElement(node, predicate = "*") { + if (!node) return null; + let element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement; + if (typeof predicate === 'function') { + while (element && !predicate(element)) { + element = element.parentElement; + } + } else { + element = element?.closest(predicate); } - if (element && selector) { - element = element.closest(selector); - } - return element && element.querySelector('.odoo-editor-editable') ? null : element; + + return element?.closest('.odoo-editor-editable') && element; } /** diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/powerbox.test.js b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/powerbox.test.js index 2a5960f1ade..7c1748763cf 100644 --- a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/powerbox.test.js +++ b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/powerbox.test.js @@ -64,6 +64,7 @@ describe('Powerbox', () => { describe('class', () => { it('should properly order default commands and categories', async () => { const editable = document.createElement('div'); + editable.classList.add('odoo-editor-editable'); document.body.append(editable); const powerbox = new Powerbox({ categories: [ @@ -97,6 +98,7 @@ describe('Powerbox', () => { }); it('should navigate through commands with arrow keys', async () => { const editable = document.createElement('div'); + editable.classList.add('odoo-editor-editable'); document.body.append(editable); const powerbox = new Powerbox({ categories: [], @@ -122,6 +124,7 @@ describe('Powerbox', () => { }); it('should execute command on press Enter', async () => { const editable = document.createElement('div'); + editable.classList.add('odoo-editor-editable'); document.body.append(editable); const powerbox = new Powerbox({ categories: [], @@ -144,6 +147,7 @@ describe('Powerbox', () => { }); it('should filter commands with `commandFilters`', async () => { const editable = document.createElement('div'); + editable.classList.add('odoo-editor-editable'); document.body.append(editable); const powerbox = new Powerbox({ categories: [], @@ -169,6 +173,7 @@ describe('Powerbox', () => { }); it('should filter commands with `isDisabled`', async () => { const editable = document.createElement('div'); + editable.classList.add('odoo-editor-editable'); document.body.append(editable); let disableCommands = false; const powerbox = new Powerbox({ @@ -198,6 +203,7 @@ describe('Powerbox', () => { }); it('should filter commands with filter text', async () => { const editable = document.createElement('div'); + editable.classList.add('odoo-editor-editable'); document.body.append(editable); editable.append(document.createTextNode('original text')); setSelection(editable.firstChild, 13); @@ -236,6 +242,7 @@ describe('Powerbox', () => { }); it('should close the Powerbox on remove last filter text with Backspace', async () => { const editable = document.createElement('div'); + editable.classList.add('odoo-editor-editable'); document.body.append(editable); editable.append(document.createTextNode('1')); setSelection(editable.firstChild, 13); @@ -277,6 +284,7 @@ describe('Powerbox', () => { }); it('should close the Powerbox on press Escape', async () => { const editable = document.createElement('div'); + editable.classList.add('odoo-editor-editable'); document.body.append(editable); const powerbox = new Powerbox({ categories: [], diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/utils.test.js b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/utils.test.js index 6e22d6d2d74..0f2f53d384f 100644 --- a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/utils.test.js +++ b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/utils.test.js @@ -55,6 +55,7 @@ const cleanTestHtml = () => { const insertTestHtml = innerHtml => { cleanTestHtml(); const container = document.createElement('DIV'); + container.classList.add('odoo-editor-editable'); container.setAttribute('contenteditable', true); container.innerHTML = innerHtml; document.body.appendChild(container);