[FIX] web_editor: do not toggle list with class nav-item
When inserting a list to a mega menu, the closest `LI` is a nav-item within the navbar. We aim to prevent the toggling of this list. This commit ensures that lists with class nav-item in the navbar are not toggled. task-3609500 X-original-commit: a64a37aeeb5c6f6eb838cf5b9c4221e8522721cf Part-of: odoo/odoo#153211
This commit is contained in:
@@ -519,7 +519,8 @@ export const editorCommands = {
|
||||
cli.tagName == 'LI' &&
|
||||
!li.has(cli) &&
|
||||
!cli.classList.contains('oe-nested') &&
|
||||
cli.isContentEditable
|
||||
cli.isContentEditable &&
|
||||
!cli.classList.contains('nav-item')
|
||||
) {
|
||||
li.add(cli);
|
||||
}
|
||||
@@ -546,10 +547,14 @@ export const editorCommands = {
|
||||
if (node.nodeType === Node.TEXT_NODE && isWhitespace(node) && closestElement(node).isContentEditable) {
|
||||
node.remove();
|
||||
} else {
|
||||
// Ensure nav-item lists are excluded from toggling
|
||||
const isNavItemList = node => node.nodeName === 'LI' && node.classList.contains('nav-item');
|
||||
let block = closestBlock(node);
|
||||
if (!['OL', 'UL'].includes(block.tagName) && block.isContentEditable) {
|
||||
block = block.closest('li') || block;
|
||||
const ublock = block.closest('ol, ul');
|
||||
block = isNavItemList(block) ? node : block;
|
||||
if (!['OL', 'UL'].includes(block.tagName) && (block.isContentEditable || block.nodeType === Node.TEXT_NODE)) {
|
||||
const closestLi = closestElement(block, 'li');
|
||||
block = closestLi && !isNavItemList(closestLi) ? closestLi : block;
|
||||
const ublock = block.nodeName === 'LI' && block.closest('ol, ul');
|
||||
ublock && getListMode(ublock) == mode ? li.add(block) : blocks.add(block);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,10 @@ HTMLLIElement.prototype.oShiftTab = function () {
|
||||
}
|
||||
|
||||
const restoreCursor = preserveCursor(this.ownerDocument);
|
||||
if (li.parentNode.parentNode.tagName === 'LI') {
|
||||
if (
|
||||
li.parentNode.parentNode.tagName === 'LI' &&
|
||||
!li.parentNode.parentNode.classList.contains('nav-item')
|
||||
) {
|
||||
const ul = li.parentNode;
|
||||
const shouldRemoveParentLi = !li.previousElementSibling && !ul.previousElementSibling;
|
||||
const toremove = shouldRemoveParentLi ? ul.parentNode : null;
|
||||
|
||||
@@ -8,19 +8,28 @@ import {
|
||||
toggleClass,
|
||||
insertListAfter,
|
||||
getAdjacents,
|
||||
closestElement,
|
||||
} from '../utils/utils.js';
|
||||
|
||||
Text.prototype.oToggleList = function (offset, mode) {
|
||||
this.parentElement.oToggleList(childNodeIndex(this), mode);
|
||||
// Create a new list if textNode is inside a nav-item list
|
||||
if (closestElement(this, 'li').classList.contains('nav-item')) {
|
||||
const restoreCursor = preserveCursor(this.ownerDocument);
|
||||
insertListAfter(this, mode, [this]);
|
||||
restoreCursor();
|
||||
} else {
|
||||
this.parentElement.oToggleList(childNodeIndex(this), mode);
|
||||
}
|
||||
};
|
||||
|
||||
HTMLElement.prototype.oToggleList = function (offset, mode = 'UL') {
|
||||
if (!isBlock(this)) {
|
||||
return this.parentElement.oToggleList(childNodeIndex(this));
|
||||
}
|
||||
const inLI = this.closest('li');
|
||||
if (inLI) {
|
||||
return inLI.oToggleList(0, mode);
|
||||
const closestLi = this.closest('li');
|
||||
// Do not toggle nav-item list as they don't behave like regular list items
|
||||
if (closestLi && !closestLi.classList.contains('nav-item')) {
|
||||
return closestLi.oToggleList(0, mode);
|
||||
}
|
||||
const restoreCursor = preserveCursor(this.ownerDocument);
|
||||
if (this.oid === 'root') {
|
||||
|
||||
@@ -114,6 +114,20 @@ describe('List', () => {
|
||||
`),
|
||||
});
|
||||
});
|
||||
it('should create a new unordered list if current node is inside a nav-item list', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<ul><li class="nav-item">a[]b</li></ul>',
|
||||
stepFunction: toggleUnorderedList,
|
||||
contentAfter: '<ul><li class="nav-item"><ul><li>a[]b</li></ul></li></ul>',
|
||||
});
|
||||
});
|
||||
it('should create a new unordered list if closestBlock is inside a nav-item list', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<ul><li class="nav-item"><div><p>a[]b</p></div></li></ul>',
|
||||
stepFunction: toggleUnorderedList,
|
||||
contentAfter: '<ul><li class="nav-item"><div><ul><li>a[]b</li></ul></div></li></ul>',
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Remove', () => {
|
||||
it('should turn an empty list into a paragraph', async () => {
|
||||
@@ -355,6 +369,20 @@ describe('List', () => {
|
||||
`),
|
||||
});
|
||||
});
|
||||
it('should create a new ordered list if current node is inside a nav-item list', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<ul><li class="nav-item">a[]b</li></ul>',
|
||||
stepFunction: toggleOrderedList,
|
||||
contentAfter: '<ul><li class="nav-item"><ol><li>a[]b</li></ol></li></ul>',
|
||||
});
|
||||
});
|
||||
it('should create a new ordered list if closestBlock is inside a nav-item list', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<ul><li class="nav-item"><div><h1>a[]b</h1></div></li></ul>',
|
||||
stepFunction: toggleOrderedList,
|
||||
contentAfter: '<ul><li class="nav-item"><div><ol><li><h1>a[]b</h1></li></ol></div></li></ul>',
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Remove', () => {
|
||||
it('should turn an empty list into a paragraph', async () => {
|
||||
@@ -674,6 +702,22 @@ describe('List', () => {
|
||||
`),
|
||||
});
|
||||
});
|
||||
it('should create a new checked list if current node is inside a nav-item list', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
removeCheckIds: true,
|
||||
contentBefore: '<ul><li class="nav-item">a[]b</li></ul>',
|
||||
stepFunction: toggleCheckList,
|
||||
contentAfter: '<ul><li class="nav-item"><ul class="o_checklist"><li>a[]b</li></ul></li></ul>',
|
||||
});
|
||||
});
|
||||
it('should create a new checked list if closestBlock is inside a nav-item list', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
removeCheckIds: true,
|
||||
contentBefore: '<ul><li class="nav-item"><div><p>a[]b</p></div></li></ul>',
|
||||
stepFunction: toggleCheckList,
|
||||
contentAfter: '<ul><li class="nav-item"><div><ul class="o_checklist"><li>a[]b</li></ul></div></li></ul>',
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Remove', () => {
|
||||
it('should turn an empty list into a paragraph', async () => {
|
||||
@@ -7493,6 +7537,18 @@ describe('List', () => {
|
||||
`),
|
||||
});
|
||||
});
|
||||
it('should not indent a nav-item list', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<ul><li class="nav-item">a[]</li></ul>',
|
||||
stepFunction: indentList,
|
||||
contentAfter: '<ul><li class="nav-item">a[]</li></ul>',
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<ul><li class="nav-item"><div><p>a[]</p></div></li></ul>',
|
||||
stepFunction: indentList,
|
||||
contentAfter: '<ul><li class="nav-item"><div><p>a[]</p></div></li></ul>',
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('with selection', () => {
|
||||
it('should indent the first element of a list', async () => {
|
||||
@@ -8192,6 +8248,136 @@ describe('List', () => {
|
||||
`),
|
||||
});
|
||||
});
|
||||
it('should outdent a list inside a nav-item list', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: unformat(`
|
||||
<ul>
|
||||
<li class="nav-item">
|
||||
<ul>
|
||||
<li>a[]b</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
`),
|
||||
stepFunction: outdentList,
|
||||
contentAfter: unformat(`
|
||||
<ul>
|
||||
<li class="nav-item">
|
||||
<p>a[]b</p>
|
||||
</li>
|
||||
</ul>
|
||||
`),
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: unformat(`
|
||||
<ul>
|
||||
<li class="nav-item">
|
||||
<ol>
|
||||
<li>a[]b</li>
|
||||
</ol>
|
||||
</li>
|
||||
</ul>
|
||||
`),
|
||||
stepFunction: outdentList,
|
||||
contentAfter: unformat(`
|
||||
<ul>
|
||||
<li class="nav-item">
|
||||
<p>a[]b</p>
|
||||
</li>
|
||||
</ul>
|
||||
`),
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: unformat(`
|
||||
<ul>
|
||||
<li class="nav-item">
|
||||
<ul class="o_checklist">
|
||||
<li>a[]b</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
`),
|
||||
stepFunction: outdentList,
|
||||
contentAfter: unformat(`
|
||||
<ul>
|
||||
<li class="nav-item">
|
||||
<p>a[]b</p>
|
||||
</li>
|
||||
</ul>
|
||||
`),
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: unformat(`
|
||||
<ul>
|
||||
<li class="nav-item">
|
||||
<div>
|
||||
<ul>
|
||||
<li>a[]b</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
`),
|
||||
stepFunction: outdentList,
|
||||
contentAfter: unformat(`
|
||||
<ul>
|
||||
<li class="nav-item">
|
||||
<div>
|
||||
<p>a[]b</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
`),
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: unformat(`
|
||||
<ul>
|
||||
<li class="nav-item">
|
||||
<div>
|
||||
<ol>
|
||||
<li>
|
||||
<h1>a[]b</h1>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
`),
|
||||
stepFunction: outdentList,
|
||||
contentAfter: unformat(`
|
||||
<ul>
|
||||
<li class="nav-item">
|
||||
<div>
|
||||
<h1>a[]b</h1>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
`),
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: unformat(`
|
||||
<ul>
|
||||
<li class="nav-item">
|
||||
<div>
|
||||
<ul class="o_checklist">
|
||||
<li>a[]b</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
`),
|
||||
stepFunction: outdentList,
|
||||
contentAfter: unformat(`
|
||||
<ul>
|
||||
<li class="nav-item">
|
||||
<div>
|
||||
<p>a[]b</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
`),
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('with selection', () => {
|
||||
it('should outdent the middle element of a list', async () => {
|
||||
|
||||
Reference in New Issue
Block a user