|
| 1 | +import { InputHTMLAttributes } from "react"; |
| 2 | + |
| 3 | +export type TextConstraints = Pick< |
| 4 | + InputHTMLAttributes<HTMLInputElement>, |
| 5 | + "pattern" | "required" | "minLength" | "maxLength" |
| 6 | +>; |
| 7 | + |
| 8 | +/** |
| 9 | + * Since the default validation messages can be verbose, this type is used to |
| 10 | + * configure when/how to display the native browser messages when the validation |
| 11 | + * state changes during the `change` event phase. The validation message will |
| 12 | + * always be shown on blur. |
| 13 | + * |
| 14 | + * When this is: |
| 15 | + * |
| 16 | + * - `true` -> always show the browser message when it exists |
| 17 | + * - `false` -> never show the browser message |
| 18 | + * - `"recommended"` -> only shows the browser message if it is not one of the |
| 19 | + * `RECOMMENDED_STATE_KEYS` validation errors |
| 20 | + * - `keyof ValidityState` -> only shows the browser message if it is not the |
| 21 | + * specific validation error |
| 22 | + * - `(keyof ValidityState)[]` -> only shows the browser message if it is not |
| 23 | + * the specific validation errors |
| 24 | + */ |
| 25 | +export type ChangeValidationBehavior = |
| 26 | + | boolean |
| 27 | + | "recommended" |
| 28 | + | keyof ValidityState |
| 29 | + | readonly (keyof ValidityState)[]; |
| 30 | + |
| 31 | +export interface ErrorMessageOptions extends TextConstraints { |
| 32 | + /** |
| 33 | + * The current input or textarea's validity state. |
| 34 | + */ |
| 35 | + validity: ValidityState; |
| 36 | + |
| 37 | + /** |
| 38 | + * The browser defined validation message based on the validity state. This |
| 39 | + * will be the empty string when there are no errors. |
| 40 | + */ |
| 41 | + validationMessage: string; |
| 42 | + |
| 43 | + /** |
| 44 | + * The current `TextField` or `TextArea` value. |
| 45 | + */ |
| 46 | + value: string; |
| 47 | + |
| 48 | + /** |
| 49 | + * Boolean if this is triggered from a blur event instead of a change event. |
| 50 | + */ |
| 51 | + isBlurEvent: boolean; |
| 52 | + |
| 53 | + /** |
| 54 | + * The change event validation behavior that is specified in the hook. |
| 55 | + */ |
| 56 | + validateOnChange: ChangeValidationBehavior; |
| 57 | +} |
| 58 | + |
| 59 | +/** |
| 60 | + * A function to get a custom error message for specific errors. This is really |
| 61 | + * useful when using the `pattern` attribute to give additional information or |
| 62 | + * changing the native "language translated" error message. |
| 63 | + * |
| 64 | + * @param options An object containing metadata that can be used to create an |
| 65 | + * error message for your `TextField` or `TextArea`. |
| 66 | + * @return An error message to display or an empty string. |
| 67 | + */ |
| 68 | +export type GetErrorMessage = (options: ErrorMessageOptions) => string; |
| 69 | + |
| 70 | +/** @internal */ |
| 71 | +const RECOMMENDED_STATE_KEYS: readonly (keyof ValidityState)[] = [ |
| 72 | + "valueMissing", |
| 73 | + "tooShort", |
| 74 | + "tooLong", |
| 75 | + "badInput", |
| 76 | +]; |
| 77 | + |
| 78 | +/** |
| 79 | + * The default implementation for getting an error message for the `TextField` |
| 80 | + * or `TextArea` components that: |
| 81 | + * |
| 82 | + * - prevents the browser `minLength` and `tooLong` error text from appearing |
| 83 | + * during change events since the message is extremely verbose |
| 84 | + * - prevents the `valueMissing` and `badInput` error text from appearing during |
| 85 | + * change events since it's better to wait for the blur event. |
| 86 | + * |
| 87 | + * The above behavior is also configured by the {@link ChangeValidationBehavior}. |
| 88 | + */ |
| 89 | +export const defaultGetErrorMessage: GetErrorMessage = ({ |
| 90 | + isBlurEvent, |
| 91 | + validity, |
| 92 | + validationMessage, |
| 93 | + validateOnChange, |
| 94 | +}) => { |
| 95 | + if (isBlurEvent || !validationMessage) { |
| 96 | + return validationMessage; |
| 97 | + } |
| 98 | + |
| 99 | + if (!validateOnChange) { |
| 100 | + return ""; |
| 101 | + } |
| 102 | + |
| 103 | + let keys = RECOMMENDED_STATE_KEYS; |
| 104 | + if ( |
| 105 | + typeof validateOnChange === "string" && |
| 106 | + validateOnChange !== "recommended" |
| 107 | + ) { |
| 108 | + keys = [validateOnChange]; |
| 109 | + } else if (Array.isArray(validateOnChange)) { |
| 110 | + keys = validateOnChange; |
| 111 | + } |
| 112 | + |
| 113 | + if ( |
| 114 | + Object.entries(validity).some( |
| 115 | + ([key, value]) => value && !keys.includes(key as keyof ValidityState) |
| 116 | + ) |
| 117 | + ) { |
| 118 | + return validationMessage; |
| 119 | + } |
| 120 | + |
| 121 | + return ""; |
| 122 | +}; |
0 commit comments