facebook / lexical

Lexical is an extensible text editor framework that provides excellent reliability, accessibility and performance.
https://lexical.dev
MIT License
20.03k stars 1.71k forks source link

Bug: $generateNodesFromDOM creates empty list items from whitespace between actual list items #2807

Open knpwrs opened 2 years ago

knpwrs commented 2 years ago

Lexical version: 0.3.8

Steps To Reproduce

  1. Use the following code to initialize Lexical:
  editorState: (editor) => {
    const parser = new DOMParser();
    const dom = parser.parseFromString(
      "<ul><li>foo</li> <li>bar</li> <li>banana</li></ul>",
      "text/html"
    );
    const nodes = $generateNodesFromDOM(editor, dom);
    const rootNode = $getRoot();
    rootNode.append(...nodes);
  }
  1. Observe that the empty spaces between the <li> elements get converted to empty list item nodes:
image
  1. If you delete the extra spaces, the list will have the correct number of list items.

Link to code example: https://codesandbox.io/s/lexical-list-parsing-bug-ktssb4?file=%2Fsrc%2FEditor.js%3A1849-2160

The current behavior

Empty list items are created from whitespace text nodes between list item nodes.

The expected behavior

The empty whitespace between list items should be ignored.

knpwrs commented 2 years ago

This appears to be at least somewhat intentional: https://github.com/facebook/lexical/blob/b7887a02780e40640cf0e7582212f9c29730c14d/packages/lexical-list/src/LexicalListNode.ts#L251

https://github.com/facebook/lexical/pull/2509

marwan38 commented 2 years ago

I came here to post this issue. If it is intentional then this PR will get closed anyways, but, worth sharing my use-case. Unlike OP, I am getting an error thrown.

  editorState: (editor) => {
    const parser = new DOMParser();
    const dom = parser.parseFromString(
      "<p>foo</p> <p>bar</p>", // NOTE THE SPACE BETWEEN THE P TAGS
      "text/html"
    );
    const nodes = $generateNodesFromDOM(editor, dom);
    const rootNode = $getRoot();
    rootNode.append(...nodes); // ERROR
  }

Error + stack trace

Error: rootNode.append: Only element or decorator nodes can be appended to the root node
    at RootNode.append (**/node_modules/lexical/Lexical.dev.js:7667:17)

That empty space is creating empty nodes in between the paragraph ones which is why they then can't be inserted into the root node.

SzDani3l commented 1 year ago

I came here to post this issue. If it is intentional then this PR will get closed anyways, but, worth sharing my use-case. Unlike OP, I am getting an error thrown.

  editorState: (editor) => {
    const parser = new DOMParser();
    const dom = parser.parseFromString(
      "<p>foo</p> <p>bar</p>", // NOTE THE SPACE BETWEEN THE P TAGS
      "text/html"
    );
    const nodes = $generateNodesFromDOM(editor, dom);
    const rootNode = $getRoot();
    rootNode.append(...nodes); // ERROR
  }

Error + stack trace

Error: rootNode.append: Only element or decorator nodes can be appended to the root node
    at RootNode.append (**/node_modules/lexical/Lexical.dev.js:7667:17)

That empty space is creating empty nodes in between the paragraph ones which is why they then can't be inserted into the root node.

I had the same issue, for now I fixed it by skipping the invalid nodes:

import { $getRoot, $isDecoratorNode, $isElementNode } from 'lexical'
[...]
const nodes = $generateNodesFromDOM(editor, dom)
const root = $getRoot()
nodes.forEach((node, i) => {
    if ($isElementNode(node) || $isDecoratorNode(node)) {
        root.append(node)
    }
})