facebook / lexical

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

Bug: Pasting nested lists changes order of text #4327

Open goldblatt opened 1 year ago

goldblatt commented 1 year ago

When I paste the following text from notion

  1. ordered list item paragraph 1
    • bullet list paragraph 2

with the metadata

<meta charset='utf-8'><ol>
<li>
<p>ordered list item</p>
<p>paragraph 1</p>
<ul>
<li>bullet list **</li>
</ul>
<p>paragraph 2</p>
</li>
</ol>

The text in the editor ends up garbled like

ordered list itemparagraph 1paragraph 2
bullet list **

Lexical version: 0.9.1

Steps To Reproduce

  1. Type the nested/mixed list and non list text above into notion (i also made a public notion https://rattle-eustoma-a47.notion.site/Example-Broken-Notion-50604f83a7f548499e75a34cf6e519e0)
  2. Copy it into the lexical editor

Link to code example: This happens in the playground

The current behavior

Nodes are out of order

The expected behavior

Nodes in order or more similar to the original text

acywatson commented 1 year ago

Thanks for reporting this. That looks like semantically valid HTML, so we should be able to handle it. Will look into this one.

goldblatt commented 1 year ago

FWIW i did a little hacking with _htmlConversions and edited https://github.com/facebook/lexical/blob/main/packages/lexical-list/src/utils.ts#L201

to be something like

function normalizeChildren(nodes: Array<LexicalNode>): Array<ListItemNode> {
  const normalizedListItems: Array<ListItemNode> = [];
  for (let i = 0; i < nodes.length; i++) {
    const node = nodes[i];
    if ($isListItemNode(node)) {
      const children = node.getChildren();
      if (children.length > 0) {
        normalizedListItems.push(...normalizeChildren(children));
      }
    } else {
      normalizedListItems.push(wrapInListItem(node));
    }
  }
  return normalizedListItems;
}

I think this works for my use cases, but there was a lot of surfaces to test for copy paste. It also seems weird to ignore the list item nodes but i think they get processed separately? idk in general i felt like I was missing some information so hopefully you'll have an easier time on your side!

acywatson commented 1 year ago

Awesome, very helpful!

By the way, there's an entire system in place for overriding copy and paste behavior so you don't have to patch internal properties:

https://lexical.dev/docs/concepts/serialization#html---lexical

goldblatt commented 1 year ago

ooo an example with copy/paste might be helpful there. I had seen that page, but wasn't totally sure how to combine the concepts since I wanted to mostly keep the copy/paste functionality y'all had (since it's I think better than what I could write on my own 😄)

EDIT: I see the instructions, classic friday brain-- LexicalNode.importDOM()

goldblatt commented 1 year ago

also my solution does not work in all cases sadly!