From 2bf0b02220ea96ee959b766a32bf1a79ae968317 Mon Sep 17 00:00:00 2001 From: Anna Koskinen Date: Wed, 15 Jan 2020 17:07:14 +0200 Subject: [PATCH] Ensure value change happens before shortcuts in compatibility components (#11871) Fixes #10854 --- .../client/ui/ShortcutActionHandler.java | 42 +++++++++++++++-- .../CompatibilityDateFieldShortcut.java | 47 +++++++++++++++++++ .../CompatibilityDateFieldShortcutTest.java | 39 +++++++++++++++ 3 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 uitest/src/main/java/com/vaadin/tests/components/datefield/CompatibilityDateFieldShortcut.java create mode 100644 uitest/src/test/java/com/vaadin/tests/components/datefield/CompatibilityDateFieldShortcutTest.java diff --git a/client/src/main/java/com/vaadin/client/ui/ShortcutActionHandler.java b/client/src/main/java/com/vaadin/client/ui/ShortcutActionHandler.java index 7499779352..1c03a2719d 100644 --- a/client/src/main/java/com/vaadin/client/ui/ShortcutActionHandler.java +++ b/client/src/main/java/com/vaadin/client/ui/ShortcutActionHandler.java @@ -126,15 +126,31 @@ public class ShortcutActionHandler { target = Util.findPaintable(client, et); } final ComponentConnector finalTarget = target; - event.preventDefault(); - /* * The focused component might have unpublished changes, try to * synchronize them before firing shortcut action. */ client.flushActiveConnector(); - + /* + * Legacy components don't have built-in logic for flushing, they need a + * workaround with blur and focus to trigger the value change. + */ + ComponentConnector activeConnector = getActiveConnector(client); + if (activeConnector != null) { + Class clz = activeConnector.getClass(); + while (clz != null) { + if (clz.getName().equals( + "com.vaadin.v7.client.ui.AbstractLegacyComponentConnector")) { + shakeTarget(et); + Scheduler.get().scheduleDeferred(() -> { + shakeTarget(et); + }); + break; + } + clz = clz.getSuperclass(); + } + } Scheduler.get().scheduleDeferred(() -> { if (finalTarget != null) { client.updateVariable(paintableId, "actiontarget", finalTarget, @@ -144,6 +160,26 @@ public class ShortcutActionHandler { }); } + /** + * We try to fire value change in the component the key combination was + * typed. E.g. TextField may contain newly typed text that is expected to be + * sent to server before the shortcut action is triggered. This is done by + * removing focus and then returning it immediately back to target element. + *

+ * This is a hack copied over from V7 in order to keep the compatibility + * classes working. Main V8 classes don't require shaking. + */ + private static void shakeTarget(final Element e) { + blur(e); + focus(e); + } + + private static native ComponentConnector getActiveConnector( + ApplicationConnection ac) + /*-{ + return ac.@com.vaadin.client.ApplicationConnection::getActiveConnector()(); + }-*/; + private static native void blur(Element e) /*-{ if (e.blur) { diff --git a/uitest/src/main/java/com/vaadin/tests/components/datefield/CompatibilityDateFieldShortcut.java b/uitest/src/main/java/com/vaadin/tests/components/datefield/CompatibilityDateFieldShortcut.java new file mode 100644 index 0000000000..0881ced2fc --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/datefield/CompatibilityDateFieldShortcut.java @@ -0,0 +1,47 @@ +package com.vaadin.tests.components.datefield; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import com.vaadin.event.ShortcutAction.KeyCode; +import com.vaadin.event.ShortcutListener; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Notification; +import com.vaadin.v7.ui.DateField; + +@SuppressWarnings("deprecation") +public class CompatibilityDateFieldShortcut extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + String dateFormat = "dd/MM/yyyy"; + + DateField dateField = new DateField(); + dateField.setValue(new Date(2018 - 1900, 0, 11)); + dateField.setDateFormat(dateFormat); + + dateField.addShortcutListener( + new ShortcutListener("Enter", KeyCode.ENTER, null) { + @Override + public void handleAction(Object sender, Object target) { + SimpleDateFormat df = new SimpleDateFormat(dateFormat); + Notification.show(df.format(dateField.getValue())); + } + }); + + addComponent(dateField); + } + + @Override + protected String getTestDescription() { + return "Modify the date manually (without using the popup element) and" + + " then press Enter. The notification should show the modified" + + " value instead of the old value."; + } + + @Override + protected Integer getTicketNumber() { + return 10854; + } +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/datefield/CompatibilityDateFieldShortcutTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/CompatibilityDateFieldShortcutTest.java new file mode 100644 index 0000000000..8ed2da2b77 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/datefield/CompatibilityDateFieldShortcutTest.java @@ -0,0 +1,39 @@ +package com.vaadin.tests.components.datefield; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebElement; + +import com.vaadin.testbench.elements.DateFieldElement; +import com.vaadin.testbench.elements.NotificationElement; +import com.vaadin.tests.tb3.SingleBrowserTest; + +public class CompatibilityDateFieldShortcutTest extends SingleBrowserTest { + + private static final String DATEFIELD_VALUE_ORIGINAL = "11/01/2018"; + private static final String DATEFIELD_VALUE_MODIFIED = "21/01/2018"; + + @Test + public void modifyValueAndPressEnter() { + openTestURL(); + + DateFieldElement dateField = $(DateFieldElement.class).first(); + WebElement dateFieldText = dateField.findElement(By.tagName("input")); + + assertEquals("DateField value should be \"" + DATEFIELD_VALUE_ORIGINAL + + "\"", DATEFIELD_VALUE_ORIGINAL, dateField.getValue()); + + dateFieldText.click(); + dateFieldText.sendKeys(Keys.HOME, Keys.DELETE, "2"); + dateFieldText.sendKeys(Keys.ENTER); + + assertEquals("DateField value should be \"" + DATEFIELD_VALUE_MODIFIED + + "\"", DATEFIELD_VALUE_MODIFIED, dateField.getValue()); + + assertEquals(DATEFIELD_VALUE_MODIFIED, + $(NotificationElement.class).first().getCaption()); + } +} -- 2.39.5