Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(DatePicker, TimePicker): explain constraint validation #6537

Merged
merged 34 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
41bec0b
docs: add brief introduction into validation
vursen Aug 12, 2024
a41a6b0
change h3 to h2
vursen Aug 13, 2024
5fbaee7
Update vaadin-date-picker-flow-parent/vaadin-date-picker-flow/src/mai…
vursen Aug 14, 2024
8a30a76
Update vaadin-date-picker-flow-parent/vaadin-date-picker-flow/src/mai…
vursen Aug 14, 2024
1eabdbc
Update vaadin-date-picker-flow-parent/vaadin-date-picker-flow/src/mai…
vursen Aug 14, 2024
8011a85
Update vaadin-date-picker-flow-parent/vaadin-date-picker-flow/src/mai…
vursen Aug 14, 2024
4cd86d5
Update vaadin-date-picker-flow-parent/vaadin-date-picker-flow/src/mai…
vursen Aug 14, 2024
8a93c9e
Update vaadin-date-picker-flow-parent/vaadin-date-picker-flow/src/mai…
vursen Aug 14, 2024
a2ef2b3
format
vursen Aug 14, 2024
b808309
add a mention of Binder
vursen Aug 14, 2024
64ad6eb
polish
vursen Aug 14, 2024
ab82b6b
format
vursen Aug 14, 2024
e470903
improve part about Binder
vursen Aug 15, 2024
fa83636
Update vaadin-date-picker-flow-parent/vaadin-date-picker-flow/src/mai…
vursen Aug 15, 2024
1232128
explain that required constraint is exception in Bindero
vursen Aug 15, 2024
7e4fa8b
polish
vursen Aug 15, 2024
36f60ce
align with text fields
vursen Aug 20, 2024
791a484
polish
vursen Aug 20, 2024
07f6127
Update vaadin-date-picker-flow-parent/vaadin-date-picker-flow/src/mai…
vursen Aug 21, 2024
857e2cb
slighly polish
vursen Aug 22, 2024
9843b90
align description
vursen Aug 22, 2024
37130e5
address code review comments
vursen Aug 26, 2024
3a8ca05
format
vursen Aug 26, 2024
b41d77f
revert unintended change
vursen Aug 26, 2024
4d319da
align with NumberField, reduce redundancy
vursen Aug 28, 2024
48dc9be
remove unnecessary JavaDoc annotations, add return annotation
vursen Aug 29, 2024
5551c58
update required docs
vursen Aug 30, 2024
d6c41a1
align with ComboBox
vursen Aug 30, 2024
ce16318
add TimePicker
vursen Sep 2, 2024
eb6b138
remove unused import
vursen Sep 2, 2024
447e78e
use 'for this field' consistently in getters and setters
vursen Sep 17, 2024
739c52c
Merge branch 'main' into improve-date-picker-validation-docs
web-padawan Sep 17, 2024
28be0ce
simplify @return annotations
vursen Sep 17, 2024
831aa4a
Merge branch 'main' into improve-date-picker-validation-docs
vursen Sep 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
* <h2>Validation</h2>
* <p>
* 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.
* <p>
* 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.
* <p>
* The following constraints are supported:
* <ul>
* <li>{@link #setRequiredIndicatorVisible(boolean)}
* <li>{@link #setMin(LocalDate)}
* <li>{@link #setMax(LocalDate)}
* </ul>
* <p>
* 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.
* <p>
* 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.
* <p>
* 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()}.
* <p>
vursen marked this conversation as resolved.
Show resolved Hide resolved
* 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
*/
Expand Down Expand Up @@ -340,12 +381,30 @@ public DatePicker(LocalDate initialDate, Locale locale) {
}

/**
* Sets the minimum date in the date picker. Dates before that will be
* disabled in the popup.
* {@inheritDoc}
* <p>
* 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 allowed date for this field. Dates before that will be
* disabled in the calendar overlay. Manual entry of dates before the
* minimum will cause the component to invalidate.
* <p>
* The minimum date is inclusive.
*
* @param min
* the minimum date that is allowed to be selected, or
* <code>null</code> to remove any minimum constraints
* @see DatePickerI18n#setMinErrorMessage(String)
*/
public void setMin(LocalDate min) {
String minAsString = FORMATTER.apply(min);
Expand All @@ -354,23 +413,27 @@ 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 allowed date for this field.
*
* @return the minimum date that is allowed to be selected, or
* <code>null</code> if there's no minimum
* @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 allowed date for this field. Dates after that will be
* disabled in the calendar overlay. Manual entry of dates after the maximum
* will cause the component to invalidate.
* <p>
* The maximum date is inclusive.
*
* @param max
* the maximum date that is allowed to be selected, or
* <code>null</code> to remove any maximum constraints
* @see DatePickerI18n#setMaxErrorMessage(String)
*/
public void setMax(LocalDate max) {
String maxAsString = FORMATTER.apply(max);
Expand All @@ -379,11 +442,11 @@ 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 allowed date for this field.
*
* @return the maximum date that is allowed to be selected, or
* <code>null</code> if there's no maximum
* @see #setMax(LocalDate)
*/
public LocalDate getMax() {
return PARSER.apply(getElement().getProperty("max"));
Expand Down Expand Up @@ -686,22 +749,47 @@ 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.
* <p>
* NOTE: The required indicator is only visible when the field has a label,
* see {@link #setLabel(String)}.
*
* @param required
* the boolean value to set
* true to make the field required, 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 true if the field is required, false otherwise
* @see #setRequiredIndicatorVisible(boolean)
*/
@Override
public boolean isRequiredIndicatorVisible() {
return super.isRequiredIndicatorVisible();
}

/**
* Alias for {@link #setRequiredIndicatorVisible(boolean)}.
*
* @param required
* true to make the field required, false otherwise
*/
public void setRequired(boolean required) {
setRequiredIndicatorVisible(required);
}

/**
* Determines whether the datepicker is marked as input required.
* <p>
* 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 true if the field is required, false otherwise
*/
public boolean isRequired() {
return isRequiredIndicatorVisible();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
* <h2>Validation</h2>
* <p>
* 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.
* <p>
* 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.
* <p>
* The following constraints are supported:
* <ul>
* <li>{@link #setRequiredIndicatorVisible(boolean)}
* <li>{@link #setMin(LocalTime)}
* <li>{@link #setMax(LocalTime)}
* </ul>
* <p>
* 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.
* <p>
* 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.
* <p>
* 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()}.
* <p>
* 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
*/
Expand Down Expand Up @@ -276,6 +317,20 @@ public TimePicker(String label, LocalTime time,
addValueChangeListener(listener);
}

/**
* {@inheritDoc}
* <p>
* 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.
*
Expand Down Expand Up @@ -404,22 +459,47 @@ 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.
* <p>
* NOTE: The required indicator is only visible when the field has a label,
* see {@link #setLabel(String)}.
*
* @param required
* the boolean value to set
* true to make the field required, 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 true if the field is required, false otherwise
* @see #setRequiredIndicatorVisible(boolean)
*/
@Override
public boolean isRequiredIndicatorVisible() {
return super.isRequiredIndicatorVisible();
}

/**
* Alias for {@link #setRequiredIndicatorVisible(boolean)}.
*
* @param required
* true to make the field required, false otherwise
*/
public void setRequired(boolean required) {
setRequiredIndicatorVisible(required);
}

/**
* Determines whether the time picker is marked as input required.
* <p>
* 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 true if the field is required, false otherwise
*/
public boolean isRequired() {
return isRequiredIndicatorVisible();
Expand Down Expand Up @@ -620,12 +700,16 @@ private void executeLocaleUpdate() {
}

/**
* Sets the minimum time in the time picker. Times before that will be
* disabled in the popup.
* Sets the minimum allowed time for this field. Times before that won't be
* displayed in the dropdown. Manual entry of times before the minimum will
* cause the component to invalidate.
* <p>
* The minimum time is inclusive.
*
* @param min
* the minimum time that is allowed to be selected, or
* <code>null</code> to remove any minimum constraints
* @see TimePickerI18n#setMinErrorMessage(String)
*/
public void setMin(LocalTime min) {
this.min = min;
Expand All @@ -634,23 +718,27 @@ 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 allowed time for this field.
*
* @return the minimum time that is allowed to be selected, or
* <code>null</code> if there's no minimum
* @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 allowed time for this field. Times after that won't be
* displayed in the dropdown. Manual entry of times after the maximum will
* cause the component to invalidate.
* <p>
* The maximum time is inclusive.
*
* @param max
* the maximum time that is allowed to be selected, or
* <code>null</code> to remove any maximum constraints
* @see TimePickerI18n#setMaxErrorMessage(String)
*/
public void setMax(LocalTime max) {
this.max = max;
Expand All @@ -659,11 +747,11 @@ 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 allowed time for this field.
*
* @return the maximum time that is allowed to be selected, or
* <code>null</code> if there's no maximum
* @see #setMin(LocalTime)
*/
public LocalTime getMax() {
return this.max;
Expand Down