Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preserve indent and text-align when converting Lexical <-> HTML #5146

Closed
wkentdag opened this issue Feb 21, 2024 · 16 comments · Fixed by #9165
Closed

Preserve indent and text-align when converting Lexical <-> HTML #5146

wkentdag opened this issue Feb 21, 2024 · 16 comments · Fixed by #9165
Assignees
Labels
enhancement New feature or request good first issue Good for newcomers plugin: richtext-lexical @payloadcms/richtext-lexical

Comments

@wkentdag
Copy link

Link to reproduction

https://github.com/wkentdag/payload/blob/main/test/_community/int.spec.ts#L80-L100

Describe the Bug

I followed the recommended approach for converting lexical rich text into HTML, and discovered that some of the formatting gets stripped in the process. Inline marks, headings, and lists transfer fine; but indentation, alignment, and preformatted whitespace are ignored.

I realize I may need to set up my own Feature to handle preformatted whitespace, but I expect that the align and indent features will work out-of-the-box.

To demonstrate, things look correct in the editor:

Screen Shot 2024-02-21 at 1 05 03 PM

But when I convert the Lexical markup to HTML, it looks like this:

Screen Shot 2024-02-21 at 1 04 45 PM

In my test repro I am using the "ad-hoc" method of converting Lexical to HTML, but you get the same result with the HTMLConverterFeature. Happy to add another test case with that example, I just skipped it to keep things simple.

To Reproduce

  1. Clone my fork
  2. run jest test/_community/int.spec.ts -t '_Community Tests correctly formats rich text'
  3. Note the failing test, which specifically chokes on the center-aligned and indented paragraphs.

Payload Version

2.11.1

Adapters and Plugins

db-mongodb

@wkentdag wkentdag added the status: needs-triage Possible bug which hasn't been reproduced yet label Feb 21, 2024
@wkentdag wkentdag changed the title Lexical Some lexical rich text formatting removed when converted to HTML Feb 21, 2024
@wkentdag wkentdag changed the title Some lexical rich text formatting removed when converted to HTML Lexical rich text misformatted when converted to HTML Feb 21, 2024
@GeekPro101
Copy link

GeekPro101 commented Mar 11, 2024

+1, setting align does nothing in the HTML for me when using HTMLConverterFeature

@AlessioGr AlessioGr self-assigned this Mar 31, 2024
@AlessioGr AlessioGr added the plugin: richtext-lexical @payloadcms/richtext-lexical label Mar 31, 2024
@SimYunSup
Copy link
Contributor

There is no Converter for align and indent. But "whitespace stripped" is not bug. Just add CSS white-space.

@wkentdag
Copy link
Author

@SimYunSup fair, but I expect the generated HTML to match the markup rendered (correctly) in the admin Lexical editor out of the box. so IMO this is still a bug.

One possible solution would be to add <pre> to the default feature set. Seems like a simple enough feature, I'd be happy to make a PR for that. what do you think @AlessioGr ?

@AlessioGr
Copy link
Member

I think adding align and indent to the default converters would be a good idea. PRs for that are welcome!

I do think that we shouldn't output hard-coded style attributes in the generated HTML, though. Feels too opinionated. But we can add classNames that make it easy for the user to target those with their own styles

@AlessioGr AlessioGr added enhancement New feature or request good first issue Good for newcomers and removed status: needs-triage Possible bug which hasn't been reproduced yet labels Apr 10, 2024
@wkentdag
Copy link
Author

@AlessioGr I wasn't suggesting modifying the generated HTML, but rather adding a preformatted whitespace feature that you can select from the dropdown. So where I'm currently expecting this snippet of rendered HTML:

<p>weirdly formatted</p>

I'd swap the paragraph for pre and get this instead:

<pre>weirdly formatted</pre>.

That's an enhancement that I'm planning to tackle. However, my primary concern is that the indent and align features are stripped during HTML conversion. Is that not a bug?

@SimYunSup
Copy link
Contributor

The Lexical Adapter is creating a Feature by adding Nodes and Plugins. It's important to note that paragraph (as <p> tag) has a Node and a corresponding converter, while Align and Indent only add attributes to the Node(code), doesn't have a Node.

If you want to convert a <p> tag to a <pre> tag, you can change the paragraph converter from HTMLConverterFeature in your service code.

However, Align and Indent are different. They are not Node-agonistic features, so you need to add a new converter (type HTMLConverter)

@wkentdag
Copy link
Author

wkentdag commented Apr 12, 2024

Thanks for the explanation @SimYunSup, I'm definitely still trying to wrap my head around all the Lexical concepts.

I'm able to reproduce align and indent in HTML by tweaking the default paragraph converter like this:

export const FormattedParagraphHTMLConverter: HTMLConverter<any> = {
  // @ts-expect-error figure out what submissionData is/does
  async converter({ converters, node, parent, submissionData }) {
    const childrenText = await convertLexicalNodesToHTML({
      converters,
      lexicalNodes: node.children,
      parent: {
        ...node,
        parent,
      },
      // @ts-expect-error saa
      submissionData,
    })

    let pTag = '<p>'
    let style = ''
    if (!!node.format) {
      style += `text-align: ${node.format};`
    }

    if (node.indent > 0) {
      style += `text-indent:${node.indent}em;`
    }

    if (!!style) {
      pTag = `<p style="${style}">`
    }

    return `${pTag}${childrenText}</p>`
  },
  nodeTypes: ['paragraph'],
}

It would be nice to have something like this in the default config without having to setup a custom feature. I can see how hardcoding the style attributes is a little rigid...what kind of interface are you imagining for providing classNames @AlessioGr ? Or is it just as simple as swapping class="align-center" for style="text-align:center;" ?

@ozzestrophic
Copy link

I agree that aligning and indentations should be resolved by default in the serializer.
Will this solution be merged anytime?

@mobeigi
Copy link

mobeigi commented Sep 1, 2024

I also would like for this PR (#5814) to be merged / reviewed. Feels like extremely basic (and desirable) functionality for those who want to render lexical to HTML. Otherwise you have no other way of positioning text based on the editors AlignFeature. Looks like it pops up on the Discord community-help quite frequently too.

Is there a way to implement the PR locally as a workaround? The convertLexicalNodesToHTML from payload/packages/richtext-lexical/src/field/features/converters/html/converter/index.ts isn't exported which makes this annoying.

@SimYunSup
Copy link
Contributor

@mobeigi convertLexicalNodesToHTML is exported in @payloadcms/richtext-lexical.

export {
convertLexicalNodesToHTML,
convertLexicalToHTML,
} from './field/features/converters/html/converter'

But you should use it with the following caution.

It must be head of converter because find converter with Array.find.

@erwin-gapai
Copy link

@wkentdag Hi, i'd like to use your code in my project, by creating a utils and import it like so:

return [
          ...rootFeatures,
          ...defaultFeatures,
          HeadingFeature({ enabledHeadingSizes: ['h2', 'h3', 'h4'] }),
          FixedToolbarFeature(),
          InlineToolbarFeature(),
          HTMLConverterFeature({}),
          FormattedParagraphHTMLConverter()
        ]

but it gives error: FormattedParagraphHTMLConverter) is not a function

@GermanJablo GermanJablo changed the title Lexical rich text misformatted when converted to HTML Preserve indent and text-align when converting Lexical <-> HTML Sep 17, 2024
@GermanJablo
Copy link
Contributor

Status: @SimYunSup kindly offered to contribute to this issue.

The current PR addressing this is #8030

SimYunSup added a commit to SimYunSup/payload that referenced this issue Sep 18, 2024
# Conflicts:
#	packages/richtext-lexical/src/lexical/theme/EditorTheme.scss
@GermanJablo
Copy link
Contributor

Status update 2

I ended up opening the following PR to resolve the indent issue here: facebook/lexical#6693

I'll do the same for text-align shortly.

If anyone is interested, contributions are still welcome for HTML serialization to include custom indentation values in editorConfig.theme.indent, as explained in the linked PR.

@MohammadKurjieh
Copy link

Temporary solution using patch-package inspired by @wkentdag that do not require any change in the project code.

diff --git a/dist/features/converters/html/converter/converters/paragraph.js b/dist/features/converters/html/converter/converters/paragraph.js
index e6b2305e6a12a3006989ee47fb29073a6d8d3e55..ba4179bcad9f01e484509a2fff376348f41e69f7 100644
--- a/dist/features/converters/html/converter/converters/paragraph.js
+++ b/dist/features/converters/html/converter/converters/paragraph.js
@@ -1,6 +1,6 @@
 import { convertLexicalNodesToHTML } from '../index.js';
 export const ParagraphHTMLConverter = {
-    async converter ({ converters, node, parent, req }) {
+    async converter({ converters, node, parent, req }) {
         const childrenText = await convertLexicalNodesToHTML({
             converters,
             lexicalNodes: node.children,
@@ -10,7 +10,22 @@ export const ParagraphHTMLConverter = {
             },
             req
         });
-        return `<p>${childrenText}</p>`;
+
+        let pTag = '<p>'
+        let style = ''
+        if (!!node.format) {
+            style += `text-align: ${node.format};`
+        }
+
+        if (node.indent > 0) {
+            style += `padding-inline-start:${node.indent * 40}px;`
+        }
+
+        if (!!style) {
+            pTag = `<p style="${style}">`
+        }
+
+        return `${pTag}${childrenText}</p>`
     },
     nodeTypes: [
         'paragraph'

Copy link
Contributor

🚀 This is included in version v3.1.0

Copy link
Contributor

This issue has been automatically locked.
Please open a new issue if this issue persists with any additional detail.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 24, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request good first issue Good for newcomers plugin: richtext-lexical @payloadcms/richtext-lexical
Projects
None yet
9 participants