vaadin / flow-components

Java counterpart of Vaadin Web Components
100 stars 66 forks source link

RichTextEditor looses newlines when placed inside GridLayout #6653

Open vt512 opened 2 days ago

vt512 commented 2 days ago

Description

Setting HTML text via asHtml().setValue() to a RichTextEditor which is displayed in a GridLayout doesn't display the HTML text correctly in Firefox.

Longer description: Add a RichTextEditor directly to a VerticalLayout (rte1) and an other one to a GridLayout (rte2). In both RichTextEditors set an HTML value via asHtml().setValue(). In rte1 the HTML value is displayed correctly, in rte2 the styling is corrupt (e.g. bullet list is missing) and newlines are lost.

The error occurs only in Firefox. In Chromium the HTML text is displayed correctly also in rte2.

The same error appears when the RichTextEditor is placed in a Dialog which was already reported in #6636 and fixed in #6638, but the fix doesn't fix the problem, when placed inside a GridLayout.

Expected outcome

The RichTextEditor should display the HTML text inside a GridLayout correctly in the same way as it does when it is placed on a View directly.

Minimal reproducible example

Add

        <dependency>
            <groupId>org.vaadin.addons.componentfactory</groupId>
            <artifactId>vaadin-css-grid-layout</artifactId>
            <version>3.0.0</version>
        </dependency>

to the pom.xml.

Extend RichTextEditor and overwrite setPresentationValue() in a way similar to the patch in #6638:

import com.vaadin.flow.component.richtexteditor.RichTextEditor;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.safety.Safelist;

public class MyRichTextEditor extends RichTextEditor {

    private boolean pendingPresentationUpdate = false;

    @Override
    protected void setPresentationValue(String newPresentationValue) {
        final var presentationValue = mySanitize(newPresentationValue);
        getElement().setProperty("htmlValue", presentationValue);
        if (pendingPresentationUpdate) return;
        pendingPresentationUpdate = true;
        getElement().getNode().runWhenAttached(ui -> {
            ui.beforeClientResponse(this, context -> {
                getElement().callJsFunction("dangerouslySetHtmlValue", getElement().getProperty("htmlValue"));
                pendingPresentationUpdate = false;
            });
        });
    }

    public static String mySanitize(String html) {
        if (html == null) return null;

        final var settings = new Document.OutputSettings();
        settings.prettyPrint(false);
        return Jsoup.clean(
                html,
                "",
                Safelist.basic()
                        .addTags("img", "h1", "h2", "h3", "s")
                        .addAttributes("img", "align", "alt", "height", "src", "title", "width")
                        .addAttributes(":all", "style")
                        .addProtocols("img", "src", "data"),
                settings
        );
    }
}

Use that class in the example:

    private static final String HTML_TEXT = "Start<ul><li>P1</li><li>P2</li></ul>End";

    private RichTextEditor rte() {
        final var editor = new MyRichTextEditor();
        editor.asHtml().setValue(HTML_TEXT);
        return editor;
    }

    private void createUI(VerticalLayout layout) {
        layout.add(rte()); // HTML text is correct

        final var button = new Button("click to open dialog with RTE");
        button.addClickListener(e -> {
            final var dialog = new Dialog();
            dialog.setHeaderTitle("RichText");
            dialog.add(rte());  // HTML text is correct
            dialog.getFooter().add(new Button("cancel", cancelClickEvent -> dialog.close()));
            dialog.open();
        });
        add(button);

        final var gridLayout = new org.vaadin.addons.componentfactory.layout.GridLayout();
        gridLayout.setTemplateColumns(new Auto());
        gridLayout.add(rte()); // HTML text is invalid
        layout.add(gridLayout);
    }

Steps to reproduce

Execute the minimal example above. Two RichTextEditors and a Button are displayed: The first RTE displays the HTML text correctly with a bullet list of two bullet points, a click on the Button opens a Dialog with a RTE that displays the HTML text also correctly, the second RTE displays the HTML text invalid in one line without line breaks and no bullet list.

Environment

Vaadin version(s): 24.4.11 OS: Linux

Browsers

Firefox

knoobie commented 1 day ago

How did you test it? The latest 24.4.x version does currently not include the mentioned fix - a version containing this fix is not released.

vt512 commented 20 hours ago

Oh, sorry, you are right. I forgot to mention it in the example above. I created a subclass of RichTextEditor and overwrote the function setPresentationValue() in a way similar to the patch from #6638. I will modify the example in the issue text above.