-
Notifications
You must be signed in to change notification settings - Fork 167
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: enable components to notify binder when validation state changes (
#13940) (CP: 23.1) (#13993) Adds the needed API to notify Binder about validation status changes that happen in field components. Fixes #8242 Related to vaadin/flow-components#1158
- Loading branch information
1 parent
00791b2
commit e606d94
Showing
6 changed files
with
405 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
50 changes: 50 additions & 0 deletions
50
flow-data/src/main/java/com/vaadin/flow/data/binder/ValidationStatusChangeEvent.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* | ||
* Copyright 2000-2022 Vaadin Ltd. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | ||
* use this file except in compliance with the License. You may obtain a copy of | ||
* the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
* License for the specific language governing permissions and limitations under | ||
* the License. | ||
*/ | ||
package com.vaadin.flow.data.binder; | ||
|
||
import com.vaadin.flow.component.HasValue; | ||
|
||
import java.io.Serializable; | ||
|
||
/** | ||
* The event to be processed when | ||
* {@link ValidationStatusChangeListener#validationStatusChanged(ValidationStatusChangeEvent)} | ||
* invoked. | ||
* | ||
* @since 2.7 | ||
* | ||
* @param <V> | ||
* the value type | ||
*/ | ||
public class ValidationStatusChangeEvent<V> implements Serializable { | ||
|
||
private final HasValue<?, V> source; | ||
private final boolean newStatus; | ||
|
||
public ValidationStatusChangeEvent(HasValue<?, V> source, | ||
boolean newStatus) { | ||
this.source = source; | ||
this.newStatus = newStatus; | ||
} | ||
|
||
public HasValue<?, V> getSource() { | ||
return source; | ||
} | ||
|
||
public boolean getNewStatus() { | ||
return newStatus; | ||
} | ||
} |
57 changes: 57 additions & 0 deletions
57
flow-data/src/main/java/com/vaadin/flow/data/binder/ValidationStatusChangeListener.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/* | ||
* Copyright 2000-2022 Vaadin Ltd. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | ||
* use this file except in compliance with the License. You may obtain a copy of | ||
* the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
* License for the specific language governing permissions and limitations under | ||
* the License. | ||
*/ | ||
package com.vaadin.flow.data.binder; | ||
|
||
import java.io.Serializable; | ||
|
||
import com.vaadin.flow.function.ValueProvider; | ||
|
||
/** | ||
* The listener interface for receiving {@link ValidationStatusChangeEvent} | ||
* events. The classes that are interested in processing validation status | ||
* changed events of field components should register implementation of this | ||
* interface via | ||
* {@link HasValidator#addValidationStatusChangeListener(ValidationStatusChangeListener)} | ||
* which are called whenever such event is fired by the component. | ||
* <p> | ||
* This interface is primarily introduced to enable binding instances subscribe | ||
* for their own associated field's validation status change events and | ||
* revalidate after that. However, when all the components implementing | ||
* {@code HasValidator} interface, implement the correct behaviour for adding | ||
* and notifying listeners of the current type, other usages are also become | ||
* possible since the {@link ValidationStatusChangeEvent} payload contains the | ||
* source {@link com.vaadin.flow.component.HasValue} field and the new | ||
* validation status, thus for instance fields or buttons in a view can | ||
* subscribe for each other's validation statuses and enable/disable or clear | ||
* values, etc. respectively. | ||
* | ||
* @since 2.7 | ||
* | ||
* @see HasValidator | ||
* @see com.vaadin.flow.data.binder.Binder.BindingBuilderImpl#bind(ValueProvider, | ||
* Setter) | ||
*/ | ||
@FunctionalInterface | ||
public interface ValidationStatusChangeListener<V> extends Serializable { | ||
|
||
/** | ||
* Invoked when a ValidationStatusChangeEvent occurs. | ||
* | ||
* @param event | ||
* the event to be processed | ||
*/ | ||
void validationStatusChanged(ValidationStatusChangeEvent<V> event); | ||
} |
130 changes: 130 additions & 0 deletions
130
...a/src/test/java/com/vaadin/flow/data/binder/BinderValidationStatusChangeListenerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
package com.vaadin.flow.data.binder; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
import org.junit.Assert; | ||
import org.junit.Before; | ||
import org.junit.Test; | ||
import org.mockito.Mockito; | ||
|
||
import com.vaadin.flow.data.binder.testcomponents.TestHasValidatorDatePicker; | ||
import com.vaadin.flow.component.HasValue; | ||
import com.vaadin.flow.tests.data.bean.Person; | ||
|
||
import static com.vaadin.flow.data.binder.testcomponents.TestHasValidatorDatePicker.INVALID_DATE_FORMAT; | ||
|
||
public class BinderValidationStatusChangeListenerTest | ||
extends BinderTestBase<Binder<Person>, Person> { | ||
|
||
private static final String BIRTH_DATE_PROPERTY = "birthDate"; | ||
|
||
private final Map<HasValue<?, ?>, String> componentErrors = new HashMap<>(); | ||
|
||
@Before | ||
public void setUp() { | ||
binder = new Binder<>(Person.class) { | ||
@Override | ||
protected void handleError(HasValue<?, ?> field, | ||
ValidationResult result) { | ||
componentErrors.put(field, result.getErrorMessage()); | ||
} | ||
|
||
@Override | ||
protected void clearError(HasValue<?, ?> field) { | ||
super.clearError(field); | ||
componentErrors.remove(field); | ||
} | ||
}; | ||
item = new Person(); | ||
} | ||
|
||
@Test | ||
public void fieldWithHasValidatorDefaults_bindIsCalled_addValidationStatusListenerIsCalled() { | ||
var field = Mockito.spy( | ||
TestHasValidatorDatePicker.DatePickerHasValidatorDefaults.class); | ||
binder.bind(field, BIRTH_DATE_PROPERTY); | ||
Mockito.verify(field, Mockito.times(1)) | ||
.addValidationStatusChangeListener(Mockito.any()); | ||
} | ||
|
||
@Test | ||
public void fieldWithHasValidatorOnlyGetDefaultValidatorOverridden_bindIsCalled_addValidationStatusListenerIsCalled() { | ||
var field = Mockito.spy( | ||
TestHasValidatorDatePicker.DataPickerHasValidatorGetDefaultValidatorOverridden.class); | ||
binder.bind(field, BIRTH_DATE_PROPERTY); | ||
Mockito.verify(field, Mockito.times(1)) | ||
.addValidationStatusChangeListener(Mockito.any()); | ||
} | ||
|
||
@Test | ||
public void fieldWithHasValidatorOnlyAddListenerOverridden_bindIsCalled_addValidationStatusListenerIsCalled() { | ||
var field = Mockito.spy( | ||
TestHasValidatorDatePicker.DataPickerHasValidatorAddListenerOverridden.class); | ||
binder.bind(field, BIRTH_DATE_PROPERTY); | ||
Mockito.verify(field, Mockito.times(1)) | ||
.addValidationStatusChangeListener(Mockito.any()); | ||
} | ||
|
||
@Test | ||
public void fieldWithHasValidatorFullyOverridden_bindIsCalled_addValidationStatusChangeListenerIsCalled() { | ||
var field = Mockito.spy( | ||
TestHasValidatorDatePicker.DataPickerHasValidatorOverridden.class); | ||
binder.bind(field, BIRTH_DATE_PROPERTY); | ||
Mockito.verify(field, Mockito.times(1)) | ||
.addValidationStatusChangeListener(Mockito.any()); | ||
} | ||
|
||
@Test | ||
public void fieldWithHasValidatorFullyOverridden_fieldValidationStatusChangesToFalse_binderHandleErrorIsCalled() { | ||
var field = new TestHasValidatorDatePicker.DataPickerHasValidatorOverridden(); | ||
binder.bind(field, BIRTH_DATE_PROPERTY); | ||
Assert.assertEquals(0, componentErrors.size()); | ||
|
||
field.fireValidationStatusChangeEvent(false); | ||
Assert.assertEquals(1, componentErrors.size()); | ||
Assert.assertEquals(INVALID_DATE_FORMAT, componentErrors.get(field)); | ||
} | ||
|
||
@Test | ||
public void fieldWithHasValidatorFullyOverridden_fieldValidationStatusChangesToTrue_binderClearErrorIsCalled() { | ||
var field = new TestHasValidatorDatePicker.DataPickerHasValidatorOverridden(); | ||
binder.bind(field, BIRTH_DATE_PROPERTY); | ||
Assert.assertEquals(0, componentErrors.size()); | ||
|
||
field.fireValidationStatusChangeEvent(false); | ||
Assert.assertEquals(1, componentErrors.size()); | ||
Assert.assertEquals(INVALID_DATE_FORMAT, componentErrors.get(field)); | ||
|
||
field.fireValidationStatusChangeEvent(true); | ||
Assert.assertEquals(0, componentErrors.size()); | ||
Assert.assertNull(componentErrors.get(field)); | ||
} | ||
|
||
@Test | ||
public void fieldWithHasValidatorOnlyAddListenerOverriddenAndCustomValidation_fieldValidationStatusChangesToFalse_binderHandleErrorIsCalled() { | ||
var field = new TestHasValidatorDatePicker.DataPickerHasValidatorAddListenerOverridden(); | ||
binder.forField(field).withValidator(field::customValidation) | ||
.bind(BIRTH_DATE_PROPERTY); | ||
|
||
field.fireValidationStatusChangeEvent(false); | ||
Assert.assertEquals(1, componentErrors.size()); | ||
Assert.assertEquals(INVALID_DATE_FORMAT, componentErrors.get(field)); | ||
} | ||
|
||
@Test | ||
public void fieldWithHasValidatorOnlyAddListenerOverriddenAndCustomValidation_fieldValidationStatusChangesToTrue_binderClearErrorIsCalled() { | ||
var field = new TestHasValidatorDatePicker.DataPickerHasValidatorAddListenerOverridden(); | ||
binder.forField(field).withValidator(field::customValidation) | ||
.bind(BIRTH_DATE_PROPERTY); | ||
|
||
field.fireValidationStatusChangeEvent(false); | ||
Assert.assertEquals(1, componentErrors.size()); | ||
Assert.assertEquals(INVALID_DATE_FORMAT, componentErrors.get(field)); | ||
|
||
field.fireValidationStatusChangeEvent(true); | ||
Assert.assertEquals(0, componentErrors.size()); | ||
Assert.assertNull(componentErrors.get(field)); | ||
} | ||
|
||
} |
Oops, something went wrong.