-
-
Notifications
You must be signed in to change notification settings - Fork 298
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(form): added a new
useTextField
hook to validate the TextField…
… and TextArea values
- Loading branch information
Showing
5 changed files
with
598 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { ReactNode } from "react"; | ||
|
||
/** | ||
* A function that can be used to dynamically get an error icon based on the | ||
* current visible error. | ||
* | ||
* @param errorMessage The current error message or an empty string | ||
* @param error Boolean if the `TextField` or `TextArea` are considered to be in | ||
* an errored state | ||
* @param errorIcon The current `errorIcon` that was provided to the | ||
* `useTextField` hook. | ||
* @return An icon to render or falsey to render nothing. | ||
*/ | ||
export type GetErrorIcon = ( | ||
errorMessage: string, | ||
error: boolean, | ||
errorIcon: ReactNode | ||
) => ReactNode; | ||
|
||
/** | ||
* The default implementation for showing an error icon in `TextField` and | ||
* `TextArea` components that will only display when the error flag is enabled. | ||
*/ | ||
export const defaultGetErrorIcon: GetErrorIcon = (_message, error, errorIcon) => | ||
error && errorIcon; |
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,122 @@ | ||
import { InputHTMLAttributes } from "react"; | ||
|
||
export type TextConstraints = Pick< | ||
InputHTMLAttributes<HTMLInputElement>, | ||
"pattern" | "required" | "minLength" | "maxLength" | ||
>; | ||
|
||
/** | ||
* Since the default validation messages can be verbose, this type is used to | ||
* configure when/how to display the native browser messages when the validation | ||
* state changes during the `change` event phase. The validation message will | ||
* always be shown on blur. | ||
* | ||
* When this is: | ||
* | ||
* - `true` -> always show the browser message when it exists | ||
* - `false` -> never show the browser message | ||
* - `"recommended"` -> only shows the browser message if it is not one of the | ||
* `RECOMMENDED_STATE_KEYS` validation errors | ||
* - `keyof ValidityState` -> only shows the browser message if it is not the | ||
* specific validation error | ||
* - `(keyof ValidityState)[]` -> only shows the browser message if it is not | ||
* the specific validation errors | ||
*/ | ||
export type ChangeValidationBehavior = | ||
| boolean | ||
| "recommended" | ||
| keyof ValidityState | ||
| readonly (keyof ValidityState)[]; | ||
|
||
export interface ErrorMessageOptions extends TextConstraints { | ||
/** | ||
* The current input or textarea's validity state. | ||
*/ | ||
validity: ValidityState; | ||
|
||
/** | ||
* The browser defined validation message based on the validity state. This | ||
* will be the empty string when there are no errors. | ||
*/ | ||
validationMessage: string; | ||
|
||
/** | ||
* The current `TextField` or `TextArea` value. | ||
*/ | ||
value: string; | ||
|
||
/** | ||
* Boolean if this is triggered from a blur event instead of a change event. | ||
*/ | ||
isBlurEvent: boolean; | ||
|
||
/** | ||
* The change event validation behavior that is specified in the hook. | ||
*/ | ||
validateOnChange: ChangeValidationBehavior; | ||
} | ||
|
||
/** | ||
* A function to get a custom error message for specific errors. This is really | ||
* useful when using the `pattern` attribute to give additional information or | ||
* changing the native "language translated" error message. | ||
* | ||
* @param options An object containing metadata that can be used to create an | ||
* error message for your `TextField` or `TextArea`. | ||
* @return An error message to display or an empty string. | ||
*/ | ||
export type GetErrorMessage = (options: ErrorMessageOptions) => string; | ||
|
||
/** @internal */ | ||
const RECOMMENDED_STATE_KEYS: readonly (keyof ValidityState)[] = [ | ||
"valueMissing", | ||
"tooShort", | ||
"tooLong", | ||
"badInput", | ||
]; | ||
|
||
/** | ||
* The default implementation for getting an error message for the `TextField` | ||
* or `TextArea` components that: | ||
* | ||
* - prevents the browser `minLength` and `tooLong` error text from appearing | ||
* during change events since the message is extremely verbose | ||
* - prevents the `valueMissing` and `badInput` error text from appearing during | ||
* change events since it's better to wait for the blur event. | ||
* | ||
* The above behavior is also configured by the {@link ChangeValidationBehavior}. | ||
*/ | ||
export const defaultGetErrorMessage: GetErrorMessage = ({ | ||
isBlurEvent, | ||
validity, | ||
validationMessage, | ||
validateOnChange, | ||
}) => { | ||
if (isBlurEvent || !validationMessage) { | ||
return validationMessage; | ||
} | ||
|
||
if (!validateOnChange) { | ||
return ""; | ||
} | ||
|
||
let keys = RECOMMENDED_STATE_KEYS; | ||
if ( | ||
typeof validateOnChange === "string" && | ||
validateOnChange !== "recommended" | ||
) { | ||
keys = [validateOnChange]; | ||
} else if (Array.isArray(validateOnChange)) { | ||
keys = validateOnChange; | ||
} | ||
|
||
if ( | ||
Object.entries(validity).some( | ||
([key, value]) => value && !keys.includes(key as keyof ValidityState) | ||
) | ||
) { | ||
return validationMessage; | ||
} | ||
|
||
return ""; | ||
}; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { ErrorMessageOptions } from "./getErrorMessage"; | ||
|
||
export interface IsErroredOptions extends ErrorMessageOptions { | ||
/** | ||
* The current error message or an empty string. | ||
*/ | ||
errorMessage: string; | ||
} | ||
|
||
/** | ||
* A function that is used to determine if a `TextField` or `TextArea` is in an | ||
* errored state. | ||
* | ||
* @param options All the current options that can be used to determine the | ||
* error state. | ||
* @return True if the component is considered to be in an errored state. | ||
*/ | ||
export type IsErrored = (options: IsErroredOptions) => boolean; | ||
|
||
/** | ||
* The default implementation for checking if a `TextField` or `TextArea` is | ||
* errored by returning `true` if the `errorMessage` string is truthy or the | ||
* value is not within the `minLength` and `maxLength` constraints when they | ||
* exist. | ||
*/ | ||
export const defaultIsErrored: IsErrored = ({ | ||
value, | ||
errorMessage, | ||
minLength, | ||
maxLength, | ||
}) => | ||
!!errorMessage || | ||
(typeof minLength === "number" && value.length < minLength) || | ||
(typeof maxLength === "number" && value.length > maxLength); |
Oops, something went wrong.