ソースを参照

Correct the logic for validation used in DateField (#11307)

Fixes #11276 

Refactor the code and correct the logic for validation

Rename an internal variable, as it contains more than one type of messages
tags/8.6.1
Sun Zhe 5年前
コミット
2bfc76f63d

+ 33
- 14
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);
}

+ 103
- 0
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());
}
}
}

+ 40
- 0
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());
}
}

読み込み中…
キャンセル
保存