Skip to content

Commit

Permalink
fix: restore i18n when detaching and reattaching (#6947) (#6951)
Browse files Browse the repository at this point in the history
Co-authored-by: Sascha Ißbrücker <[email protected]>
  • Loading branch information
vaadin-bot and sissbruecker authored Dec 16, 2024
1 parent 8c28632 commit bd8cc5e
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<UI> command) {
getElement().getNode().runWhenAttached(ui -> ui
.beforeClientResponse(this, context -> command.accept(ui)));
Expand Down Expand Up @@ -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();
}
}

/**
Expand Down

0 comments on commit bd8cc5e

Please sign in to comment.