diff --git a/vaadin-date-picker-flow-parent/vaadin-date-picker-flow/src/main/java/com/vaadin/flow/component/datepicker/DatePicker.java b/vaadin-date-picker-flow-parent/vaadin-date-picker-flow/src/main/java/com/vaadin/flow/component/datepicker/DatePicker.java index de34cc163f2..dac11ff864a 100644 --- a/vaadin-date-picker-flow-parent/vaadin-date-picker-flow/src/main/java/com/vaadin/flow/component/datepicker/DatePicker.java +++ b/vaadin-date-picker-flow-parent/vaadin-date-picker-flow/src/main/java/com/vaadin/flow/component/datepicker/DatePicker.java @@ -56,6 +56,7 @@ import com.vaadin.flow.component.shared.InputField; import com.vaadin.flow.component.shared.ValidationUtil; import com.vaadin.flow.component.shared.internal.ValidationController; +import com.vaadin.flow.data.binder.Binder; import com.vaadin.flow.data.binder.HasValidator; import com.vaadin.flow.data.binder.ValidationResult; import com.vaadin.flow.data.binder.ValidationStatusChangeEvent; @@ -82,6 +83,46 @@ * the format of the current locale or through the date picker overlay. The * overlay opens when the field is clicked and/or any input is entered when the * field is focused. + *

Validation

+ *

+ * Date Picker comes with a built-in validation mechanism based on constraints. + * Validation is triggered whenever the user initiates a date change, for + * example by selection from the overlay or manual entry followed by Enter or + * blur. Programmatic value changes trigger validation as well. + *

+ * Validation verifies that the value is parsable into {@link LocalDate} and + * satisfies the specified constraints. If validation fails, the component is + * marked as invalid and an error message is displayed below the input. + *

+ * The following constraints are supported: + *

+ *

+ * Error messages for unparsable input and constraints can be configured with + * the {@link DatePickerI18n} object, using the respective properties. If you + * want to provide a single catch-all error message, you can also use the + * {@link #setErrorMessage(String)} method. Note that such an error message will + * take priority over i18n error messages if both are set. + *

+ * In addition to validation, constraints may also have a visual aspect. For + * example, dates before the minimum date are displayed as disabled in the + * overlay to prevent their selection. + *

+ * For more advanced validation that requires custom rules, you can use + * {@link Binder}. By default, before running custom validators, Binder will + * also check if the date is parsable and satisfies the component constraints, + * displaying error messages from the {@link DatePickerI18n} object. The + * exception is the required constraint, for which Binder provides its own API, + * see {@link Binder.BindingBuilder#asRequired(String) asRequired()}. + *

+ * However, if Binder doesn't fit your needs and you want to implement fully + * custom validation logic, you can disable the constraint validation by setting + * {@link #setManualValidation(boolean)} to true. This will allow you to control + * the invalid state and the error message manually using + * {@link #setInvalid(boolean)} and {@link #setErrorMessage(String)} API. * * @author Vaadin Ltd */ @@ -340,12 +381,29 @@ public DatePicker(LocalDate initialDate, Locale locale) { } /** - * Sets the minimum date in the date picker. Dates before that will be - * disabled in the popup. + * {@inheritDoc} + *

+ * Distinct error messages for unparsable input and different constraints + * can be configured with the {@link DatePickerI18n} object, using the + * respective properties. However, note that the error message set with + * {@link #setErrorMessage(String)} will take priority and override any i18n + * error messages if both are set. + */ + @Override + public void setErrorMessage(String errorMessage) { + HasValidationProperties.super.setErrorMessage(errorMessage); + } + + /** + * Sets the minimum date allowed to be selected for this field. Dates before + * that will be disabled in the calendar overlay. Manual entry of such dates + * will cause the component to invalidate. + *

+ * The minimum date is inclusive. * * @param min - * the minimum date that is allowed to be selected, or - * null to remove any minimum constraints + * the minimum date, or {@code null} to remove this constraint + * @see DatePickerI18n#setMinErrorMessage(String) */ public void setMin(LocalDate min) { String minAsString = FORMATTER.apply(min); @@ -354,23 +412,25 @@ public void setMin(LocalDate min) { } /** - * Gets the minimum date in the date picker. Dates before that will be - * disabled in the popup. + * Gets the minimum date allowed to be selected for this field. * - * @return the minimum date that is allowed to be selected, or - * null if there's no minimum + * @return the minimum date, or {@code null} if no minimum is set + * @see #setMax(LocalDate) */ public LocalDate getMin() { return PARSER.apply(getElement().getProperty("min")); } /** - * Sets the maximum date in the date picker. Dates after that will be - * disabled in the popup. + * Sets the maximum date allowed to be selected for this field. Dates after + * that will be disabled in the calendar overlay. Manual entry of such dates + * will cause the component to invalidate. + *

+ * The maximum date is inclusive. * * @param max - * the maximum date that is allowed to be selected, or - * null to remove any maximum constraints + * the maximum date, or {@code null} to remove this constraint + * @see DatePickerI18n#setMaxErrorMessage(String) */ public void setMax(LocalDate max) { String maxAsString = FORMATTER.apply(max); @@ -379,11 +439,10 @@ public void setMax(LocalDate max) { } /** - * Gets the maximum date in the date picker. Dates after that will be - * disabled in the popup. + * Gets the maximum date allowed to be selected for this field. * - * @return the maximum date that is allowed to be selected, or - * null if there's no maximum + * @return the maximum date, or {@code null} if no maximum is set + * @see #setMax(LocalDate) */ public LocalDate getMax() { return PARSER.apply(getElement().getProperty("max")); @@ -686,22 +745,49 @@ public LocalDate getInitialPosition() { } /** - * Sets whether the date picker is marked as input required. + * Sets whether the user is required to provide a value. When required, an + * indicator appears next to the label and the field invalidates if the + * value is cleared. + *

+ * NOTE: The required indicator is only visible when the field has a label, + * see {@link #setLabel(String)}. * * @param required - * the boolean value to set + * {@code true} to make the field required, {@code false} + * otherwise + * @see DatePickerI18n#setRequiredErrorMessage(String) + */ + @Override + public void setRequiredIndicatorVisible(boolean required) { + super.setRequiredIndicatorVisible(required); + } + + /** + * Gets whether the user is required to provide a value. + * + * @return {@code true} if the field is required, {@code false} otherwise + * @see #setRequiredIndicatorVisible(boolean) + */ + @Override + public boolean isRequiredIndicatorVisible() { + return super.isRequiredIndicatorVisible(); + } + + /** + * Alias for {@link #setRequiredIndicatorVisible(boolean)}. + * + * @param required + * {@code true} to make the field required, {@code false} + * otherwise */ public void setRequired(boolean required) { setRequiredIndicatorVisible(required); } /** - * Determines whether the datepicker is marked as input required. - *

- * This property is not synchronized automatically from the client side, so - * the returned value may not be the same as in client side. + * Alias for {@link #isRequiredIndicatorVisible()} * - * @return {@code true} if the input is required, {@code false} otherwise + * @return {@code true} if the field is required, {@code false} otherwise */ public boolean isRequired() { return isRequiredIndicatorVisible(); diff --git a/vaadin-time-picker-flow-parent/vaadin-time-picker-flow/src/main/java/com/vaadin/flow/component/timepicker/TimePicker.java b/vaadin-time-picker-flow-parent/vaadin-time-picker-flow/src/main/java/com/vaadin/flow/component/timepicker/TimePicker.java index 0967e1ec683..8d093734943 100644 --- a/vaadin-time-picker-flow-parent/vaadin-time-picker-flow/src/main/java/com/vaadin/flow/component/timepicker/TimePicker.java +++ b/vaadin-time-picker-flow-parent/vaadin-time-picker-flow/src/main/java/com/vaadin/flow/component/timepicker/TimePicker.java @@ -52,6 +52,7 @@ import com.vaadin.flow.component.shared.InputField; import com.vaadin.flow.component.shared.ValidationUtil; import com.vaadin.flow.component.shared.internal.ValidationController; +import com.vaadin.flow.data.binder.Binder; import com.vaadin.flow.data.binder.HasValidator; import com.vaadin.flow.data.binder.ValidationResult; import com.vaadin.flow.data.binder.ValidationStatusChangeEvent; @@ -67,6 +68,46 @@ * time can be entered directly using a keyboard or by choosing a value from a * set of predefined options presented in an overlay. The overlay opens when the * field is clicked or any input is entered when the field is focused. + *

Validation

+ *

+ * Time Picker comes with a built-in validation mechanism based on constraints. + * Validation is triggered whenever the user initiates a time change, for + * example by selection from the dropdown or manual entry followed by Enter or + * blur. Programmatic value changes trigger validation as well. + *

+ * Validation verifies that the value is parsable into {@link LocalTime} and + * satisfies the specified constraints. If validation fails, the component is + * marked as invalid and an error message is displayed below the input. + *

+ * The following constraints are supported: + *

+ *

+ * Error messages for unparsable input and constraints can be configured with + * the {@link TimePickerI18n} object, using the respective properties. If you + * want to provide a single catch-all error message, you can also use the + * {@link #setErrorMessage(String)} method. Note that such an error message will + * take priority over i18n error messages if both are set. + *

+ * In addition to validation, constraints may also have a visual impact. For + * example, times before the minimum time or after the maximum time are not + * displayed in the dropdown to prevent their selection. + *

+ * For more advanced validation that requires custom rules, you can use + * {@link Binder}. By default, before running custom validators, Binder will + * also check if the time is parsable and satisfies the component constraints, + * displaying error messages from the {@link TimePickerI18n} object. The + * exception is the required constraint, for which Binder provides its own API, + * see {@link Binder.BindingBuilder#asRequired(String) asRequired()}. + *

+ * However, if Binder doesn't fit your needs and you want to implement fully + * custom validation logic, you can disable the constraint validation by setting + * {@link #setManualValidation(boolean)} to true. This will allow you to control + * the invalid state and the error message manually using + * {@link #setInvalid(boolean)} and {@link #setErrorMessage(String)} API. * * @author Vaadin Ltd */ @@ -276,6 +317,20 @@ public TimePicker(String label, LocalTime time, addValueChangeListener(listener); } + /** + * {@inheritDoc} + *

+ * Distinct error messages for unparsable input and different constraints + * can be configured with the {@link TimePickerI18n} object, using the + * respective properties. However, note that the error message set with + * {@link #setErrorMessage(String)} will take priority and override any i18n + * error messages if both are set. + */ + @Override + public void setErrorMessage(String errorMessage) { + HasValidationProperties.super.setErrorMessage(errorMessage); + } + /** * Sets the label for the time picker. * @@ -404,22 +459,49 @@ protected boolean isInputValuePresent() { } /** - * Sets whether the time picker is marked as input required. + * Sets whether the user is required to provide a value. When required, an + * indicator appears next to the label and the field invalidates if the + * value is cleared. + *

+ * NOTE: The required indicator is only visible when the field has a label, + * see {@link #setLabel(String)}. * * @param required - * the boolean value to set + * {@code true} to make the field required, {@code false} + * otherwise + * @see TimePickerI18n#setRequiredErrorMessage(String) + */ + @Override + public void setRequiredIndicatorVisible(boolean required) { + super.setRequiredIndicatorVisible(required); + } + + /** + * Gets whether the user is required to provide a value. + * + * @return {@code true} if the field is required, {@code false} otherwise + * @see #setRequiredIndicatorVisible(boolean) + */ + @Override + public boolean isRequiredIndicatorVisible() { + return super.isRequiredIndicatorVisible(); + } + + /** + * Alias for {@link #setRequiredIndicatorVisible(boolean)}. + * + * @param required + * {@code true} to make the field required, {@code false} + * otherwise */ public void setRequired(boolean required) { setRequiredIndicatorVisible(required); } /** - * Determines whether the time picker is marked as input required. - *

- * This property is not synchronized automatically from the client side, so - * the returned value may not be the same as in client side. + * Alias for {@link #isRequiredIndicatorVisible()} * - * @return {@code true} if the input is required, {@code false} otherwise + * @return {@code true} if the field is required, {@code false} otherwise */ public boolean isRequired() { return isRequiredIndicatorVisible(); @@ -620,12 +702,15 @@ private void executeLocaleUpdate() { } /** - * Sets the minimum time in the time picker. Times before that will be - * disabled in the popup. + * Sets the minimum time allowed to be selected for this field. Times before + * that won't be displayed in the dropdown. Manual entry of such times will + * cause the component to invalidate. + *

+ * The minimum time is inclusive. * * @param min - * the minimum time that is allowed to be selected, or - * null to remove any minimum constraints + * the minimum time, or {@code null} to remove this constraint + * @see TimePickerI18n#setMinErrorMessage(String) */ public void setMin(LocalTime min) { this.min = min; @@ -634,23 +719,25 @@ public void setMin(LocalTime min) { } /** - * Gets the minimum time in the time picker. Time before that will be - * disabled in the popup. + * Gets the minimum time allowed to be selected for this field. * - * @return the minimum time that is allowed to be selected, or - * null if there's no minimum + * @return the minimum time, or {@code null} if no minimum is set + * @see #setMax(LocalTime) */ public LocalTime getMin() { return this.min; } /** - * Sets the maximum time in the time picker. Times after that will be - * disabled in the popup. + * Sets the maximum time allowed to be selected for this field. Times after + * that won't be displayed in the dropdown. Manual entry of such times will + * cause the component to invalidate. + *

+ * The maximum time is inclusive. * * @param max - * the maximum time that is allowed to be selected, or - * null to remove any maximum constraints + * the maximum time, or {@code null} to remove this constraint + * @see TimePickerI18n#setMaxErrorMessage(String) */ public void setMax(LocalTime max) { this.max = max; @@ -659,11 +746,10 @@ public void setMax(LocalTime max) { } /** - * Gets the maximum time in the time picker. Times after that will be - * disabled in the popup. + * Gets the maximum time allowed to be selected for this field. * - * @return the maximum time that is allowed to be selected, or - * null if there's no maximum + * @return the maximum time, or {@code null} if no maximum is set + * @see #setMin(LocalTime) */ public LocalTime getMax() { return this.max;