Skip to content

Commit

Permalink
feat: add i18n error message support for RadioButtonGroup (#6477)
Browse files Browse the repository at this point in the history
  • Loading branch information
vursen authored Jul 30, 2024
1 parent 53797bf commit 3f01844
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
import org.junit.Test;
import org.openqa.selenium.Keys;

import static com.vaadin.flow.component.radiobutton.tests.validation.BinderValidationPage.REQUIRED_ERROR_MESSAGE;

@TestPath("vaadin-radio-button-group/validation/binder")
public class BinderValidationIT
extends AbstractValidationIT<RadioButtonGroupElement> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import com.vaadin.flow.component.shared.SelectionPreservationMode;
import com.vaadin.flow.component.shared.ValidationUtil;
import com.vaadin.flow.data.binder.HasValidator;
import com.vaadin.flow.data.binder.ValidationResult;
import com.vaadin.flow.data.provider.DataChangeEvent;
import com.vaadin.flow.data.provider.DataProvider;
import com.vaadin.flow.data.provider.DataProviderWrapper;
Expand All @@ -57,11 +58,13 @@
import com.vaadin.flow.function.SerializablePredicate;
import com.vaadin.flow.shared.Registration;

import java.io.Serializable;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Stream;

/**
Expand Down Expand Up @@ -104,8 +107,13 @@ public class RadioButtonGroup<T>

private SerializableConsumer<UI> sizeRequest;

private RadioButtonGroupI18n i18n;

private boolean manualValidationEnabled = false;

private String customErrorMessage;
private String constraintErrorMessage;

private SelectionPreservationHandler<T> selectionPreservationHandler;

private static <T> T presentationToModel(
Expand Down Expand Up @@ -237,6 +245,44 @@ public RadioButtonGroup(String label,
setItems(items);
}

/**
* Sets an error message to display for all constraint violations.
* <p>
* This error message takes priority over i18n error messages when both are
* set.
*
* @param errorMessage
* the error message to set, or {@code null} to clear
*/
@Override
public void setErrorMessage(String errorMessage) {
customErrorMessage = errorMessage;
updateErrorMessage();
}

/**
* Gets the error message displayed for all constraint violations.
*
* @return the error message
*/
@Override
public String getErrorMessage() {
return customErrorMessage;
}

private void setConstraintErrorMessage(String errorMessage) {
constraintErrorMessage = errorMessage;
updateErrorMessage();
}

private void updateErrorMessage() {
String errorMessage = constraintErrorMessage;
if (customErrorMessage != null && !customErrorMessage.isEmpty()) {
errorMessage = customErrorMessage;
}
getElement().setProperty("errorMessage", errorMessage);
}

@Override
public RadioButtonGroupDataView<T> setItems(
DataProvider<T, Void> dataProvider) {
Expand Down Expand Up @@ -795,13 +841,99 @@ public void setManualValidation(boolean enabled) {
this.manualValidationEnabled = enabled;
}

/**
* Validates the current value against the constraints and sets the
* {@code invalid} property and the {@code errorMessage} property based on
* the result. If a custom error message is provided with
* {@link #setErrorMessage(String)}, it is used. Otherwise, the error
* message defined in the i18n object is used.
* <p>
* The method does nothing if the manual validation mode is enabled.
*/
protected void validate() {
if (!this.manualValidationEnabled) {
boolean isInvalid = ValidationUtil.validateRequiredConstraint("",
isRequiredIndicatorVisible(), getValue(), getEmptyValue())
.isError();
if (this.manualValidationEnabled) {
return;
}

ValidationResult result = ValidationUtil.validateRequiredConstraint(
getI18nErrorMessage(
RadioButtonGroupI18n::getRequiredErrorMessage),
isRequiredIndicatorVisible(), getValue(), getEmptyValue());
if (result.isError()) {
setInvalid(true);
setConstraintErrorMessage(result.getErrorMessage());
} else {
setInvalid(false);
setConstraintErrorMessage("");
}
}

/**
* Gets the internationalization object previously set for this component.
* <p>
* NOTE: Updating the instance that is returned from this method will not
* update the component if not set again using
* {@link #setI18n(RadioButtonGroupI18n)}
*
* @return the i18n object or {@code null} if no i18n object has been set
*/
public RadioButtonGroupI18n getI18n() {
return i18n;
}

/**
* Sets the internationalization object for this component.
*
* @param i18n
* the i18n object, not {@code null}
*/
public void setI18n(RadioButtonGroupI18n i18n) {
this.i18n = Objects.requireNonNull(i18n,
"The i18n properties object should not be null");
}

private String getI18nErrorMessage(
Function<RadioButtonGroupI18n, String> getter) {
return Optional.ofNullable(i18n).map(getter).orElse("");
}

/**
* The internationalization properties for {@link RadioButtonGroup}.
*/
public static class RadioButtonGroupI18n implements Serializable {

private String requiredErrorMessage;

/**
* Gets the error message displayed when the field is required but
* empty.
*
* @return the error message or {@code null} if not set
* @see RadioButtonGroup#isRequiredIndicatorVisible()
* @see RadioButtonGroup#setRequiredIndicatorVisible(boolean)
*/
public String getRequiredErrorMessage() {
return requiredErrorMessage;
}

setInvalid(isInvalid);
/**
* Sets the error message to display when the field is required but
* empty.
* <p>
* Note, custom error messages set with
* {@link RadioButtonGroup#setErrorMessage(String)} take priority over
* i18n error messages.
*
* @param errorMessage
* the error message or {@code null} to clear it
* @return this instance for method chaining
* @see RadioButtonGroup#isRequiredIndicatorVisible()
* @see RadioButtonGroup#setRequiredIndicatorVisible(boolean)
*/
public RadioButtonGroupI18n setRequiredErrorMessage(
String errorMessage) {
requiredErrorMessage = errorMessage;
return this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,63 @@
*/
package com.vaadin.flow.component.radiobutton.validation;

import org.junit.Assert;
import org.junit.Test;

import com.vaadin.flow.component.radiobutton.RadioButtonGroup;
import com.vaadin.tests.validation.AbstractBasicValidationTest;

public class BasicValidationTest
extends AbstractBasicValidationTest<RadioButtonGroup<String>, String> {
@Test
public void required_validate_emptyErrorMessageDisplayed() {
testField.setRequiredIndicatorVisible(true);
testField.setValue("foo");
testField.setValue(null);
Assert.assertEquals("", getErrorMessageProperty());
}

@Test
public void required_setI18nErrorMessage_validate_i18nErrorMessageDisplayed() {
testField.setRequiredIndicatorVisible(true);
testField.setI18n(new RadioButtonGroup.RadioButtonGroupI18n()
.setRequiredErrorMessage("Field is required"));
testField.setValue("foo");
testField.setValue(null);
Assert.assertEquals("Field is required", getErrorMessageProperty());
}

@Test
public void setI18nAndCustomErrorMessage_validate_customErrorMessageDisplayed() {
testField.setRequiredIndicatorVisible(true);
testField.setI18n(new RadioButtonGroup.RadioButtonGroupI18n()
.setRequiredErrorMessage("Field is required"));
testField.setErrorMessage("Custom error message");
testField.setValue("foo");
testField.setValue(null);
Assert.assertEquals("Custom error message", getErrorMessageProperty());
}

@Test
public void setI18nAndCustomErrorMessage_validate_removeCustomErrorMessage_i18nErrorMessageDisplayed() {
testField.setRequiredIndicatorVisible(true);
testField.setI18n(new RadioButtonGroup.RadioButtonGroupI18n()
.setRequiredErrorMessage("Field is required"));
testField.setErrorMessage("Custom error message");
testField.setValue("foo");
testField.setValue(null);
testField.setErrorMessage("");
Assert.assertEquals("Field is required", getErrorMessageProperty());
}

@Override
protected RadioButtonGroup<String> createTestField() {
return new RadioButtonGroup<String>();
RadioButtonGroup<String> select = new RadioButtonGroup<>();
select.setItems("foo");
return select;
}

private String getErrorMessageProperty() {
return testField.getElement().getProperty("errorMessage");
}
}

0 comments on commit 3f01844

Please sign in to comment.