[FIX] web_editor: remove unwanted styles and tags
Before this commit: When pasting text from outside odoo, it often includes additional tags such as <b> and <P>, along with unwanted styles. After this commit: When pasting text from outside odoo, those extra tags and styles are now removed. task-3378093 closes odoo/odoo#137236 X-original-commit: 5485824654179981cc7562e643123a6decbb02a2 Signed-off-by: David Monjoie (dmo) <dmo@odoo.com>
This commit is contained in:
@@ -174,7 +174,6 @@ export const CLIPBOARD_WHITELISTS = {
|
||||
],
|
||||
attributes: ['class', 'href', 'src', 'target'],
|
||||
styledTags: ['SPAN', 'B', 'STRONG', 'I', 'S', 'U', 'FONT', 'TD'],
|
||||
styles: ['text-decoration', 'font-weight', 'background-color', 'color', 'font-style', 'text-decoration-line', 'font-size']
|
||||
};
|
||||
|
||||
// Commands that don't require a DOM selection but take an argument instead.
|
||||
@@ -3275,7 +3274,12 @@ export class OdooEditor extends EventTarget {
|
||||
* @param {Node} node
|
||||
*/
|
||||
_cleanForPaste(node) {
|
||||
if (!this._isWhitelisted(node) || this._isBlacklisted(node)) {
|
||||
if (
|
||||
!this._isWhitelisted(node) ||
|
||||
this._isBlacklisted(node) ||
|
||||
// Google Docs have their html inside a B tag with custom id.
|
||||
node.id && node.id.startsWith('docs-internal-guid')
|
||||
) {
|
||||
if (!node.matches || node.matches(CLIPBOARD_BLACKLISTS.remove.join(','))) {
|
||||
node.remove();
|
||||
} else {
|
||||
@@ -3312,14 +3316,8 @@ export class OdooEditor extends EventTarget {
|
||||
for (const attribute of [...node.attributes]) {
|
||||
// Keep allowed styles on nodes with allowed tags.
|
||||
if (CLIPBOARD_WHITELISTS.styledTags.includes(node.nodeName) && attribute.name === 'style') {
|
||||
const spanInlineStyles = attribute.value.split(';').map(x => x.trim());
|
||||
const allowedSpanInlineStyles = spanInlineStyles.filter(rawStyle => {
|
||||
return CLIPBOARD_WHITELISTS.styles.includes(rawStyle.split(':')[0].trim());
|
||||
});
|
||||
node.removeAttribute(attribute.name);
|
||||
if (allowedSpanInlineStyles.length > 0) {
|
||||
node.setAttribute(attribute.name, allowedSpanInlineStyles.join(';'));
|
||||
} else if (['SPAN', 'FONT'].includes(node.tagName)) {
|
||||
if (['SPAN', 'FONT'].includes(node.tagName)) {
|
||||
for (const unwrappedNode of unwrapContents(node)) {
|
||||
this._cleanForPaste(unwrappedNode);
|
||||
}
|
||||
@@ -3355,12 +3353,11 @@ export class OdooEditor extends EventTarget {
|
||||
okClass instanceof RegExp ? okClass.test(item) : okClass === item,
|
||||
);
|
||||
} else {
|
||||
const allowedSpanStyles = CLIPBOARD_WHITELISTS.styles.map(s => `span[style*="${s}"]`);
|
||||
return (
|
||||
item.nodeType === Node.TEXT_NODE ||
|
||||
(
|
||||
item.matches &&
|
||||
item.matches([...CLIPBOARD_WHITELISTS.nodes, ...allowedSpanStyles].join(','))
|
||||
item.matches(CLIPBOARD_WHITELISTS.nodes)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
EMAIL_REGEX,
|
||||
PHONE_REGEX,
|
||||
URL_REGEX,
|
||||
unwrapContents,
|
||||
} from './utils.js';
|
||||
|
||||
const NOT_A_NUMBER = /[^\d]/g;
|
||||
@@ -149,13 +150,16 @@ function sanitizeNode(node, root) {
|
||||
node = parent; // The node has been removed, update the reference.
|
||||
} else if (
|
||||
node.nodeName === 'P' && // Note: not sure we should limit to <p>.
|
||||
node.parentElement.nodeName === 'LI' &&
|
||||
isEmptyBlock(node)
|
||||
node.parentElement.nodeName === 'LI'
|
||||
) {
|
||||
// Remove empty paragraphs in <li>.
|
||||
const parent = node.parentElement;
|
||||
const restoreCursor = node.isConnected && preserveCursor(root.ownerDocument);
|
||||
node.remove();
|
||||
if (isEmptyBlock(node)) {
|
||||
node.remove();
|
||||
} else {
|
||||
unwrapContents(node);
|
||||
}
|
||||
fillEmpty(parent);
|
||||
restoreCursor?.(new Map([[node, parent]]));
|
||||
node = parent; // The node has been removed, update the reference.
|
||||
|
||||
@@ -239,7 +239,34 @@ describe('Paste', () => {
|
||||
stepFunction: async editor => {
|
||||
await pasteHtml(editor, 'a<span style="text-decoration: underline">bc</span>d');
|
||||
},
|
||||
contentAfter: '<p>123a<span style="text-decoration: underline">bc</span>d[]</p>',
|
||||
contentAfter: '<p>123abcd[]</p>',
|
||||
});
|
||||
});
|
||||
it('should remove unwanted styles and b tag when pasting from paragraph from gdocs', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<p>[]<br></p>',
|
||||
stepFunction: async editor => {
|
||||
await pasteHtml(editor, `<meta charset="utf-8"><b style="font-weight:normal;" id="docs-internal-guid-ddad60c5-7fff-0a8f-fdd5-c1107201fe26"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:11pt;font-family:Arial,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">test1</span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:11pt;font-family:Arial,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">test2</span></p></b>`);
|
||||
},
|
||||
contentAfter: '<p>test1</p><p>test2[]<br></p>',
|
||||
});
|
||||
});
|
||||
it('should remove unwanted b tag and p tag with unwanted styles when pasting list from gdocs', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<p>[]<br></p>',
|
||||
stepFunction: async editor => {
|
||||
await pasteHtml(editor, '<meta charset="utf-8"><b style="font-weight:normal;" id="docs-internal-guid-5d8bcf85-7fff-ebec-8604-eedd96f2d601"><ul style="margin-top:0;margin-bottom:0;padding-inline-start:48px;"><li dir="ltr" style="list-style-type:disc;font-size:11pt;font-family:Arial,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;" aria-level="1"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;" role="presentation"><span style="font-size:11pt;font-family:Arial,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Google</span></p></li><li dir="ltr" style="list-style-type:disc;font-size:11pt;font-family:Arial,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;" aria-level="1"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;" role="presentation"><span style="font-size:11pt;font-family:Arial,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Test</span></p></li><li dir="ltr" style="list-style-type:disc;font-size:11pt;font-family:Arial,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;" aria-level="1"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;" role="presentation"><span style="font-size:11pt;font-family:Arial,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">test2</span></p></li></ul></b>');
|
||||
},
|
||||
contentAfter: '<ul><li>Google</li><li>Test</li><li>test2</li></ul><p>[]<br></p>',
|
||||
});
|
||||
});
|
||||
it('should remove unwanted styles and keep tags when pasting list from gdoc', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<p>[]<br></p>',
|
||||
stepFunction: async editor => {
|
||||
await pasteHtml(editor, '<meta charset="utf-8"><b style="font-weight:normal;" id="docs-internal-guid-477946a8-7fff-f959-18a4-05014997e161"><ul style="margin-top:0;margin-bottom:0;padding-inline-start:48px;"><li dir="ltr" style="list-style-type:disc;font-size:20pt;font-family:Arial,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;" aria-level="1"><h1 dir="ltr" style="line-height:1.38;margin-top:20pt;margin-bottom:0pt;" role="presentation"><span style="font-size:20pt;font-family:Arial,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Google</span></h1></li><li dir="ltr" style="list-style-type:disc;font-size:20pt;font-family:Arial,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;" aria-level="1"><h1 dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:6pt;" role="presentation"><span style="font-size:20pt;font-family:Arial,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Test</span></h1></li><li dir="ltr" style="list-style-type:disc;font-size:20pt;font-family:Arial,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;" aria-level="1"><h1 dir="ltr" style="line-height:1.38;margin-top:20pt;margin-bottom:0pt;" role="presentation"><span style="font-size:20pt;font-family:Arial,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">test2</span></h1></li></ul></b>');
|
||||
},
|
||||
contentAfter: '<ul><li><h1>Google</h1></li><li><h1>Test</h1></li><li><h1>test2</h1></li></ul><p>[]<br></p>',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -668,7 +695,7 @@ describe('Paste', () => {
|
||||
stepFunction: async editor => {
|
||||
await pasteHtml(editor, complexHtmlData);
|
||||
},
|
||||
contentAfter: '<p>1<b style="font-weight: bolder">23</b> 4[]abcd</p>',
|
||||
contentAfter: '<p>1<b>23</b> 4[]abcd</p>',
|
||||
});
|
||||
});
|
||||
it('should paste a text in a p', async () => {
|
||||
@@ -677,7 +704,7 @@ describe('Paste', () => {
|
||||
stepFunction: async editor => {
|
||||
await pasteHtml(editor, complexHtmlData);
|
||||
},
|
||||
contentAfter: '<p>ab1<b style="font-weight: bolder">23</b> 4[]cd</p>',
|
||||
contentAfter: '<p>ab1<b>23</b> 4[]cd</p>',
|
||||
});
|
||||
});
|
||||
it('should paste a text in a span', async () => {
|
||||
@@ -686,7 +713,7 @@ describe('Paste', () => {
|
||||
stepFunction: async editor => {
|
||||
await pasteHtml(editor, complexHtmlData);
|
||||
},
|
||||
contentAfter: '<p>a<span>b1<b style="font-weight: bolder">23</b> 4[]c</span>d</p>',
|
||||
contentAfter: '<p>a<span>b1<b>23</b> 4[]c</span>d</p>',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -697,7 +724,7 @@ describe('Paste', () => {
|
||||
stepFunction: async editor => {
|
||||
await pasteHtml(editor, complexHtmlData);
|
||||
},
|
||||
contentAfter: '<p>a1<b style="font-weight: bolder">23</b> 4[]d</p>',
|
||||
contentAfter: '<p>a1<b>23</b> 4[]d</p>',
|
||||
});
|
||||
});
|
||||
it('should paste a text in a span', async () => {
|
||||
@@ -706,7 +733,7 @@ describe('Paste', () => {
|
||||
stepFunction: async editor => {
|
||||
await pasteHtml(editor, complexHtmlData);
|
||||
},
|
||||
contentAfter: '<p>a<span>b1<b style="font-weight: bolder">23</b> 4[]e</span>f</p>',
|
||||
contentAfter: '<p>a<span>b1<b>23</b> 4[]e</span>f</p>',
|
||||
});
|
||||
});
|
||||
it('should paste a text when selection across two span', async () => {
|
||||
@@ -715,14 +742,14 @@ describe('Paste', () => {
|
||||
stepFunction: async editor => {
|
||||
await pasteHtml(editor, complexHtmlData);
|
||||
},
|
||||
contentAfter: '<p>a<span>b1<b style="font-weight: bolder">23</b> 4[]e</span>f</p>',
|
||||
contentAfter: '<p>a<span>b1<b>23</b> 4[]e</span>f</p>',
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<p>a<span>b[c</span>- -<span>d]e</span>f</p>',
|
||||
stepFunction: async editor => {
|
||||
await pasteHtml(editor, complexHtmlData);
|
||||
},
|
||||
contentAfter: '<p>a<span>b1<b style="font-weight: bolder">23</b> 4[]e</span>f</p>',
|
||||
contentAfter: '<p>a<span>b1<b>23</b> 4[]e</span>f</p>',
|
||||
});
|
||||
});
|
||||
it('should paste a text when selection across two p', async () => {
|
||||
@@ -731,14 +758,14 @@ describe('Paste', () => {
|
||||
stepFunction: async editor => {
|
||||
await pasteHtml(editor, complexHtmlData);
|
||||
},
|
||||
contentAfter: '<div>a<p>b1<b style="font-weight: bolder">23</b> 4[]e</p>f</div>',
|
||||
contentAfter: '<div>a<p>b1<b>23</b> 4[]e</p>f</div>',
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<div>a<p>b[c</p>- -<p>d]e</p>f</div>',
|
||||
stepFunction: async editor => {
|
||||
await pasteHtml(editor, complexHtmlData);
|
||||
},
|
||||
contentAfter: '<div>a<p>b1<b style="font-weight: bolder">23</b> 4[]e</p>f</div>',
|
||||
contentAfter: '<div>a<p>b1<b>23</b> 4[]e</p>f</div>',
|
||||
});
|
||||
});
|
||||
it('should paste a text when selection leave a span', async () => {
|
||||
@@ -747,14 +774,14 @@ describe('Paste', () => {
|
||||
stepFunction: async editor => {
|
||||
await pasteHtml(editor, complexHtmlData);
|
||||
},
|
||||
contentAfter: '<div>ab<span>c1<b style="font-weight: bolder">23</b> 4[]</span>f</div>',
|
||||
contentAfter: '<div>ab<span>c1<b>23</b> 4[]</span>f</div>',
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<div>a[b<span>c]d</span>ef</div>',
|
||||
stepFunction: async editor => {
|
||||
await pasteHtml(editor, complexHtmlData);
|
||||
},
|
||||
contentAfter: '<div>a1<b style="font-weight: bolder">23</b> 4[]<span>d</span>ef</div>',
|
||||
contentAfter: '<div>a1<b>23</b> 4[]<span>d</span>ef</div>',
|
||||
});
|
||||
});
|
||||
it('should paste a text when selection across two element', async () => {
|
||||
@@ -763,21 +790,21 @@ describe('Paste', () => {
|
||||
stepFunction: async editor => {
|
||||
await pasteHtml(editor, complexHtmlData);
|
||||
},
|
||||
contentAfter: '<div>1a<p>b1<b style="font-weight: bolder">23</b> 4[]<span>e</span>f</p></div>',
|
||||
contentAfter: '<div>1a<p>b1<b>23</b> 4[]<span>e</span>f</p></div>',
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<div>2a<span>b[c</span><p>d]e</p>f</div>',
|
||||
stepFunction: async editor => {
|
||||
await pasteHtml(editor, complexHtmlData);
|
||||
},
|
||||
contentAfter: '<div>2a<span>b1<b style="font-weight: bolder">23</b> 4[]</span>e<br>f</div>',
|
||||
contentAfter: '<div>2a<span>b1<b>23</b> 4[]</span>e<br>f</div>',
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<div>3a<p>b[c</p><p>d]e</p>f</div>',
|
||||
stepFunction: async editor => {
|
||||
await pasteHtml(editor, complexHtmlData);
|
||||
},
|
||||
contentAfter: '<div>3a<p>b1<b style="font-weight: bolder">23</b> 4[]e</p>f</div>',
|
||||
contentAfter: '<div>3a<p>b1<b>23</b> 4[]e</p>f</div>',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -223,24 +223,24 @@ describe('Paste HTML tables', () => {
|
||||
|
||||
|
||||
<tbody><tr>
|
||||
<td class="" style="color: #495057;font-size: 10.0pt;font-style: italic"><span class="" style="color: #495057;font-size: 10.0pt;font-weight: 400;font-style: italic;text-decoration: none">Italic
|
||||
then also </span><span class="" style="color: #495057;font-size: 10.0pt;font-weight: 700;font-style: italic;text-decoration: none">BOLD</span></td>
|
||||
<td class="" style="color: #495057;font-size: 10.0pt;font-style: italic"><s>Italic strike</s></td>
|
||||
<td class="">Italic
|
||||
then also BOLD</td>
|
||||
<td class=""><s>Italic strike</s></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="" style="color: #495057;font-size: 10.0pt;font-weight: 700"><span class="" style="color: #495057;font-size: 10.0pt;font-weight: 700;font-style: normal;text-decoration: none">Just bold </span><span class="" style="color: #495057;font-size: 10.0pt;font-weight: 400;font-style: italic;text-decoration: none">Just Italic</span></td>
|
||||
<td class="" style="color: #495057;font-size: 10.0pt;font-weight: 700;text-decoration: underline">Bold underline</td>
|
||||
<td class="">Just bold Just Italic</td>
|
||||
<td class="">Bold underline</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="" style="color: red;font-size: 10.0pt">Color text</td>
|
||||
<td class="" style="color: red;font-size: 10.0pt;text-decoration: underline"><s>Color strike and underline</s></td>
|
||||
<td class="">Color text</td>
|
||||
<td class=""><s>Color strike and underline</s></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="" style="color: #495057;font-size: 10.0pt;background-color: yellow">Color background</td>
|
||||
<td class="" style="color: red;font-size: 10.0pt;background-color: yellow">Color text on color background</td>
|
||||
<td class="">Color background</td>
|
||||
<td class="">Color text on color background</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="" style="font-size: 14.0pt">14pt MONO TEXT
|
||||
<td class="">14pt MONO TEXT
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table><p>
|
||||
@@ -330,33 +330,33 @@ describe('Paste HTML tables', () => {
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="font-weight:normal;font-style:italic;color:#495057">
|
||||
<span style="font-size:10pt;font-style:italic;color:#495057">Italic then also
|
||||
</span><span style="font-size:10pt;font-weight:bold;font-style:italic;color:#495057">BOLD</span>
|
||||
<td>
|
||||
Italic then also
|
||||
BOLD
|
||||
</td>
|
||||
<td style="font-style:italic;text-decoration:line-through;color:#495057">Italic strike</td>
|
||||
<td>Italic strike</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-weight:bold;color:#495057">
|
||||
<span style="font-size:10pt;font-weight:bold;font-style:normal;color:#495057">Just
|
||||
Bold </span><span style="font-size:10pt;font-style:italic;color:#495057">Just
|
||||
Italic</span>
|
||||
<td>
|
||||
Just
|
||||
Bold Just
|
||||
Italic
|
||||
</td>
|
||||
<td style="font-weight:bold;text-decoration:underline;color:#495057">Bold underline</td>
|
||||
<td>Bold underline</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span style="color:#ff0000">Color text</span></td>
|
||||
<td style="text-decoration:underline line-through;color:#ff0000">Color
|
||||
<td>Color text</td>
|
||||
<td>Color
|
||||
strike and underline</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="background-color:#ffff00;font-weight:normal;color:#495057">Color background
|
||||
<td>Color background
|
||||
</td>
|
||||
<td style="background-color:#ffff00;color:#ff0000">Color
|
||||
<td>Color
|
||||
text on color background</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-size:14pt;font-weight:normal">14pt MONO TEXT</td>
|
||||
<td>14pt MONO TEXT</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table><p>
|
||||
@@ -473,21 +473,21 @@ describe('Paste HTML tables', () => {
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<font style="color: rgb(255, 0, 0)">Color text</font>
|
||||
Color text
|
||||
</td>
|
||||
<td><u><s>
|
||||
<font style="color: rgb(255, 0, 0)">Color strike and underline</font>
|
||||
Color strike and underline
|
||||
</s></u></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="background-color: rgb(255, 255, 0)">Color background</td>
|
||||
<td style="background-color: rgb(255, 255, 0)">
|
||||
<font style="color: rgb(255, 0, 0)">Color text on color background</font>
|
||||
<td>Color background</td>
|
||||
<td>
|
||||
Color text on color background
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<font style="font-size: 14pt">14pt MONO TEXT</font>
|
||||
14pt MONO TEXT
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table><p>
|
||||
|
||||
@@ -2673,14 +2673,6 @@ describe('List', () => {
|
||||
});
|
||||
describe('Complex merges with some containers parsed in list item', () => {
|
||||
it('should treat two blocks in a list item and keep the blocks', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore:
|
||||
'<ul><li><p>abc</p></li><li><p>def[]</p><p>ghi</p></li><li><p>klm</p></li></ul>',
|
||||
stepFunction: deleteForward,
|
||||
// Paragraphs in list items are treated as nonsense.
|
||||
contentAfter:
|
||||
'<ul><li><p>abc</p></li><li><p>def[]ghi</p></li><li><p>klm</p></li></ul>',
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore:
|
||||
'<ul><li><h1>abc</h1></li><li><h2>def[]</h2><h3>ghi</h3></li><li><h4>klm</h4></li></ul>',
|
||||
@@ -2691,27 +2683,7 @@ describe('List', () => {
|
||||
'<ul><li><h1>abc</h1></li><li><h2>def[]ghi</h2></li><li><h4>klm</h4></li></ul>',
|
||||
});
|
||||
});
|
||||
it('should merge a bold list item (checked/unchecked) into a non-formatted list item', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
removeCheckIds: true,
|
||||
contentBefore:
|
||||
'<ul class="o_checklist"><li class="o_checked"><p>abc</p></li><li><p><b>de</b>fg[]</p><p><b>hij</b>klm</p></li><li class="o_checked"><p>nop</p></li></ul>',
|
||||
stepFunction: deleteForward,
|
||||
// Two paragraphs in a checklist item = Two list items.
|
||||
// Paragraphs in list items are treated as nonsense.
|
||||
contentAfter:
|
||||
'<ul class="o_checklist"><li class="o_checked"><p>abc</p></li><li><p><b>de</b>fg[]<b>hij</b>klm</p></li><li class="o_checked"><p>nop</p></li></ul>',
|
||||
});
|
||||
});
|
||||
it('should treat two blocks in a checklist item and keep the blocks', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
removeCheckIds: true,
|
||||
contentBefore:
|
||||
'<ul class="o_checklist"><li class="o_checked"><p>abc</p></li><li><p>def[]</p><p>ghi</p></li><li class="o_checked"><p>klm</p></li></ul>',
|
||||
stepFunction: deleteForward,
|
||||
contentAfter:
|
||||
'<ul class="o_checklist"><li class="o_checked"><p>abc</p></li><li><p>def[]ghi</p></li><li class="o_checked"><p>klm</p></li></ul>',
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
removeCheckIds: true,
|
||||
contentBefore:
|
||||
@@ -2721,16 +2693,6 @@ describe('List', () => {
|
||||
'<ul class="o_checklist"><li class="o_checked"><h1>abc</h1></li><li class="o_checked"><h2>def[]ghi</h2></li><li class="o_checked"><h4>klm</h4></li></ul>',
|
||||
});
|
||||
});
|
||||
it('should merge a bold list item into a non-formatted list item', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
removeCheckIds: true,
|
||||
contentBefore:
|
||||
'<ul><li><p>abc</p></li><li><p><b>de</b>fg[]</p><p><b>hij</b>klm</p></li><li><p>nop</p></li></ul>',
|
||||
stepFunction: deleteForward,
|
||||
contentAfter:
|
||||
'<ul><li><p>abc</p></li><li><p><b>de</b>fg[]<b>hij</b>klm</p></li><li><p>nop</p></li></ul>',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Selection not collapsed', () => {
|
||||
@@ -4842,7 +4804,7 @@ describe('List', () => {
|
||||
await deleteBackward(editor);
|
||||
await deleteBackward(editor);
|
||||
},
|
||||
contentAfter: '<ul><li><p>a[]b</p></li></ul>',
|
||||
contentAfter: '<ul><li>a[]b</li></ul>',
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore:
|
||||
@@ -4851,7 +4813,7 @@ describe('List', () => {
|
||||
await deleteBackward(editor);
|
||||
await deleteBackward(editor);
|
||||
},
|
||||
contentAfter: '<ul><li><p>a[]b</p></li></ul>',
|
||||
contentAfter: '<ul><li>a[]b</li></ul>',
|
||||
});
|
||||
});
|
||||
it('should merge an ordered list item that is in an unordered list item into a non-indented list item', async () => {
|
||||
@@ -4948,7 +4910,7 @@ describe('List', () => {
|
||||
await deleteBackward(editor);
|
||||
await deleteBackward(editor);
|
||||
},
|
||||
contentAfter: '<ol><li><p>a[]b</p></li></ol>',
|
||||
contentAfter: '<ol><li>a[]b</li></ol>',
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore:
|
||||
@@ -4957,7 +4919,7 @@ describe('List', () => {
|
||||
await deleteBackward(editor);
|
||||
await deleteBackward(editor);
|
||||
},
|
||||
contentAfter: '<ol><li><p>a[]b</p></li></ol>',
|
||||
contentAfter: '<ol><li>a[]b</li></ol>',
|
||||
});
|
||||
});
|
||||
it('should merge an unordered list item that is in an ordered list item into a non-indented list item', async () => {
|
||||
@@ -5075,7 +5037,7 @@ describe('List', () => {
|
||||
await deleteBackward(editor);
|
||||
await deleteBackward(editor);
|
||||
},
|
||||
contentAfter: '<ul><li><p>a[]b</p></li></ul>',
|
||||
contentAfter: '<ul><li>a[]b</li></ul>',
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore:
|
||||
@@ -5084,7 +5046,7 @@ describe('List', () => {
|
||||
await deleteBackward(editor);
|
||||
await deleteBackward(editor);
|
||||
},
|
||||
contentAfter: '<ul><li><p>a[]b</p></li></ul>',
|
||||
contentAfter: '<ul><li>a[]b</li></ul>',
|
||||
});
|
||||
});
|
||||
it('should merge an checklist list item that is in an unordered list item into a non-indented list item', async () => {
|
||||
@@ -5198,7 +5160,7 @@ describe('List', () => {
|
||||
},
|
||||
// Paragraphs in list items are kept unless empty
|
||||
contentAfter:
|
||||
'<ul class="o_checklist"><li class="o_checked"><p>a[]b</p></li></ul>',
|
||||
'<ul class="o_checklist"><li class="o_checked">a[]b</li></ul>',
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
removeCheckIds: true,
|
||||
@@ -5210,7 +5172,7 @@ describe('List', () => {
|
||||
},
|
||||
// Paragraphs in list items are kept unless empty
|
||||
contentAfter:
|
||||
'<ul class="o_checklist"><li class="o_checked"><p>a[]b</p></li></ul>',
|
||||
'<ul class="o_checklist"><li class="o_checked">a[]b</li></ul>',
|
||||
});
|
||||
});
|
||||
it('should merge an unordered list item that is in an checklist list item into a non-indented list item', async () => {
|
||||
@@ -5335,14 +5297,6 @@ describe('List', () => {
|
||||
});
|
||||
describe('Complex merges with some containers parsed in list item', () => {
|
||||
it('should treat two blocks in a list item and keep blocks', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore:
|
||||
'<ol><li><p>abc</p></li><li><p>def</p><p>[]ghi</p></li><li><p>klm</p></li></ol>',
|
||||
stepFunction: deleteBackward,
|
||||
// Paragraphs in list items are treated as nonsense.
|
||||
contentAfter:
|
||||
'<ol><li><p>abc</p></li><li><p>def[]ghi</p></li><li><p>klm</p></li></ol>',
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore:
|
||||
'<ol><li><h1>abc</h1></li><li><h2>def</h2><h3>[]ghi</h3></li><li><h4>klm</h4></li></ol>',
|
||||
@@ -5353,26 +5307,7 @@ describe('List', () => {
|
||||
'<ol><li><h1>abc</h1></li><li><h2>def[]ghi</h2></li><li><h4>klm</h4></li></ol>',
|
||||
});
|
||||
});
|
||||
it('should merge a bold list item into a non-formatted list item', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore:
|
||||
'<ol><li><p>abc</p></li><li><p><b>de</b>fg</p><p><b>[]hij</b>klm</p></li><li><p>nop</p></li></ol>',
|
||||
stepFunction: deleteBackward,
|
||||
// Two paragraphs in a list item = Two list items.
|
||||
// Paragraphs in list items are treated as nonsense.
|
||||
contentAfter:
|
||||
'<ol><li><p>abc</p></li><li><p><b>de</b>fg[]<b>hij</b>klm</p></li><li><p>nop</p></li></ol>',
|
||||
});
|
||||
});
|
||||
it('should treat two blocks in a list item and keep blocks', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore:
|
||||
'<ul><li><p>abc</p></li><li><p>def</p><p>[]ghi</p></li><li><p>klm</p></li></ul>',
|
||||
stepFunction: deleteBackward,
|
||||
// Paragraphs in list items are treated as nonsense.
|
||||
contentAfter:
|
||||
'<ul><li><p>abc</p></li><li><p>def[]ghi</p></li><li><p>klm</p></li></ul>',
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore:
|
||||
'<ul><li><h1>abc</h1></li><li><h2>def</h2><h3>[]ghi</h3></li><li><h4>klm</h4></li></ul>',
|
||||
@@ -5383,27 +5318,7 @@ describe('List', () => {
|
||||
'<ul><li><h1>abc</h1></li><li><h2>def[]ghi</h2></li><li><h4>klm</h4></li></ul>',
|
||||
});
|
||||
});
|
||||
it('should merge a bold list item into a non-formatted list item', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore:
|
||||
'<ul><li><p>abc</p></li><li><p><b>de</b>fg</p><p><b>[]hij</b>klm</p></li><li><p>nop</p></li></ul>',
|
||||
stepFunction: deleteBackward,
|
||||
// Two paragraphs in a list item = Two list items.
|
||||
// Paragraphs in list items are treated as nonsense.
|
||||
contentAfter:
|
||||
'<ul><li><p>abc</p></li><li><p><b>de</b>fg[]<b>hij</b>klm</p></li><li><p>nop</p></li></ul>',
|
||||
});
|
||||
});
|
||||
it('should treat two blocks in a list item and keep blocks', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
removeCheckIds: true,
|
||||
contentBefore:
|
||||
'<ul class="o_checklist"><li class="o_checked"><p>abc</p></li><li class="o_checked"><p>def</p><p>[]ghi</p></li><li class="o_checked"><p>klm</p></li></ul>',
|
||||
stepFunction: deleteBackward,
|
||||
// Paragraphs in list items are treated as nonsense.
|
||||
contentAfter:
|
||||
'<ul class="o_checklist"><li class="o_checked"><p>abc</p></li><li class="o_checked"><p>def[]ghi</p></li><li class="o_checked"><p>klm</p></li></ul>',
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
removeCheckIds: true,
|
||||
contentBefore:
|
||||
@@ -5415,18 +5330,6 @@ describe('List', () => {
|
||||
'<ul class="o_checklist"><li class="o_checked"><h1>abc</h1></li><li class="o_checked"><h2>def[]ghi</h2></li><li class="o_checked"><h4>klm</h4></li></ul>',
|
||||
});
|
||||
});
|
||||
it('should merge a bold list item into a non-formatted list item', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
removeCheckIds: true,
|
||||
contentBefore:
|
||||
'<ul class="o_checklist"><li class="o_checked"><p>abc</p></li><li class="o_checked"><p><b>de</b>fg</p><p><b>[]hij</b>klm</p></li><li class="o_checked"><p>nop</p></li></ul>',
|
||||
stepFunction: deleteBackward,
|
||||
// Two paragraphs in a list item = Two list items.
|
||||
// Paragraphs in list items are treated as nonsense.
|
||||
contentAfter:
|
||||
'<ul class="o_checklist"><li class="o_checked"><p>abc</p></li><li class="o_checked"><p><b>de</b>fg[]<b>hij</b>klm</p></li><li class="o_checked"><p>nop</p></li></ul>',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Selection not collapsed', () => {
|
||||
|
||||
Reference in New Issue
Block a user