diff options
6 files changed, 149 insertions, 0 deletions
diff --git a/client/src/main/java/com/vaadin/client/ui/VAbstractPopupCalendar.java b/client/src/main/java/com/vaadin/client/ui/VAbstractPopupCalendar.java index 2682973964..a199e2cd4a 100644 --- a/client/src/main/java/com/vaadin/client/ui/VAbstractPopupCalendar.java +++ b/client/src/main/java/com/vaadin/client/ui/VAbstractPopupCalendar.java @@ -496,6 +496,10 @@ public abstract class VAbstractPopupCalendar<PANEL extends VAbstractCalendarPane setText(previousValue); } updateTextFieldEnabled(); + bufferedDateString = text.getText(); + updateBufferedResolutions(); + // send the Time changes with delayed rpc. + sendBufferedValuesWithDelay(); } /** diff --git a/client/src/main/java/com/vaadin/client/ui/VDateField.java b/client/src/main/java/com/vaadin/client/ui/VDateField.java index 2d4136bd76..a6c5a158de 100644 --- a/client/src/main/java/com/vaadin/client/ui/VDateField.java +++ b/client/src/main/java/com/vaadin/client/ui/VDateField.java @@ -291,6 +291,26 @@ public abstract class VDateField<R extends Enum<R>> extends FlowPanel } /** + * Puts the {@link #bufferedDateString} and {@link #bufferedResolutions} + * changes into the rpc queue and clears their values. + * <p> + * Note: The value will not be sent to the server immediately. It will be + * sent when a non {@link com.vaadin.shared.annotations.Delayed} annotated + * rpc is triggered. + * </p> + * + * @since + */ + public void sendBufferedValuesWithDelay() { + rpc.updateValueWithDelay(bufferedDateString, + bufferedResolutions.entrySet().stream().collect( + Collectors.toMap(entry -> entry.getKey().name(), + entry -> entry.getValue()))); + bufferedDateString = null; + bufferedResolutions.clear(); + } + + /** * Returns all available resolutions for the field in the ascending order * (which is the same as order of enumeration ordinals). * <p> diff --git a/server/src/main/java/com/vaadin/ui/AbstractDateField.java b/server/src/main/java/com/vaadin/ui/AbstractDateField.java index f03c900b97..f7a085bc0d 100644 --- a/server/src/main/java/com/vaadin/ui/AbstractDateField.java +++ b/server/src/main/java/com/vaadin/ui/AbstractDateField.java @@ -88,6 +88,16 @@ public abstract class AbstractDateField<T extends Temporal & TemporalAdjuster & @Override public void update(String newDateString, Map<String, Integer> resolutions) { + valueUpdate(newDateString, resolutions); + } + + @Override + public void updateValueWithDelay(String newDateString, + Map<String, Integer> resolutions) { + valueUpdate(newDateString, resolutions); + } + + private void valueUpdate(String newDateString, Map<String, Integer> resolutions) { Set<String> resolutionNames = getResolutions().map(Enum::name) .collect(Collectors.toSet()); resolutionNames.retainAll(resolutions.keySet()); diff --git a/shared/src/main/java/com/vaadin/shared/ui/datefield/AbstractDateFieldServerRpc.java b/shared/src/main/java/com/vaadin/shared/ui/datefield/AbstractDateFieldServerRpc.java index e82c45f171..0344110443 100644 --- a/shared/src/main/java/com/vaadin/shared/ui/datefield/AbstractDateFieldServerRpc.java +++ b/shared/src/main/java/com/vaadin/shared/ui/datefield/AbstractDateFieldServerRpc.java @@ -17,6 +17,7 @@ package com.vaadin.shared.ui.datefield; import java.util.Map; +import com.vaadin.shared.annotations.Delayed; import com.vaadin.shared.communication.ServerRpc; /** @@ -42,6 +43,27 @@ public interface AbstractDateFieldServerRpc extends ServerRpc { void update(String newDateString, Map<String, Integer> resolutions); /** + * Updates the typed data string and resolution names and values with + * delayed rpc. The rpc will be sent by triggering another non + * {@link Delayed} annotated rpc. + * + * @since + * + * @param newDateString + * the value of the text field part. It enables analyzing invalid + * input on the server. {@code null} if the date was chosen with + * popup calendar or contains user-typed string + * @param resolutions + * map of time unit (resolution) name and value, the key is the + * resolution name e.g. "HOUR", "MINUTE", the value can be + * {@code null}. If the map is empty, that means the + * {@code newDateString} is invalid + */ + @Delayed(lastOnly = true) + void updateValueWithDelay(String newDateString, + Map<String, Integer> resolutions); + + /** * Indicates to the server that the client-side has lost focus. */ void blur(); diff --git a/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldEventOrder.java b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldEventOrder.java new file mode 100644 index 0000000000..420c83cca7 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldEventOrder.java @@ -0,0 +1,54 @@ +package com.vaadin.tests.components.datefield; + +import com.vaadin.annotations.Widgetset; +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.ValueChangeMode; +import com.vaadin.shared.ui.datefield.DateTimeResolution; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.tests.widgetset.TestingWidgetSet; +import com.vaadin.ui.Button; +import com.vaadin.ui.DateTimeField; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.TextField; +import com.vaadin.v7.shared.ui.datefield.Resolution; +import com.vaadin.v7.ui.DateField; + +@Widgetset(TestingWidgetSet.NAME) +public class DateTimeFieldEventOrder extends AbstractTestUIWithLog { + + @Override + protected void setup(VaadinRequest request) { + + HorizontalLayout horizontalLayout = new HorizontalLayout(); + + DateTimeField dateField = new DateTimeField(); + dateField.setResolution(DateTimeResolution.SECOND); + dateField.setId("test-field"); + dateField.addValueChangeListener( + event -> log("DateTimeField value change event")); + + Button button = new Button("test"); + button.setId("test-button"); + button.addClickListener(ev -> { + log("Button Click Event"); + }); + + TextField tf = new TextField("test"); + tf.setValueChangeMode(ValueChangeMode.BLUR); + tf.addValueChangeListener(event -> log("TextField value change event")); + + DateField df = new DateField(); + df.setResolution(Resolution.SECOND); + df.addValueChangeListener(event -> { + log("DateTimeField V7 value change event"); + }); + + horizontalLayout.addComponents(dateField, button, tf, df); + addComponent(horizontalLayout); + } + + @Override + protected Integer getTicketNumber() { + return 11316; + } +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldEventOrderTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldEventOrderTest.java new file mode 100644 index 0000000000..cead7d6f2b --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldEventOrderTest.java @@ -0,0 +1,39 @@ +package com.vaadin.tests.components.datefield; + +import java.util.List; + +import org.junit.Test; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.Select; + +import com.vaadin.testbench.By; +import com.vaadin.testbench.elements.DateTimeFieldElement; +import com.vaadin.tests.tb3.SingleBrowserTest; + +import static org.junit.Assert.assertEquals; + +public class DateTimeFieldEventOrderTest extends SingleBrowserTest { + + @Test + public void testEventOrderIsCorrect() { + openTestURL(); + + DateTimeFieldElement field = $(DateTimeFieldElement.class).first(); + + field.openPopup(); + + List<WebElement> timeSelects = findElement( + By.className("v-datefield-calendarpanel-time")) + .findElements(By.tagName("select")); + + new Select(timeSelects.get(0)).selectByValue("09"); + + findElement(By.id("test-button")).click(); + + assertEquals("The button click event should come second.", + "2. Button Click Event", getLogRow(0)); + assertEquals("The value change event of DTF should come firstly.", + "1. DateTimeField value change event", getLogRow(1)); + + } +} |