Testing Out the Lexical Implementation

The importDOM/exportDOM behavior of Lexical can sometimes cause Content Layout Shifts. I am using this post to test out all components to try to minimize content layout shifts.

Date Created:
Last Edited:
About Lexical State Handling

The way this site handles Lexical Editor States - when communicating with the server - is by:

  1. exporting the DOM to JSON on the client using editor.getEditorState().toJSON()
  2. calling exportDOM on the server
    1. Saving both the HTML and the result of exportDOM in the database
    2. exportDOM is called on the server right now using JSDOM.
  3. rendering the result of exportDOM when the lexical state needs to be rendered
  4. Calling importDOM on the client when a lexical state needs to be rendered

importJSON is used when copying and pasting things around the editor. As a result, importJSON, exportJSON, importDOM, and exportDOM all need to be optimized so that they accurately represent an editor node, and exportDOM needs to be optimized to prevent Content Layout Shift from occurring.

Note: importJSON, exportJSON, importDOM, and exportDOM are all methods that each node in the Lexical editor has.


What is This Page


I am using this page to test out the methods described above for each Lexical node to make sure that they all work correctly.


Lexical Nodes


This list does not include text nodes and nodes that are used only as a part of a larger node, e.g. TableCellNode and



Heading Node


The heading node is used to render heading elements <h1> - <h6>. There should be a keyboard shortcut that allows users to toggle between heading elements. See below:

Element

H1

H2

H3

H4

H5

H6

P

Keyboard Shortcut

ALT+H+1

ALT+H+2

ALT+H+3

ALT+H+4

ALT+H+5

ALT+H+6

ALT+P

The heading node inherits properties from the extended ElementNode - so it can have a block-level background color, borders, custom margin / padding, box shadow, and outlines.


Quote Node


The quote node allows for the rendering of <blockquote> elements - for when the user wants to insert a quote. There is really nothing special about the way this node is handled.

Self control means wanting to be effective at some random point in the infinite radiations of my spiritual existence.
Franz Kafka


List Node


The list node allows users to insert ordered and unordered lists. The list node inherits properties from the extended ElementNode - see the description of this node above. The max depth of a list node is 5. You should probably listen for text changes - and if the current selection is collapsed and contains the text - or \d+. , then you should insert an unordered list or ordered list, respectively - this allows ordered lists to start at a place other than 1.

  1. This
    1. is
      1. An
        1. Ordered
          1. List
  2. The list starts at 123 using the method described above.
  • This
    • is
      • an
        • unordered
          • list


Link Node


The link node allows users to insert links - anchor tags (<a>). The Link node has url, target, rel, and title attributes. The url attribute is the webpage the link is linking to, the target should always be _blank, and the rel attribute should be ugc because the link is user generated content - we don't know what the user is linking to.

Aside Node


The aside node allows users to insert <aside> elements. An example of an AsideNode can be seen to the right. These nodes can either be inserted to the left or right side of a ParagraphNode only. There is a side attribute that the AsideNode uses to keep track of which side the Aside node should appear on. The reason the AsideNode was included in the Lexical editor is to give people the opportunity to explain things that might break the flow of the article - you can see examples of <aside> elements around the web if you read news / blog articles.


Details Node


The details node allows users to insert <details> and <summary> elements.

Here is an example of a <details> element. Here is the mdn description of the <details> element. Similar to the <aside> element, the details element should probably be used to give additional information to the user when you don't want to interrupt the normal flow of the article.


Table Node


The table node allows users to insert tables with a maximum size of 50 columns x 50 rows. The user can customize the table by adding banded rows / banded columns, customizing the background color, borders, and vertical align of cells, and customizing the width of columns / height of rows.



YouTube Node


The YouTube node allows users to embed YouTube videos in an <iframe>. The alignment of a YouTube video - left, center, or justified - can be customized.




News Node


The News node allows users to link to news articles on other sites similar to other platforms. The requirements for a news article to be embedded are specified in the Embed News Content dialog.



Horizontal Rule Node


The Horizontal Rule node allows users to insert a horizontal rule (<hr /> element). The width and color of the horizontal rule is customizable. The default horizontal rule can be seen above.




Audio Node


The audio node allows users to insert audio files - <audio> elements. This let's users upload audio without having to upload a video.


Video Node


The Video node allows users to insert video files - <video> elements.


Image Node


The image node allows users to insert images - <img> elements. When uploaded, the image initially has an unassigned width and height - the width and height can be changed by resizing the image. The image can be aligned left, center, right, or justify.


Image Carousel Node


The image carousel node allows users to render image carousels using splidejs.



Code Node


The code node allows users to insert <code> elements that are highlighted using a variety of coding languages.

#include <stdio.h>
int main() { 
printf("Hello World!");
}


Paragraph Node


The paragraph node allows users to insert paragraphs - these are the most common nodes.


Math Node


The math node allows users to insert block level and inline math content using MathML and KaTeX. Here is some block level Math content:

Here is some inline Math Content, the Pythagorean Theorem: . You need to double click on inline math equations to edit them.

Testing out the MathNode for Longer Equations:

  • Needed to change the MathNode to have overflow-x: auto


Text Node


The text node allows users to insert bold content with the <strong> tag, italic content with the <em> tag, strikethrough content with the <span> tag, underline content with the <span> tag, some superscript content with the <sup> tag, some subscript content with the <sub> tag, inline code content with the <span> tag, and inline quote content with the <q> tag.

Known Problems

  1. The ElementNodeWithBlockStyle node is not exporting / importing borders correctly. The border radius is not working correctly.

There maybe a problem of appropriately escaping lists that needs to be looked into.

  1. Details Element: There may be a problem with deleting the details element.
    • When an empty paragraph that is immediately following a <details> element is deleted, the entire details element is deleted. Instead, the behavior should be that whatever is inside the AccordionContentNode should be focused.
    • Process when deleting Paragraph:
      • KEY_BACKSPACE_COMMAND: LexicalRichText
      • DELETE_CHARACTER_COMMAND: LexicalRightText
      • selection.deleteCharacter()
      • ParagraphNode.collapseAtStart()
      • If prevNode is DetialsNode, set DetailsNode open and select the AccordionContentNode
  1. TableNode:
    1. When the Edit Selected Cells dialog is closed, the page does not scroll back into the correct position.
    2. When the Edit Table dialog is closed, the page does not scroll back into the correct position.
    3. The Edit Table dialog does not work correctly.
    4. When another lexical editor on the page is focused, the activeCell for the TableNodes in the previously focused editor should be set to false.
    5. The height property for TableRowNodes is not working properly - this is working fine now.
    6. On load, there is a problem with reading the activeCell of a TableNode (See image below)

  • TableNode should be changed so that when editable is set to false, there is a TableWrapper node that is inserted and the TableNode is then inserted into the table wrapper
    • The table wrapper should provide horizontal rule
  1. NewsNode:
    1. Improve the styling of the NewsNode
    2. I think it looks pretty good now - search for .lexical-news-article when you need to change it again
  1. VideoNode / Audio Node:
    1. For the video node / audio node title input - need to remove some inline styling
    2. Need to improve the styling for the video - title should have an .h6 class and the caption for the video should have a .body1 class.
    3. Might want to transcode video files differently - look into adaptive bitrate streaming.
      1. Need to change to Adaptive bitrate streaming
    4. Might want to also decrease the max-height of video nodes to something less than 400/350 (idk what it is now) - it seems to be less on other platforms.
      1. You also have to think about vertical video here - which might be the most typical type of video (iPhone self recording)
  2. CodeNode:
    1. When starting a new line, it is impossible to add a tab or space
      1. Look into INSERT_TAB_COMMAND handlers
      2. This was solved by fixing the white-space attribute of the code.lexical-code styling
  3. Need to keep track of when the CustomEvent NEW_CONTENT_LOADED is called in Lexical. It shouldn't be called when rendering lexical - only as the result of commands being dispatched (when the editor is being created).
  4. Something is wrong with image upload (and maybe audio and video upload as well)
    1. First of all, for image, audio, and video upload, we want to prevent showing the filename of the uploaded media file - this needs to be addressed
    2. Secondly, the image upload doesn't allow you to update additional images after you have already uploaded one - this needs to be addressed
  5. There is a problem with disabling / un-disabling buttons on the toolbars that needs to be addresses (the ones that I have noticed are embedding YouTube/News and Quotes)


Things to Address


  • LinkNode: We don't know whether the user is linking to an unsafe website.
    • There maybe should be a listener that listens for when these links are clicked. The listener launches a dialog that makes the user confirm they want to go to whatever site is linked.
    • You could use the Google Safe Browsing API to determine whether the site is safe, and if it is not, change the LinkNode to a TextNode on the server.
    • Some combination of the two, e.g. Only show the dialog when Google says the site is unsafe / doesn't know whether the site is safe, and tell the user that we don't know about the site.
  • LinkNode: Inserting a link using the floating menu - the popover for the link should be dispatched relative to the floating menu, not in its normal position.
    • The problem with this is we don't know how to address the appropriate link node popup with the data-pelem attribute, unless we add a
  • Spacing between sections of an article: there should be some margin-top on all article sections except for the first - otherwise, it will be difficult for users to efficiently. space out their content.
  • When a word in a text node receives the dblclick event, the space to the right of the word is also highlighted, which makes it difficult to effectively insert inline-code blocks and sometimes links.
  • You should probably calculate how certain Lexical Sections and Code Sections and Markdown Sections will affect page load and implement pagination (or at least lazy loading of sections of an article).


Improvements to Make


  1. When a ParagraphNode is inserted inside an AsideNode the ParagraphNode should probably have its margin set to 0 immediately with the setBlockStyle method - this looks better.
  2. At the begging of a ElementNodeWithBlockStyle, maybe allow the user to specify CSS within brackets or something.
    1. If the list / heading node / paragraph starts with an expression matching this regex: /^css[]$/, then convert the CSS and style the node appropriately. This will prevent the user from having to open up the dialog.
  3. When you add Keyboard shortcuts to the Alt key, there should be a timeout where the default is prevented to prevent the default Alt behavior.
  4. Implement an inline node with the TextNode for the <kbd> keyboard input element.
  5. Server:
    1. All fetch calls or axios.get/axios.post calls on the backend should have a timeout.

Keeping Track of Certain Decisions


  1. Why not Include an Image Grid for Uploading Multiple Images?
    1. For one, I don't want to look into how to do it properly.
    2. Secondly, The users have the opportunity to upload custom code - where they can create image grids themselves
    3. There are enough tools out there today that should allow users to stick together images into a grid
    4. I think the Carousel should be enough
Notes on Editable Change
  1. Some things are hard to manage in both editable / not -editable lexical states.
  2. When the editor changes editability, we:
    1. set the data-no-edit on the root element of the editor
    2. Remove the character counter if there is one on the editor
    3. We remove the tabindex attribute from the row and column resizers
  3. We should:
    1. Wrap the TableNode in a TableNodeWrapper element
      1. You need to update the TableNodes that are currently in the database
      2. You need to check whether any TableNodes don't have a TableWrapperNode as a parent when validating editor
    2. Look at MathNode and TableCellNode for listeners that are added


How To Update Lexical States


Reasons You Might Want to Update the Stored Lexical State:

  1. You updated the exportDOM behavior of a node and you want the new version of exportDOM to be stored in the database so that the lexical state renders correctly.
  2. You changed the properties that a Lexical node has, and you want to add a property to the nodes in the database.
    1. Make sure to test function and/or create a Database Snapshot before doing this
    2. Check out the lexical/index file on the backend for an example of this
      1. I just did this to implement TableWrapperNode

Since you have the JSON and the HTML for the lexical state stored on the server, if you want to update the lexical state for a node / type of node, you can query the database for the nodes and call the updated exportDOM on the JSON to solve the issue described in 1. or you could add a default property / derive an attribute from the node to solve the issue described in 2..

Lexical does have a versioning system to help you avoid breaking changes. The version of a node should be stored as a version attribute when calling exportJSON and a data-v attribute when calling exportDOM. Breaking changes should only occur when the way a node is rendered completely changes - you should try to prevent this.

Make sure, when you add a new node to lexical, to register the node on the backend implementation

Comments

You must be logged in to post a comment!

Insert Math Markup

ESC
About Inserting Math Content
Display Style:

Embed News Content

ESC
About Embedding News Content

Embed Youtube Video

ESC
Embedding Youtube Videos

Embed TikTok Video

ESC
Embedding TikTok Videos

Embed X Post

ESC
Embedding X Posts

Embed Instagram Post

ESC
Embedding Instagram Posts

Insert Details Element

ESC

Example Output:

Summary Title
You will be able to insert content here after confirming the title of the <details> element.

Insert Table

ESC
Customization
Align:
Preview:

Insert Horizontal Rule

#000000

Preview:


Insert Chart

ESC

View Content At Different Sizes

ESC

Edit Style of Block Nodes

ESC

Edit the background color, default text color, margin, padding, and border of block nodes. Editable block nodes include paragraphs, headers, and lists.

#ffffff
#000000

Edit Selected Cells

Change the background color, vertical align, and borders of the cells in the current selection.

#ffffff
Vertical Align:
Border
#000000
Border Style:

Edit Table

ESC
Customization:
Align:

Upload Lexical State

ESC

Upload a .lexical file. If the file type matches the type of the current editor, then a preview will be shown below the file input.

Upload 3D Object

ESC

Upload Jupyter Notebook

ESC

Upload a Jupyter notebook and embed the resulting HTML in the text editor.

Insert Custom HTML

ESC

Edit Image Background Color

ESC
#ffffff

Insert Columns Layout

ESC
Column Type:

Select Code Language

ESC
Select Coding Language