diff --git a/vaadin-rich-text-editor-flow-parent/vaadin-rich-text-editor-flow-integration-tests/src/main/java/com/vaadin/flow/component/richtexteditor/tests/RichTextEditorI18nPage.java b/vaadin-rich-text-editor-flow-parent/vaadin-rich-text-editor-flow-integration-tests/src/main/java/com/vaadin/flow/component/richtexteditor/tests/RichTextEditorI18nPage.java index 4fceebbaff9..865d6ebf13c 100644 --- a/vaadin-rich-text-editor-flow-parent/vaadin-rich-text-editor-flow-integration-tests/src/main/java/com/vaadin/flow/component/richtexteditor/tests/RichTextEditorI18nPage.java +++ b/vaadin-rich-text-editor-flow-parent/vaadin-rich-text-editor-flow-integration-tests/src/main/java/com/vaadin/flow/component/richtexteditor/tests/RichTextEditorI18nPage.java @@ -9,31 +9,72 @@ package com.vaadin.flow.component.richtexteditor.tests; import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.html.NativeButton; import com.vaadin.flow.component.richtexteditor.RichTextEditor; import com.vaadin.flow.router.Route; @Route("vaadin-rich-text-editor/i18n") public class RichTextEditorI18nPage extends Div { + private final RichTextEditor editor; + public RichTextEditorI18nPage() { - RichTextEditor editor = new RichTextEditor(); - editor.setI18n(createCustomI18n()); + editor = new RichTextEditor(); + add(editor); - Div i18nOutput = new Div(); - i18nOutput.setId("i18n-output"); - i18nOutput.setText(editor.getI18n().toString()); + NativeButton setFullI18nButton = new NativeButton("set full i18n", + e -> editor.setI18n(createFullI18n())); + setFullI18nButton.setId("set-full-i18n"); - add(editor); - add(i18nOutput); + NativeButton setPartialI18nButton = new NativeButton("set partial i18n", + e -> editor.setI18n(createPartialI18n())); + setPartialI18nButton.setId("set-partial-i18n"); + + NativeButton detachButton = new NativeButton("detach", + e -> remove(editor)); + detachButton.setId("detach"); + + NativeButton attachButton = new NativeButton("attach", + e -> add(editor)); + attachButton.setId("attach"); + + add(setFullI18nButton, setPartialI18nButton, detachButton, + attachButton); + } + + private RichTextEditor.RichTextEditorI18n createFullI18n() { + //@formatter:off + return new RichTextEditor.RichTextEditorI18n() + .setUndo("Undo custom") + .setRedo("Redo custom") + .setBold("Bold custom") + .setItalic("Italic custom") + .setUnderline("Underline custom") + .setStrike("Strike custom") + .setColor("Color custom") + .setBackground("Background custom") + .setH1("Header 1 custom") + .setH2("Header 2 custom") + .setH3("Header 3 custom") + .setSubscript("Subscript custom") + .setSuperscript("Superscript custom") + .setListOrdered("Ordered list custom") + .setListBullet("Bullet list custom") + .setAlignLeft("Align left custom") + .setAlignCenter("Align center custom") + .setAlignRight("Align right custom") + .setImage("Image custom") + .setLink("Link custom") + .setBlockquote("Blockquote custom") + .setCodeBlock("Code block custom") + .setClean("Clean custom"); + //@formatter:on } - private RichTextEditor.RichTextEditorI18n createCustomI18n() { - return new RichTextEditor.RichTextEditorI18n().setUndo("1").setRedo("2") - .setBold("3").setItalic("4").setUnderline("5").setStrike("6") - .setColor("7").setBackground("8").setH1("9").setH2("10") - .setH3("11").setSubscript("12").setSuperscript("13") - .setListOrdered("14").setListBullet("15").setAlignLeft("16") - .setAlignCenter("17").setAlignRight("18").setImage("19") - .setLink("20").setBlockquote("21").setCodeBlock("22") - .setClean("23"); + private RichTextEditor.RichTextEditorI18n createPartialI18n() { + //@formatter:off + return new RichTextEditor.RichTextEditorI18n() + .setUndo("Undo custom") + .setRedo("Redo custom"); + //@formatter:on } } diff --git a/vaadin-rich-text-editor-flow-parent/vaadin-rich-text-editor-flow-integration-tests/src/test/java/com/vaadin/flow/component/richtexteditor/tests/RichTextEditorI18nIT.java b/vaadin-rich-text-editor-flow-parent/vaadin-rich-text-editor-flow-integration-tests/src/test/java/com/vaadin/flow/component/richtexteditor/tests/RichTextEditorI18nIT.java index 062ff1fd9fe..f1e80ceade9 100644 --- a/vaadin-rich-text-editor-flow-parent/vaadin-rich-text-editor-flow-integration-tests/src/test/java/com/vaadin/flow/component/richtexteditor/tests/RichTextEditorI18nIT.java +++ b/vaadin-rich-text-editor-flow-parent/vaadin-rich-text-editor-flow-integration-tests/src/test/java/com/vaadin/flow/component/richtexteditor/tests/RichTextEditorI18nIT.java @@ -20,18 +20,85 @@ @TestPath("vaadin-rich-text-editor/i18n") public class RichTextEditorI18nIT extends AbstractComponentIT { private RichTextEditorElement editor; - private TestBenchElement i18nOutput; @Before public void init() { open(); editor = $(RichTextEditorElement.class).waitForFirst(); - i18nOutput = $(TestBenchElement.class).id("i18n-output"); } @Test - public void initEditorWithCustomI18n_i18nUpdated() { - Assert.assertEquals(i18nOutput.getText(), - editor.getTitles().toString()); + public void setFullI18n_updatesAllTexts() { + $("button").id("set-full-i18n").click(); + + Assert.assertEquals("Undo custom", getToolbarButtonTooltipText("undo")); + Assert.assertEquals("Redo custom", getToolbarButtonTooltipText("redo")); + Assert.assertEquals("Bold custom", getToolbarButtonTooltipText("bold")); + Assert.assertEquals("Italic custom", + getToolbarButtonTooltipText("italic")); + Assert.assertEquals("Underline custom", + getToolbarButtonTooltipText("underline")); + Assert.assertEquals("Strike custom", + getToolbarButtonTooltipText("strike")); + Assert.assertEquals("Color custom", + getToolbarButtonTooltipText("color")); + Assert.assertEquals("Background custom", + getToolbarButtonTooltipText("background")); + Assert.assertEquals("Header 1 custom", + getToolbarButtonTooltipText("h1")); + Assert.assertEquals("Header 2 custom", + getToolbarButtonTooltipText("h2")); + Assert.assertEquals("Header 3 custom", + getToolbarButtonTooltipText("h3")); + Assert.assertEquals("Subscript custom", + getToolbarButtonTooltipText("subscript")); + Assert.assertEquals("Superscript custom", + getToolbarButtonTooltipText("superscript")); + Assert.assertEquals("Ordered list custom", + getToolbarButtonTooltipText("ol")); + Assert.assertEquals("Bullet list custom", + getToolbarButtonTooltipText("ul")); + Assert.assertEquals("Align left custom", + getToolbarButtonTooltipText("left")); + Assert.assertEquals("Align center custom", + getToolbarButtonTooltipText("center")); + Assert.assertEquals("Align right custom", + getToolbarButtonTooltipText("right")); + Assert.assertEquals("Image custom", + getToolbarButtonTooltipText("image")); + Assert.assertEquals("Link custom", getToolbarButtonTooltipText("link")); + Assert.assertEquals("Blockquote custom", + getToolbarButtonTooltipText("blockquote")); + Assert.assertEquals("Code block custom", + getToolbarButtonTooltipText("code")); + Assert.assertEquals("Clean custom", + getToolbarButtonTooltipText("clean")); + } + + @Test + public void setPartialI18n_mergesWithExistingI18n() { + $("button").id("set-partial-i18n").click(); + + Assert.assertEquals("Undo custom", getToolbarButtonTooltipText("undo")); + Assert.assertEquals("Redo custom", getToolbarButtonTooltipText("redo")); + Assert.assertEquals("bold", getToolbarButtonTooltipText("bold")); + Assert.assertEquals("italic", getToolbarButtonTooltipText("italic")); + } + + @Test + public void setI18n_detach_attach_i18nRestored() { + $("button").id("set-full-i18n").click(); + $("button").id("detach").click(); + $("button").id("attach").click(); + + editor = $(RichTextEditorElement.class).waitForFirst(); + Assert.assertEquals("Undo custom", getToolbarButtonTooltipText("undo")); + Assert.assertEquals("Redo custom", getToolbarButtonTooltipText("redo")); + } + + private String getToolbarButtonTooltipText(String buttonId) { + TestBenchElement tooltip = editor.$("vaadin-tooltip") + .withAttribute("for", "btn-" + buttonId).first(); + return tooltip.getPropertyString("text"); } } diff --git a/vaadin-rich-text-editor-flow-parent/vaadin-rich-text-editor-flow/src/main/java/com/vaadin/flow/component/richtexteditor/RichTextEditor.java b/vaadin-rich-text-editor-flow-parent/vaadin-rich-text-editor-flow/src/main/java/com/vaadin/flow/component/richtexteditor/RichTextEditor.java index e4235cda349..87e5548514d 100644 --- a/vaadin-rich-text-editor-flow-parent/vaadin-rich-text-editor-flow/src/main/java/com/vaadin/flow/component/richtexteditor/RichTextEditor.java +++ b/vaadin-rich-text-editor-flow-parent/vaadin-rich-text-editor-flow/src/main/java/com/vaadin/flow/component/richtexteditor/RichTextEditor.java @@ -39,6 +39,7 @@ import elemental.json.JsonArray; import elemental.json.JsonObject; +import elemental.json.JsonType; /** * Rich Text Editor is an input field for entering rich text. It allows you to @@ -96,18 +97,36 @@ public RichTextEditorI18n getI18n() { public void setI18n(RichTextEditorI18n i18n) { this.i18n = Objects.requireNonNull(i18n, "The i18n properties object should not be null"); + runBeforeClientResponse(ui -> { if (i18n == this.i18n) { - JsonObject i18nObject = (JsonObject) JsonSerializer - .toJson(this.i18n); - for (String key : i18nObject.keys()) { - getElement().executeJs("this.set('i18n." + key + "', $0)", - i18nObject.get(key)); - } + setI18nWithJS(); } }); } + private void setI18nWithJS() { + JsonObject i18nJson = (JsonObject) JsonSerializer.toJson(this.i18n); + + // Remove properties with null values to prevent errors in web + // component + removeNullValuesFromJsonObject(i18nJson); + + // Assign new I18N object to WC, by merging the existing + // WC I18N, and the values from the new RichTextEditorI18n instance, + // into an empty object + getElement().executeJs("this.i18n = Object.assign({}, this.i18n, $0);", + i18nJson); + } + + private void removeNullValuesFromJsonObject(JsonObject jsonObject) { + for (String key : jsonObject.keys()) { + if (jsonObject.get(key).getType() == JsonType.NULL) { + jsonObject.remove(key); + } + } + } + void runBeforeClientResponse(SerializableConsumer command) { getElement().getNode().runWhenAttached(ui -> ui .beforeClientResponse(this, context -> command.accept(ui))); @@ -147,6 +166,11 @@ protected void onAttach(AttachEvent attachEvent) { // presentation value to run the necessary JS for initializing the // client-side element setPresentationValue(getValue()); + + // Element state is not persisted across attach/detach + if (this.i18n != null) { + setI18nWithJS(); + } } /**