mirror of
https://github.com/chenasraf/formplex-react.git
synced 2026-05-17 17:48:11 +00:00
feat: add validator export
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
# Changelog
|
||||
|
||||
## v0.1.4
|
||||
|
||||
- feat: add `validator` export with built-in validators for easy use
|
||||
|
||||
## v0.1.3
|
||||
|
||||
- feat: add `validate` method
|
||||
|
||||
56
README.md
56
README.md
@@ -51,59 +51,15 @@ const { field, handleSubmit, isValid, errors, state, rawState, setValue, setValu
|
||||
Use `field()` from the previous hook on your inputs, should support most input types:
|
||||
|
||||
```tsx
|
||||
// you can import some built-in validators, or create your own
|
||||
import { validator } from 'formplex-react'
|
||||
|
||||
<input type="text" {...field('firstName', { required: true, minLength: 2 })} />
|
||||
<input type="number" {...field('age', {
|
||||
required: true,
|
||||
validate: (n) => n < 18 ? "Must be 18 or over" : null,
|
||||
parse: Number,
|
||||
})} />
|
||||
<select {...field('gender', { required: true })}>
|
||||
...
|
||||
</select>
|
||||
```
|
||||
|
||||
## Quick-start
|
||||
|
||||
See the [full documentation](https://chenasraf.github.io/formplex-react/) for all the available
|
||||
options, return values and more examples.
|
||||
|
||||
### Use the hook
|
||||
|
||||
Start by calling the hook, passing in any options you would like for the form, and get the return
|
||||
values as needed.
|
||||
|
||||
This is a full example of a hook usage with all the available options and return values. All options
|
||||
are optional, see the docs for each for more information.
|
||||
|
||||
```tsx
|
||||
const { field, handleSubmit, isValid, errors, state, rawState, setValue, setValues } =
|
||||
useForm<MyFormData>({
|
||||
initialState: {
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
},
|
||||
autoValidateBehavior: 'onChange',
|
||||
errorMessages: {
|
||||
required: 'This field is required',
|
||||
minLength: (n) => `Must be more than ${n} chars long`,
|
||||
maxLength: (n) => `Must be less than ${n} chars long`,
|
||||
},
|
||||
onSubmit(values, e) {
|
||||
console.log('Form submitted:', values)
|
||||
fetch('/submit', { method: 'POST', body: JSON.stringify(values) })
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Register an input
|
||||
|
||||
Use `field()` from the previous hook on your inputs, should support most input types:
|
||||
|
||||
```tsx
|
||||
<input type="text" {...field('firstName', { required: true, minLength: 2 })} />
|
||||
<input type="number" {...field('age', {
|
||||
required: true,
|
||||
validate: (n) => n < 18 ? "Must be 18 or over" : null,
|
||||
validate: validator.min(18, 'Must be 18 or over'),
|
||||
// You can implement the above validator yourself like this:
|
||||
// validate: (n) => n < 18 ? "Must be 18 or over" : null,
|
||||
parse: Number,
|
||||
})} />
|
||||
<select {...field('gender', { required: true })}>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "formplex-react",
|
||||
"version": "0.1.3",
|
||||
"version": "0.1.4",
|
||||
"description": "Incredibly easy & flexible React form hooks",
|
||||
"keywords": [
|
||||
"react",
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './use-form'
|
||||
export * from './types'
|
||||
export * as validator from './validators'
|
||||
|
||||
122
src/types.ts
122
src/types.ts
@@ -118,7 +118,6 @@ export interface UseFormReturn<T> {
|
||||
*/
|
||||
validate(): boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for every field. See each property for more information.
|
||||
*/
|
||||
@@ -186,17 +185,16 @@ export interface FieldOptions<T, K extends keyof T = keyof T> {
|
||||
* @see {@link UseFormOptions.errorMessages | UseFormOptions.errorMessages} for global error messages for the default validation methods
|
||||
* @see {@link FieldOptions.errorMessages} for field-specific error messages for the default validation methods
|
||||
*/
|
||||
validate?: (value: T[K]) => string | undefined | null
|
||||
validate?: Validator<T[K]>
|
||||
|
||||
/**
|
||||
* A custom parser for the field. This will be called when the field is updated, and will cause the
|
||||
* {@link UseFormReturn.state} object to be updated with the result of this function.
|
||||
*
|
||||
* {@link UseFormReturn.rawState | UseFormReturn.rawState} will always contain the raw value of the field as it was
|
||||
* placed here.
|
||||
* Parse the value of the field before it is set in the form state.
|
||||
*
|
||||
* @param value The value of the field.
|
||||
* @returns The parsed value of the field.
|
||||
* @returns The parsed value.
|
||||
*
|
||||
* @see {@link UseFormReturn.state} for the parsed form state
|
||||
* @see {@link UseFormReturn.rawState} for the raw form state
|
||||
*/
|
||||
parse?: (value: string) => T[K]
|
||||
|
||||
@@ -253,6 +251,69 @@ export interface ErrorMessage {
|
||||
message: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation function for a field.
|
||||
*
|
||||
* @typeParam T - The type of the form field.
|
||||
*/
|
||||
export type Validator<T = unknown> = (value: T) => string | undefined | null
|
||||
|
||||
/**
|
||||
* A function that receives the validation argument of a field, such as the minimum length or the regular expression,
|
||||
* and returns the error message for the field.
|
||||
*
|
||||
* @typeParam T The type of the validation argument (e.g. `minLength` has `number` to represent the minimum length).
|
||||
* @param validation The validation that was not met.
|
||||
*/
|
||||
export type MessageResolver<T> = (validation: T) => string
|
||||
|
||||
/**
|
||||
* Map of custom error messages for the default validation methods.
|
||||
*
|
||||
* @see {@link UseFormOptions.errorMessages | UseFormOptions.errorMessages} for global error messages for the default validation methods
|
||||
* @see {@link FieldOptions.errorMessages | FieldOptions.errorMessages} for field-specific error messages for the default validation methods
|
||||
*/
|
||||
export interface ErrorStrings {
|
||||
/**
|
||||
* Error message for when the field is required but missing.
|
||||
*
|
||||
* Default: `"Required"`
|
||||
*
|
||||
* @see {@link FieldOptions.required | FieldOptions.required} for defining a field as required
|
||||
*/
|
||||
required: string
|
||||
|
||||
/**
|
||||
* Error message for when the field length is too short.
|
||||
*
|
||||
* Can either be a string, or a function resolving to a string.
|
||||
*
|
||||
* Default:
|
||||
* ```ts
|
||||
* (n) => `Must be at least ${n} characters long`
|
||||
* ```
|
||||
*
|
||||
* @see {@link FieldOptions.minLength | FieldOptions.minLength} for defining a minimum length for the field
|
||||
* @see {@link MessageResolver} for the function signature
|
||||
*/
|
||||
minLength: string | MessageResolver<number>
|
||||
|
||||
/**
|
||||
* Error message for when the field length is too long.
|
||||
*
|
||||
* Can either be a string, or a function resolving to a string.
|
||||
*
|
||||
* Default:
|
||||
* ```ts
|
||||
* (n) => `Must be no more than ${n} characters long`
|
||||
* ```
|
||||
*
|
||||
* @see {@link FieldOptions.maxLength | FieldOptions.maxLength} for defining a maximum length for the field
|
||||
* @see {@link MessageResolver} for the function signature
|
||||
*/
|
||||
maxLength: string | MessageResolver<number>
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export type FieldReturn<E> = {
|
||||
@@ -285,48 +346,3 @@ export type ChangeEvent = {
|
||||
}
|
||||
/** @hidden */
|
||||
export type BlurEvent = ChangeEvent
|
||||
|
||||
/**
|
||||
* Map of custom error messages for the default validation methods.
|
||||
*
|
||||
* @see {@link UseFormOptions.errorMessages | UseFormOptions.errorMessages} for global error messages for the default validation methods
|
||||
* @see {@link FieldOptions.errorMessages | FieldOptions.errorMessages} for field-specific error messages for the default validation methods
|
||||
*/
|
||||
export interface ErrorStrings {
|
||||
/**
|
||||
* Error message for when the field is required but missing.
|
||||
*
|
||||
* Default: `"Required"`
|
||||
*
|
||||
* @see {@link FieldOptions.required | FieldOptions.required} for defining a field as required
|
||||
*/
|
||||
required: string
|
||||
|
||||
/**
|
||||
* Error message for when the field length is too short.
|
||||
*
|
||||
* Can either be a string, or a function resolving to a string.
|
||||
*
|
||||
* Default:
|
||||
* ```ts
|
||||
* (n) => `Must be at least ${n} characters long`
|
||||
* ```
|
||||
*
|
||||
* @see {@link FieldOptions.minLength | FieldOptions.minLength} for defining a minimum length for the field
|
||||
*/
|
||||
minLength: string | ((length: number) => string)
|
||||
|
||||
/**
|
||||
* Error message for when the field length is too long.
|
||||
*
|
||||
* Can either be a string, or a function resolving to a string.
|
||||
*
|
||||
* Default:
|
||||
* ```ts
|
||||
* (n) => `Must be no more than ${n} characters long`
|
||||
* ```
|
||||
*
|
||||
* @see {@link FieldOptions.maxLength | FieldOptions.maxLength} for defining a maximum length for the field
|
||||
*/
|
||||
maxLength: string | ((length: number) => string)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
UseFormOptions,
|
||||
UseFormReturn,
|
||||
} from './types'
|
||||
import { parseStr } from './utils'
|
||||
|
||||
/**
|
||||
* The main hook for using forms. See each option and return property for more information
|
||||
@@ -60,9 +61,6 @@ export function useForm<T>({
|
||||
value: T[K],
|
||||
options: FieldOptions<T, K>,
|
||||
): ErrorMessage | undefined {
|
||||
const parseStr = (o: string | ((...args: unknown[]) => string), val: unknown) =>
|
||||
typeof o === 'function' ? o(val) : o
|
||||
|
||||
const errorStrings = {
|
||||
...errorMessages,
|
||||
...(options?.errorMessages ?? {}),
|
||||
|
||||
6
src/utils.ts
Normal file
6
src/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { MessageResolver } from './types'
|
||||
|
||||
/** @hidden */
|
||||
export function parseStr<T>(o: string | MessageResolver<T>, val: T) {
|
||||
return typeof o === 'function' ? o(val) : o
|
||||
}
|
||||
72
src/validators.ts
Normal file
72
src/validators.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { MessageResolver, Validator } from './types'
|
||||
import { parseStr } from './utils'
|
||||
|
||||
/**
|
||||
* Combine multiple validators into one. The first validator to return an error message will be used.
|
||||
*
|
||||
* @param validators - The validators to combine.
|
||||
* @returns A validator that combines the given validators.
|
||||
*/
|
||||
export function combine(...validators: Validator[]): Validator {
|
||||
return (value: unknown) => {
|
||||
for (const validator of validators) {
|
||||
const error = validator(value)
|
||||
if (error) {
|
||||
return error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a validator that checks if the value is at or above a certain number.
|
||||
* @param n The number to check against.
|
||||
* @param message The error message to use if the value is below `n`.
|
||||
* @returns A validator that checks if the value is `n` or above.
|
||||
*/
|
||||
export function min(n: number, message: string | MessageResolver<number>): Validator<number> {
|
||||
return (value) => (value < n ? parseStr(message, n) : null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a validator that checks if the value is at or below a certain number.
|
||||
* @param n The number to check against.
|
||||
* @param message The error message to use if the value is above `n`.
|
||||
* @returns A validator that checks if the value is `n` or below.
|
||||
*/
|
||||
export function max(n: number, message: string | MessageResolver<number>): Validator<number> {
|
||||
return (value) => (value > n ? parseStr(message, n) : null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a validator that checks if the string is no less than `n` characters long.
|
||||
* @param n The minimum length of the string.
|
||||
* @param message The error message to use if the string is too short.
|
||||
* @returns A validator that checks if the string is at least `n` characters long.
|
||||
*/
|
||||
export function minLength(n: number, message: string | MessageResolver<number>): Validator<string> {
|
||||
return (value) => (value.length < n ? parseStr(message, n) : null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a validator that checks if the string is no more than `n` characters long.
|
||||
* @param n The maximum length of the string.
|
||||
* @param message The error message to use if the string is too long.
|
||||
* @returns A validator that checks if the string is at most `n` characters long.
|
||||
*/
|
||||
export function maxLength(n: number, message: string | MessageResolver<number>): Validator<string> {
|
||||
return (value) => (value.length > n ? parseStr(message, n) : null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a validator that checks if the string matches a regular expression.
|
||||
* @param regex The regular expression to match against.
|
||||
* @param message The error message to use if the string does not match the regular expression.
|
||||
* @returns A validator that checks if the string matches the regular expression.
|
||||
*/
|
||||
export function pattern(
|
||||
regex: RegExp,
|
||||
message: string | MessageResolver<RegExp>,
|
||||
): Validator<string> {
|
||||
return (value) => (regex.test(value) ? null : parseStr(message, regex))
|
||||
}
|
||||
Reference in New Issue
Block a user