diff --git a/packages/quill/src/core/editor.ts b/packages/quill/src/core/editor.ts index b6391a7cd4..9b08ba1dbe 100644 --- a/packages/quill/src/core/editor.ts +++ b/packages/quill/src/core/editor.ts @@ -337,10 +337,17 @@ function convertListHTML( } const [{ child, offset, length, indent, type }, ...rest] = items; const [tag, attribute] = getListType(type); + const classes = + child.domNode instanceof Element + ? Array.from(child.domNode.classList) + .filter((className) => !className.startsWith('ql-indent-')) + .join(' ') + : ''; + const classAttribute = classes ? ` class="${escapeText(classes)}"` : ''; if (indent > lastIndent) { types.push(type); if (indent === lastIndent + 1) { - return `<${tag}>${convertHTML( + return `<${tag}>${convertHTML( child, offset, length, @@ -350,7 +357,7 @@ function convertListHTML( } const previousType = types[types.length - 1]; if (indent === lastIndent && type === previousType) { - return `${convertHTML( + return `${convertHTML( child, offset, length, diff --git a/packages/quill/test/unit/core/editor.spec.ts b/packages/quill/test/unit/core/editor.spec.ts index 2d47a1dec9..b0abf8a240 100644 --- a/packages/quill/test/unit/core/editor.spec.ts +++ b/packages/quill/test/unit/core/editor.spec.ts @@ -17,6 +17,7 @@ import { FontClass } from '../../../src/formats/font.js'; import Header from '../../../src/formats/header.js'; import Italic from '../../../src/formats/italic.js'; import { AlignClass } from '../../../src/formats/align.js'; +import { DirectionClass } from '../../../src/formats/direction.js'; import Video from '../../../src/formats/video.js'; import Strike from '../../../src/formats/strike.js'; import Underline from '../../../src/formats/underline.js'; @@ -47,6 +48,7 @@ const createEditor = (htmlOrContents: string | Delta) => { Header, Italic, AlignClass, + DirectionClass, Video, Strike, Underline, @@ -1242,6 +1244,122 @@ describe('Editor', () => { expect(editor.getHTML(0, 5)).toEqual('
  • Test
'); }); + test('aligned bullet list', () => { + const editor = createEditor( + ` +
    +
  1. One
  2. +
  3. Two
  4. +
  5. Three
  6. +
+ `, + ); + expect(editor.getHTML(0, 14)).toEqualHTML(` +
    +
  • One
  • +
  • Two
  • +
  • Three
  • +
+ `); + }); + + test('aligned ordered list', () => { + const editor = createEditor( + ` +
    +
  1. One
  2. +
  3. Two
  4. +
+ `, + ); + expect(editor.getHTML(0, 8)).toEqualHTML(` +
    +
  1. One
  2. +
  3. Two
  4. +
+ `); + }); + + test('aligned checklist', () => { + const editor = createEditor( + ` +
    +
  1. One
  2. +
  3. Two
  4. +
+ `, + ); + expect(editor.getHTML(0, 8)).toEqualHTML(` +
    +
  • One
  • +
+
    +
  • Two
  • +
+ `); + }); + + test('aligned nested list with skipped indent', () => { + const editor = createEditor( + ` +
    +
  1. One
  2. +
  3. Two
  4. +
+ `, + ); + expect(editor.getHTML(0, 8)).toEqualHTML(` +
    +
  • One +
      +
    • +
        +
      • Two
      • +
      +
    • +
    +
  • +
+ `); + }); + + test('indent-only list item', () => { + const editor = createEditor( + ` +
    +
  1. One
  2. +
+ `, + ); + expect(editor.getHTML(0, 4)).toEqualHTML(` +
    +
  • +
      +
    • One
    • +
    +
  • +
+ `); + }); + + test('list item direction and custom classes', () => { + const editor = createEditor( + '
  1. One
', + ); + editor.scroll.domNode.querySelector('li')!.classList.add('custom'); + expect(editor.getHTML(0, 4)).toEqualHTML(` +
  • One
+ `); + }); + + test('escape list item class attribute', () => { + const editor = createEditor('
  1. One
'); + editor.scroll.domNode.querySelector('li')!.classList.add('custom"&<>'); + expect(editor.getHTML(0, 4)).toEqual( + '
  • One
', + ); + }); + test('across lines', () => { const editor = createEditor( '

Header

Text

Quote
',