Mentioning at the beginning of a line and before other text causes a JavaScript error
Description
Same as with the issue #1232, trying to create a mention at the beginning of a line and before other text.
Steps
- Go to Mention plugin page
- Type a Space before "💬 Mention" or at the start of any of the paragraphs, move the caret to the beginning of the line and type '@'.
- The Mention combobox does not display, but the mention input is created and the caret is inside of it.
- Press 'Enter' or 'Tab'
- The following error occurs
Cannot find a descendant at path [0,0,0] in node: {"children":[{"type":"h2","children":[{"text":""},{"text":"@"},{"text":" 💬 Mention"}]},{"type":"p","children":[{"text":"This example shows how you might implement a simple @-mentions feature that lets users autocomplete mentioning a user by their username. Which, in this case means Star Wars characters. The mentions are rendered as void inline elements inside the document."}]},{"type":"p","children":[{"text":"Try mentioning characters, like "},{"type":"mention","children":[{"text":""}],"value":"R2-D2"},{"text":" or "},{"type":"mention","children":[{"text":""}],"value":"Mace Windu"},{"text":""}]}],"operations":[{"type":"set_selection","properties":{"anchor":{"path":[0,1,0],"offset":0},"focus":{"path":[0,1,0],"offset":0}},"newProperties":{"anchor":{"path":[0,0,0],"offset":0},"focus":{"path":[0,0,0],"offset":0}}},{"type":"insert_text","path":[0,1,0],"offset":0,"text":"@"},{"type":"move_node","path":[0,1,0],"newPath":[0,2]},{"type":"remove_node","path":[0,1],"node":{"type":"mention_input","children":[],"trigger":"@"}}],"selection":{"anchor":{"path":[0,0,0],"offset":0},"focus":{"path":[0,0,0],"offset":0}},"marks":null,"id":"main","prevSelection":{"anchor":{"path":[0,1,0],"offset":0},"focus":{"path":[0,1,0],"offset":0}},"currentKeyboardEvent":null,"key":0.7095012255939719,"plugins":[{"key":"react","type":"react","options":{},"inject":{},"editor":{}},{"key":"history","type":"history","options":{},"inject":{},"editor":{}},{"key":"event-editor","handlers":{},"type":"event-editor","options":{},"inject":{},"editor":{}},{"key":"inline-void","type":"inline-void","options":{},"inject":{},"editor":{}},{"key":"insertData","type":"insertData","options":{},"inject":{},"editor":{}},{"key":"selection","handlers":{},"type":"selection","options":{},"inject":{},"editor":{}},{"key":"deserializeHtml","editor":{"insertData":{"format":"text/html"}},"type":"deserializeHtml","options":{},"inject":{}},{"key":"deserializeAst","editor":{"insertData":{"format":"application/x-slate-fragment"}},"type":"deserializeAst","options":{},"inject":{}},{"key":"blockquote","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"BLOCKQUOTE"}]},"handlers":{},"options":{"hotkey":"mod+shift+."},"type":"blockquote","inject":{},"editor":{}},{"component":null,"key":"code_block","inject":{"pluginsByKey":{"deserializeHtml":{"editor":{"insertData":{}}}}},"isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"PRE"},{"validNodeName":"P","validStyle":{"fontFamily":"Consolas"}}]},"handlers":{},"options":{"hotkey":["mod+opt+8","mod+shift+8"],"syntax":true,"syntaxPopularFirst":false},"plugins":[{"key":"code_line","isElement":true,"type":"code_line","options":{},"inject":{},"editor":{}},{"key":"code_syntax","isLeaf":true,"type":"code_syntax","options":{},"inject":{},"editor":{}}],"type":"code_block","editor":{}},{"key":"code_line","isElement":true,"type":"code_line","options":{},"inject":{},"editor":{}},{"key":"code_syntax","isLeaf":true,"type":"code_syntax","options":{},"inject":{},"editor":{}},{"key":"heading","plugins":[{"key":"h1","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H1"}]},"handlers":{},"options":{"hotkey":["mod+opt+1","mod+shift+1"]},"type":"h1","inject":{},"editor":{}},{"key":"h2","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H2"}]},"handlers":{},"options":{"hotkey":["mod+opt+2","mod+shift+2"]},"type":"h2","inject":{},"editor":{}},{"key":"h3","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H3"}]},"handlers":{},"options":{"hotkey":["mod+opt+3","mod+shift+3"]},"type":"h3","inject":{},"editor":{}},{"key":"h4","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H4"}]},"handlers":{},"options":{},"type":"h4","inject":{},"editor":{}},{"key":"h5","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H5"}]},"handlers":{},"options":{},"type":"h5","inject":{},"editor":{}},{"key":"h6","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H6"}]},"handlers":{},"options":{},"type":"h6","inject":{},"editor":{}}],"options":{"levels":6},"type":"heading","inject":{},"editor":{}},{"key":"h1","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H1"}]},"handlers":{},"options":{"hotkey":["mod+opt+1","mod+shift+1"]},"type":"h1","inject":{},"editor":{}},{"key":"h2","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H2"}]},"handlers":{},"options":{"hotkey":["mod+opt+2","mod+shift+2"]},"type":"h2","inject":{},"editor":{}},{"key":"h3","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H3"}]},"handlers":{},"options":{"hotkey":["mod+opt+3","mod+shift+3"]},"type":"h3","inject":{},"editor":{}},{"key":"h4","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H4"}]},"handlers":{},"options":{},"type":"h4","inject":{},"editor":{}},{"key":"h5","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H5"}]},"handlers":{},"options":{},"type":"h5","inject":{},"editor":{}},{"key":"h6","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H6"}]},"handlers":{},"options":{},"type":"h6","inject":{},"editor":{}},{"key":"p","isElement":true,"handlers":{},"options":{"hotkey":["mod+opt+0","mod+shift+0"]},"deserializeHtml":{"rules":[{"validNodeName":"P"}]},"type":"p","inject":{},"editor":{}},{"key":"bold","isLeaf":true,"deserializeHtml":{"rules":[{"validNodeName":["STRONG","B"]},{"validStyle":{"fontWeight":["600","700","bold"]}}]},"handlers":{},"options":{"hotkey":"mod+b"},"type":"bold","inject":{},"editor":{}},{"key":"code","isLeaf":true,"deserializeHtml":{"rules":[{"validNodeName":["CODE"]},{"validStyle":{"wordWrap":"break-word"}},{"validStyle":{"fontFamily":"Consolas"}}]},"handlers":{},"options":{"hotkey":"mod+e"},"type":"code","inject":{},"editor":{}},{"key":"italic","isLeaf":true,"handlers":{},"options":{"hotkey":"mod+i"},"deserializeHtml":{"rules":[{"validNodeName":["EM","I"]},{"validStyle":{"fontStyle":"italic"}}]},"type":"italic","inject":{},"editor":{}},{"key":"strikethrough","isLeaf":true,"handlers":{},"options":{"hotkey":"mod+shift+x"},"deserializeHtml":{"rules":[{"validNodeName":["S","DEL","STRIKE"]},{"validStyle":{"textDecoration":"line-through"}}]},"type":"strikethrough","inject":{},"editor":{}},{"key":"subscript","isLeaf":true,"handlers":{},"options":{"hotkey":"mod+,","clear":"superscript"},"deserializeHtml":{"rules":[{"validNodeName":["SUB"]},{"validStyle":{"verticalAlign":"sub"}}]},"type":"subscript","inject":{},"editor":{}},{"key":"superscript","isLeaf":true,"handlers":{},"options":{"hotkey":"mod+.","clear":"subscript"},"deserializeHtml":{"rules":[{"validNodeName":["SUP"]},{"validStyle":{"verticalAlign":"super"}}]},"type":"superscript","inject":{},"editor":{}},{"key":"underline","isLeaf":true,"handlers":{},"options":{"hotkey":"mod+u"},"deserializeHtml":{"rules":[{"validNodeName":["U"]},{"validStyle":{"textDecoration":["underline"]}}]},"type":"underline","inject":{},"editor":{}},{"key":"combobox","handlers":{},"type":"combobox","options":{},"inject":{},"editor":{}},{"key":"mention","options":{"id":"mention","trigger":"@"},"isElement":true,"isInline":true,"isVoid":true,"handlers":{},"plugins":[{"key":"mention_input","isElement":true,"isInline":true,"type":"mention_input","options":{},"inject":{},"editor":{}}],"type":"mention","inject":{},"editor":{}},{"key":"mention_input","isElement":true,"isInline":true,"type":"mention_input","options":{},"inject":{},"editor":{}},{"key":"#","options":{"id":"#","trigger":"#","inputCreation":{"key":"creationId","value":"main"}},"isElement":true,"isInline":true,"isVoid":true,"handlers":{},"plugins":[{"key":"mention_input","isElement":true,"isInline":true,"type":"mention_input","options":{},"inject":{},"editor":{}}],"type":"#","inject":{},"editor":{}},{"key":"/","options":{"id":"/","trigger":"/"},"isElement":true,"isInline":true,"isVoid":true,"handlers":{},"plugins":[{"key":"mention_input","isElement":true,"isInline":true,"type":"mention_input","options":{},"inject":{},"editor":{}}],"type":"/","inject":{},"editor":{}}],"pluginsByKey":{"react":{"key":"react","type":"react","options":{},"inject":{},"editor":{}},"history":{"key":"history","type":"history","options":{},"inject":{},"editor":{}},"event-editor":{"key":"event-editor","handlers":{},"type":"event-editor","options":{},"inject":{},"editor":{}},"inline-void":{"key":"inline-void","type":"inline-void","options":{},"inject":{},"editor":{}},"insertData":{"key":"insertData","type":"insertData","options":{},"inject":{},"editor":{}},"selection":{"key":"selection","handlers":{},"type":"selection","options":{},"inject":{},"editor":{}},"deserializeHtml":{"key":"deserializeHtml","editor":{"insertData":{"format":"text/html"}},"type":"deserializeHtml","options":{},"inject":{}},"deserializeAst":{"key":"deserializeAst","editor":{"insertData":{"format":"application/x-slate-fragment"}},"type":"deserializeAst","options":{},"inject":{}},"blockquote":{"key":"blockquote","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"BLOCKQUOTE"}]},"handlers":{},"options":{"hotkey":"mod+shift+."},"type":"blockquote","inject":{},"editor":{}},"code_block":{"component":null,"key":"code_block","inject":{"pluginsByKey":{"deserializeHtml":{"editor":{"insertData":{}}}}},"isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"PRE"},{"validNodeName":"P","validStyle":{"fontFamily":"Consolas"}}]},"handlers":{},"options":{"hotkey":["mod+opt+8","mod+shift+8"],"syntax":true,"syntaxPopularFirst":false},"plugins":[{"key":"code_line","isElement":true,"type":"code_line","options":{},"inject":{},"editor":{}},{"key":"code_syntax","isLeaf":true,"type":"code_syntax","options":{},"inject":{},"editor":{}}],"type":"code_block","editor":{}},"code_line":{"key":"code_line","isElement":true,"type":"code_line","options":{},"inject":{},"editor":{}},"code_syntax":{"key":"code_syntax","isLeaf":true,"type":"code_syntax","options":{},"inject":{},"editor":{}},"heading":{"key":"heading","plugins":[{"key":"h1","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H1"}]},"handlers":{},"options":{"hotkey":["mod+opt+1","mod+shift+1"]},"type":"h1","inject":{},"editor":{}},{"key":"h2","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H2"}]},"handlers":{},"options":{"hotkey":["mod+opt+2","mod+shift+2"]},"type":"h2","inject":{},"editor":{}},{"key":"h3","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H3"}]},"handlers":{},"options":{"hotkey":["mod+opt+3","mod+shift+3"]},"type":"h3","inject":{},"editor":{}},{"key":"h4","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H4"}]},"handlers":{},"options":{},"type":"h4","inject":{},"editor":{}},{"key":"h5","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H5"}]},"handlers":{},"options":{},"type":"h5","inject":{},"editor":{}},{"key":"h6","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H6"}]},"handlers":{},"options":{},"type":"h6","inject":{},"editor":{}}],"options":{"levels":6},"type":"heading","inject":{},"editor":{}},"h1":{"key":"h1","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H1"}]},"handlers":{},"options":{"hotkey":["mod+opt+1","mod+shift+1"]},"type":"h1","inject":{},"editor":{}},"h2":{"key":"h2","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H2"}]},"handlers":{},"options":{"hotkey":["mod+opt+2","mod+shift+2"]},"type":"h2","inject":{},"editor":{}},"h3":{"key":"h3","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H3"}]},"handlers":{},"options":{"hotkey":["mod+opt+3","mod+shift+3"]},"type":"h3","inject":{},"editor":{}},"h4":{"key":"h4","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H4"}]},"handlers":{},"options":{},"type":"h4","inject":{},"editor":{}},"h5":{"key":"h5","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H5"}]},"handlers":{},"options":{},"type":"h5","inject":{},"editor":{}},"h6":{"key":"h6","isElement":true,"deserializeHtml":{"rules":[{"validNodeName":"H6"}]},"handlers":{},"options":{},"type":"h6","inject":{},"editor":{}},"p":{"key":"p","isElement":true,"handlers":{},"options":{"hotkey":["mod+opt+0","mod+shift+0"]},"deserializeHtml":{"rules":[{"validNodeName":"P"}]},"type":"p","inject":{},"editor":{}},"bold":{"key":"bold","isLeaf":true,"deserializeHtml":{"rules":[{"validNodeName":["STRONG","B"]},{"validStyle":{"fontWeight":["600","700","bold"]}}]},"handlers":{},"options":{"hotkey":"mod+b"},"type":"bold","inject":{},"editor":{}},"code":{"key":"code","isLeaf":true,"deserializeHtml":{"rules":[{"validNodeName":["CODE"]},{"validStyle":{"wordWrap":"break-word"}},{"validStyle":{"fontFamily":"Consolas"}}]},"handlers":{},"options":{"hotkey":"mod+e"},"type":"code","inject":{},"editor":{}},"italic":{"key":"italic","isLeaf":true,"handlers":{},"options":{"hotkey":"mod+i"},"deserializeHtml":{"rules":[{"validNodeName":["EM","I"]},{"validStyle":{"fontStyle":"italic"}}]},"type":"italic","inject":{},"editor":{}},"strikethrough":{"key":"strikethrough","isLeaf":true,"handlers":{},"options":{"hotkey":"mod+shift+x"},"deserializeHtml":{"rules":[{"validNodeName":["S","DEL","STRIKE"]},{"validStyle":{"textDecoration":"line-through"}}]},"type":"strikethrough","inject":{},"editor":{}},"subscript":{"key":"subscript","isLeaf":true,"handlers":{},"options":{"hotkey":"mod+,","clear":"superscript"},"deserializeHtml":{"rules":[{"validNodeName":["SUB"]},{"validStyle":{"verticalAlign":"sub"}}]},"type":"subscript","inject":{},"editor":{}},"superscript":{"key":"superscript","isLeaf":true,"handlers":{},"options":{"hotkey":"mod+.","clear":"subscript"},"deserializeHtml":{"rules":[{"validNodeName":["SUP"]},{"validStyle":{"verticalAlign":"super"}}]},"type":"superscript","inject":{},"editor":{}},"underline":{"key":"underline","isLeaf":true,"handlers":{},"options":{"hotkey":"mod+u"},"deserializeHtml":{"rules":[{"validNodeName":["U"]},{"validStyle":{"textDecoration":["underline"]}}]},"type":"underline","inject":{},"editor":{}},"combobox":{"key":"combobox","handlers":{},"type":"combobox","options":{},"inject":{},"editor":{}},"mention":{"key":"mention","options":{"id":"mention","trigger":"@"},"isElement":true,"isInline":true,"isVoid":true,"handlers":{},"plugins":[{"key":"mention_input","isElement":true,"isInline":true,"type":"mention_input","options":{},"inject":{},"editor":{}}],"type":"mention","inject":{},"editor":{}},"mention_input":{"key":"mention_input","isElement":true,"isInline":true,"type":"mention_input","options":{},"inject":{},"editor":{}},"#":{"key":"#","options":{"id":"#","trigger":"#","inputCreation":{"key":"creationId","value":"main"}},"isElement":true,"isInline":true,"isVoid":true,"handlers":{},"plugins":[{"key":"mention_input","isElement":true,"isInline":true,"type":"mention_input","options":{},"inject":{},"editor":{}}],"type":"#","inject":{},"editor":{}},"/":{"key":"/","options":{"id":"/","trigger":"/"},"isElement":true,"isInline":true,"isVoid":true,"handlers":{},"plugins":[{"key":"mention_input","isElement":true,"isInline":true,"type":"mention_input","options":{},"inject":{},"editor":{}}],"type":"/","inject":{},"editor":{}}},"history":{"undos":[[{"type":"insert_text","path":[0,0],"offset":0,"text":" "},{"type":"set_selection","properties":{"anchor":{"path":[0,0],"offset":1},"focus":{"path":[0,0],"offset":1}},"newProperties":{"anchor":{"path":[0,0],"offset":0},"focus":{"path":[0,0],"offset":0}}}],[{"type":"insert_node","path":[0,0],"node":{"type":"mention_input","children":[{"text":""}],"trigger":"@"}},{"type":"set_selection","properties":{"anchor":{"path":[0,1],"offset":0},"focus":{"path":[0,1],"offset":0}},"newProperties":{"anchor":{"path":[0,0,0],"offset":0},"focus":{"path":[0,0,0],"offset":0}}},{"type":"insert_node","path":[0,0],"node":{"text":""}},{"type":"set_selection","properties":{"anchor":{"path":[0,1,0],"offset":0},"focus":{"path":[0,1,0],"offset":0}},"newProperties":{"anchor":{"path":[0,0,0],"offset":0},"focus":{"path":[0,0,0],"offset":0}}},{"type":"insert_text","path":[0,1,0],"offset":0,"text":"@"},{"type":"move_node","path":[0,1,0],"newPath":[0,2]},{"type":"remove_node","path":[0,1],"node":{"type":"mention_input","children":[],"trigger":"@"}}]],"redos":[]}}
Environment
- browser: chrome, safari
Funding
- You can sponsor this specific effort via a Polar.sh pledge below
- We receive the pledge once the issue is completed & verified
Also encountered this issue, but unfortunately I don't understand how to fix it.
Combobox itself is not visible too in that situation, I think the root of it is getRangeBoundingClientRect function, it calls toDOMRange which calls slate.ReactEditor.toDOMRange and this function throws, so getDefaultBoundingClientRect is used instead and combobox is rendered somewhere outside of viewport. And then if you blindly press enter editor throws again, basically with the same error as toDOMRange, but this time the error is not handled
Further investigation leads me to think that it's because when creating the mention_input node the selection moves to [0,0,0] (inside of the inline), then to have leaf nodes on each side of the inline element an empty leaf node is inserted, pushing the mention input to [0,1,0] (selection remains inside of the mention_input), and finally the selection is set again to the previous selection [0,0,0] (before inserting the empty leaf). But as the error prompts, this path doesn't exist.
Same for when there is already an inline and the trigger is typed right next to it (and there is already a space after). It creates the mention_input, then a leaf node, then the selection is set to where the mention_input was originally created.
I also investigated this and came to the same conclusion as @PibeG. Slate inserts an empty text node before the mention input to fulfil this normalization constraint:
- Inline nodes cannot be the first or last child of a parent block, nor can it be next to another inline node in the children array. If this is the case, an empty text node will be added to correct this to be in compliance with the constraint.
Source: https://docs.slatejs.org/concepts/11-normalizing