summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/src/main/java/com/vaadin/ui/AbstractDateField.java47
-rw-r--r--uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldBinderCrossValidation.java103
-rw-r--r--uitest/src/test/java/com/vaadin/tests/components/datefield/DateFieldBinderCrossValidationTest.java40
3 files changed, 176 insertions, 14 deletions
diff --git a/server/src/main/java/com/vaadin/ui/AbstractDateField.java b/server/src/main/java/com/vaadin/ui/AbstractDateField.java
index 48bc3e13f5..f03c900b97 100644
--- a/server/src/main/java/com/vaadin/ui/AbstractDateField.java
+++ b/server/src/main/java/com/vaadin/ui/AbstractDateField.java
@@ -38,10 +38,9 @@ import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import elemental.json.Json;
+import com.googlecode.gentyref.GenericTypeReflector;
import org.jsoup.nodes.Element;
-import com.googlecode.gentyref.GenericTypeReflector;
import com.vaadin.data.Result;
import com.vaadin.data.ValidationResult;
import com.vaadin.data.Validator;
@@ -64,6 +63,8 @@ import com.vaadin.ui.declarative.DesignAttributeHandler;
import com.vaadin.ui.declarative.DesignContext;
import com.vaadin.util.TimeZoneUtil;
+import elemental.json.Json;
+
/**
* A date editor component with {@link LocalDate} as an input value.
*
@@ -107,14 +108,14 @@ public abstract class AbstractDateField<T extends Temporal & TemporalAdjuster &
newDate = reconstructDateFromFields(resolutions, oldDate);
}
- boolean parseErrorWasSet = currentParseErrorMessage != null;
+ boolean parseErrorWasSet = currentErrorMessage != null;
hasChanges |= !Objects.equals(dateString, newDateString)
|| !Objects.equals(oldDate, newDate)
|| parseErrorWasSet;
if (hasChanges) {
dateString = newDateString;
- currentParseErrorMessage = null;
+ currentErrorMessage = null;
if (newDateString == null || newDateString.isEmpty()) {
boolean valueChanged = setValue(newDate, true);
if (!valueChanged && parseErrorWasSet) {
@@ -138,8 +139,8 @@ public abstract class AbstractDateField<T extends Temporal & TemporalAdjuster &
});
if (parsedDate.isError()) {
dateString = null;
- currentParseErrorMessage = parsedDate
- .getMessage().orElse("Parsing error");
+ currentErrorMessage = parsedDate.getMessage()
+ .orElse("Parsing error");
if (!isDifferentValue(null)) {
doSetValue(null);
@@ -187,7 +188,7 @@ public abstract class AbstractDateField<T extends Temporal & TemporalAdjuster &
private String dateString = "";
- private String currentParseErrorMessage;
+ private String currentErrorMessage;
private String defaultParseErrorMessage = "Date format not recognized";
@@ -607,7 +608,7 @@ public abstract class AbstractDateField<T extends Temporal & TemporalAdjuster &
*/
@Override
public void setValue(T value) {
- currentParseErrorMessage = null;
+ currentErrorMessage = null;
/*
* First handle special case when the client side component have a date
* string but value is null (e.g. unparsable date string typed in by the
@@ -783,16 +784,16 @@ public abstract class AbstractDateField<T extends Temporal & TemporalAdjuster &
new ValueContext(this, this));
if (result.isError()) {
- currentParseErrorMessage = getDateOutOfRangeMessage();
+ currentErrorMessage = getDateOutOfRangeMessage();
}
- getState().parsable = currentParseErrorMessage == null;
+ getState().parsable = currentErrorMessage == null;
ErrorMessage errorMessage;
- if (currentParseErrorMessage == null) {
+ if (currentErrorMessage == null) {
errorMessage = null;
} else {
- errorMessage = new UserError(currentParseErrorMessage);
+ errorMessage = new UserError(currentErrorMessage);
}
setComponentError(errorMessage);
@@ -876,9 +877,27 @@ public abstract class AbstractDateField<T extends Temporal & TemporalAdjuster &
return new Validator<T>() {
@Override
public ValidationResult apply(T value, ValueContext context) {
- if (currentParseErrorMessage != null) {
- return ValidationResult.error(currentParseErrorMessage);
+
+ // currentErrorMessage contains two type of messages, one is
+ // DateOutOfRangeMessage and the other one is the ParseError
+ if (currentErrorMessage != null) {
+ if (currentErrorMessage
+ .equals(getDateOutOfRangeMessage())) {
+ // if the currentErrorMessage is DateOutOfRangeMessage,
+ // then need to double check whether the error message
+ // has been updated, that is because of #11276.
+ ValidationResult validationResult = getRangeValidator()
+ .apply(value, context);
+ if (validationResult.isError()) {
+ return ValidationResult.error(currentErrorMessage);
+ }
+ } else {
+ // if the current Error is parsing error, pass it to the
+ // ValidationResult
+ return ValidationResult.error(currentErrorMessage);
+ }
}
+
// Pass to range validator.
return getRangeValidator().apply(value, context);
}
diff --git a/uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldBinderCrossValidation.java b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldBinderCrossValidation.java
new file mode 100644
index 0000000000..21685f6523
--- /dev/null
+++ b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldBinderCrossValidation.java
@@ -0,0 +1,103 @@
+package com.vaadin.tests.components.datefield;
+
+import java.time.LocalDate;
+import java.util.Objects;
+
+import com.gargoylesoftware.htmlunit.javascript.host.html.FormField;
+
+import com.vaadin.annotations.Widgetset;
+import com.vaadin.data.Binder;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.shared.ui.ErrorLevel;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.tests.components.AbstractTestUIWithLog;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.DateField;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.TextField;
+
+@Widgetset("com.vaadin.DefaultWidgetSet")
+public class DateFieldBinderCrossValidation extends AbstractTestUI {
+
+ @Override
+ protected void setup(VaadinRequest request) {
+ HorizontalLayout horizontalLayout = new HorizontalLayout();
+ Label label = new Label();
+ label.setId("status");
+
+ Binder<FromToModel> fromToModelBinder = new Binder<>();
+
+ DateField fromField = new DateField();
+ fromField.setId("from-field");
+ fromField.setDateFormat("yyyy/MM/dd");
+ final Binder.Binding<FromToModel, LocalDate> fromBinding = fromToModelBinder
+ .forField(fromField).asRequired()
+ .bind(FromToModel::getFromDate, FromToModel::setFromDate);
+ DateField toField = new DateField();
+ toField.setId("to-field");
+ toField.setDateFormat("yyyy/MM/dd");
+ final Binder.Binding<FromToModel, LocalDate> toBinding = fromToModelBinder
+ .forField(toField).asRequired()
+ .bind(FromToModel::getToDate, FromToModel::setToDate);
+
+ fromField.addValueChangeListener(e -> {
+ toField.setRangeStart(e.getValue());
+ if (toField.getValue() != null) {
+ toBinding.validate();
+ }
+ label.setValue("from field is " + fromField.getErrorMessage()
+ + ". To field is " + toField.getErrorMessage());
+ });
+ toField.addValueChangeListener(e -> {
+ fromField.setRangeEnd(e.getValue());
+ if (fromField.getValue() != null) {
+ fromBinding.validate();
+ }
+ label.setValue("from field is " + fromField.getErrorMessage()
+ + ". To field is " + toField.getErrorMessage());
+ });
+
+ horizontalLayout.addComponents(fromField, toField, label);
+
+ addComponents(horizontalLayout);
+ }
+
+ private static class FromToModel {
+
+ private LocalDate fromDate;
+ private LocalDate toDate;
+
+ public LocalDate getFromDate() {
+ return fromDate;
+ }
+
+ public void setFromDate(LocalDate fromDate) {
+ this.fromDate = fromDate;
+ }
+
+ public LocalDate getToDate() {
+ return toDate;
+ }
+
+ public void setToDate(LocalDate toDate) {
+ this.toDate = toDate;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof FromToModel))
+ return false;
+ FromToModel that = (FromToModel) o;
+ return Objects.equals(getFromDate(), that.getFromDate())
+ && Objects.equals(getToDate(), that.getToDate());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getFromDate(), getToDate());
+ }
+ }
+}
diff --git a/uitest/src/test/java/com/vaadin/tests/components/datefield/DateFieldBinderCrossValidationTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateFieldBinderCrossValidationTest.java
new file mode 100644
index 0000000000..77bf68d9a3
--- /dev/null
+++ b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateFieldBinderCrossValidationTest.java
@@ -0,0 +1,40 @@
+package com.vaadin.tests.components.datefield;
+
+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.LabelElement;
+import com.vaadin.tests.tb3.SingleBrowserTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class DateFieldBinderCrossValidationTest extends SingleBrowserTest {
+
+ private final static String EXPECTED_ERROR = "from field is Date is out of allowed range. To field is Date is out of allowed range";
+ private final static String EXPECTED_NULL_ERROR = "from field is null. To field is null";
+
+ @Test
+ public void makeBothFieldInvalidThenValid() {
+ openTestURL();
+
+ DateFieldElement fromField = $(DateFieldElement.class).id("from-field");
+ WebElement fromFieldText = fromField.findElement(By.tagName("input"));
+ DateFieldElement toField = $(DateFieldElement.class).id("to-field");
+ WebElement toFieldText = toField.findElement(By.tagName("input"));
+ LabelElement label = $(LabelElement.class).id("status");
+
+ fromFieldText.sendKeys("2019/01/01", Keys.ENTER);
+ toFieldText.sendKeys("2018/02/02", Keys.ENTER);
+
+ assertEquals("Error message should contain the information",
+ EXPECTED_ERROR, label.getText());
+
+ fromFieldText.clear();
+ fromFieldText.sendKeys("2018/01/01", Keys.ENTER);
+ assertEquals("Error message should be null", EXPECTED_NULL_ERROR,
+ label.getText());
+ }
+}