diff --git a/packages/lexical-list/src/LexicalListNode.ts b/packages/lexical-list/src/LexicalListNode.ts index 680e27aa899..45a87f30d2a 100644 --- a/packages/lexical-list/src/LexicalListNode.ts +++ b/packages/lexical-list/src/LexicalListNode.ts @@ -155,7 +155,7 @@ export class ListNode extends ElementNode { } exportDOM(editor: LexicalEditor): DOMExportOutput { - const {element} = super.exportDOM(editor); + const element = this.createDOM(editor._config, editor); if (element && isHTMLElement(element)) { if (this.__start !== 1) { element.setAttribute('start', String(this.__start)); diff --git a/packages/lexical-playground/__tests__/unit/docSerialization.test.ts b/packages/lexical-playground/__tests__/unit/docSerialization.test.ts index 41e697d30ce..8b9e73a6de1 100644 --- a/packages/lexical-playground/__tests__/unit/docSerialization.test.ts +++ b/packages/lexical-playground/__tests__/unit/docSerialization.test.ts @@ -15,7 +15,13 @@ */ import {serializedDocumentFromEditorState} from '@lexical/file'; -import {$createParagraphNode, $createTextNode, $getRoot} from 'lexical'; +import {$generateHtmlFromNodes, $generateNodesFromDOM} from '@lexical/html'; +import { + $createParagraphNode, + $createTextNode, + $getRoot, + $insertNodes, +} from 'lexical'; import {initializeUnitTest} from 'lexical/src/__tests__/utils'; import {docFromHash, docToHash} from '../../src/utils/docSerialization'; @@ -48,5 +54,172 @@ describe('docSerialization', () => { expect(await docFromHash(await docToHash(doc))).toEqual(doc); }); }); + + describe('Preserve indent serializing HTML <-> Lexical', () => { + it('preserves indentation', async () => { + const {editor} = testEnv; + const parser = new DOMParser(); + const htmlString = `
+ paragraph +
++ quote ++
+ paragraph +
++ quote +`; + const dom = parser.parseFromString(htmlString, 'text/html'); + await editor.update(() => { + const nodes = $generateNodesFromDOM(editor, dom); + $getRoot().select(); + $insertNodes(nodes); + }); + + const expectedEditorState = { + root: { + children: [ + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: 'paragraph', + type: 'text', + version: 1, + }, + ], + direction: 'ltr', + format: '', + indent: 0, + textFormat: 0, + textStyle: '', + type: 'paragraph', + version: 1, + }, + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: 'heading', + type: 'text', + version: 1, + }, + ], + direction: 'ltr', + format: '', + indent: 0, + tag: 'h1', + type: 'heading', + version: 1, + }, + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: 'quote', + type: 'text', + version: 1, + }, + ], + direction: 'ltr', + format: '', + indent: 0, + type: 'quote', + version: 1, + }, + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: 'paragraph', + type: 'text', + version: 1, + }, + ], + direction: 'ltr', + format: '', + indent: 2, + textFormat: 0, + textStyle: '', + type: 'paragraph', + version: 1, + }, + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: 'heading', + type: 'text', + version: 1, + }, + ], + direction: 'ltr', + format: '', + indent: 2, + tag: 'h1', + type: 'heading', + version: 1, + }, + { + children: [ + { + detail: 0, + format: 0, + mode: 'normal', + style: '', + text: 'quote', + type: 'text', + version: 1, + }, + ], + direction: 'ltr', + format: '', + indent: 2, + type: 'quote', + version: 1, + }, + ], + direction: 'ltr', + format: '', + indent: 0, + type: 'root', + version: 1, + }, + }; + + const editorState = editor.getEditorState().toJSON(); + expect(editorState).toEqual(expectedEditorState); + let htmlString2; + await editor.update(() => { + htmlString2 = $generateHtmlFromNodes(editor); + }); + expect(htmlString2).toBe( + '
paragraph
quote
paragraph
quote', + ); + }); + }); }); }); diff --git a/packages/lexical-rich-text/src/index.ts b/packages/lexical-rich-text/src/index.ts index fbf9f53b0ec..bf53a8acdd4 100644 --- a/packages/lexical-rich-text/src/index.ts +++ b/packages/lexical-rich-text/src/index.ts @@ -92,6 +92,7 @@ import { PASTE_COMMAND, REMOVE_TEXT_COMMAND, SELECT_ALL_COMMAND, + setNodeIndentFromDOM, } from 'lexical'; import caretFromPoint from 'shared/caretFromPoint'; import { @@ -415,6 +416,7 @@ function $convertHeadingElement(element: HTMLElement): DOMConversionOutput { ) { node = $createHeadingNode(nodeName); if (element.style !== null) { + setNodeIndentFromDOM(element, node); node.setFormat(element.style.textAlign as ElementFormatType); } } @@ -425,6 +427,7 @@ function $convertBlockquoteElement(element: HTMLElement): DOMConversionOutput { const node = $createQuoteNode(); if (element.style !== null) { node.setFormat(element.style.textAlign as ElementFormatType); + setNodeIndentFromDOM(element, node); } return {node}; } diff --git a/packages/lexical/src/LexicalUtils.ts b/packages/lexical/src/LexicalUtils.ts index 472703d63d5..9039a186c50 100644 --- a/packages/lexical/src/LexicalUtils.ts +++ b/packages/lexical/src/LexicalUtils.ts @@ -1820,3 +1820,12 @@ export function $cloneWithProperties