UNPKG

ra-core

Version:

Core components of react-admin, a frontend Framework for building admin applications on top of REST services, using ES6, React

219 lines 7.7 kB
import lodashMemoize from 'lodash/memoize.js'; /* @link http://stackoverflow.com/questions/46155/validate-email-address-in-javascript */ const EMAIL_REGEX = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; // eslint-disable-line no-useless-escape export const isEmpty = (value) => typeof value === 'undefined' || value === null || value === '' || (Array.isArray(value) && value.length === 0); // type predicate, see https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates function isValidationErrorMessageWithArgs(error) { return error ? error.hasOwnProperty('message') : false; } const getMessage = (message, messageArgs, value, values) => typeof message === 'function' ? message({ args: messageArgs, value, values, }) : messageArgs ? { message, args: messageArgs, } : message; // If we define validation functions directly in JSX, it will // result in a new function at every render, and then trigger infinite re-render. // Hence, we memoize every built-in validator to prevent a "Maximum call stack" error. const memoize = (fn) => lodashMemoize(fn, (...args) => JSON.stringify(args)); const isFunction = value => typeof value === 'function'; export const combine2Validators = (validator1, validator2) => { return (value, values, meta) => { const result1 = validator1(value, values, meta); if (!result1) { return validator2(value, values, meta); } if (typeof result1 === 'string' || isValidationErrorMessageWithArgs(result1)) { return result1; } return result1.then(resolvedResult1 => { if (!resolvedResult1) { return validator2(value, values, meta); } return resolvedResult1; }); }; }; // Compose multiple validators into a single one for use with react-hook-form export const composeValidators = (...validators) => { const allValidators = (Array.isArray(validators[0]) ? validators[0] : validators).filter(isFunction); return allValidators.reduce(combine2Validators, () => null); }; // Compose multiple validators into a single one for use with react-hook-form export const composeSyncValidators = (...validators) => (value, values, meta) => { const allValidators = (Array.isArray(validators[0]) ? validators[0] : validators).filter(isFunction); for (const validator of allValidators) { const error = validator(value, values, meta); if (error) { return error; } } }; /** * Required validator * * Returns an error if the value is null, undefined, or empty * * @param {string|Function} message * * @example * * const titleValidators = [required('The title is required')]; * <TextInput name="title" validate={titleValidators} /> */ export const required = memoize((message = 'ra.validation.required') => Object.assign((value, values) => isEmpty(value) ? getMessage(message, undefined, value, values) : undefined, { isRequired: true })); /** * Minimum length validator * * Returns an error if the value has a length less than the parameter * * @param {integer} min * @param {string|Function} message * * @example * * const passwordValidators = [minLength(10, 'Should be at least 10 characters')]; * <TextInput type="password" name="password" validate={passwordValidators} /> */ export const minLength = memoize((min, message = 'ra.validation.minLength') => (value, values) => !isEmpty(value) && value.length < min ? getMessage(message, { min }, value, values) : undefined); /** * Maximum length validator * * Returns an error if the value has a length higher than the parameter * * @param {integer} max * @param {string|Function} message * * @example * * const nameValidators = [maxLength(10, 'Should be at most 10 characters')]; * <TextInput name="name" validate={nameValidators} /> */ export const maxLength = memoize((max, message = 'ra.validation.maxLength') => (value, values) => !isEmpty(value) && value.length > max ? getMessage(message, { max }, value, values) : undefined); /** * Minimum validator * * Returns an error if the value is less than the parameter * * @param {integer} min * @param {string|Function} message * * @example * * const fooValidators = [minValue(5, 'Should be more than 5')]; * <NumberInput name="foo" validate={fooValidators} /> */ export const minValue = memoize((min, message = 'ra.validation.minValue') => (value, values) => !isEmpty(value) && value < min ? getMessage(message, { min }, value, values) : undefined); /** * Maximum validator * * Returns an error if the value is higher than the parameter * * @param {integer} max * @param {string|Function} message * * @example * * const fooValidators = [maxValue(10, 'Should be less than 10')]; * <NumberInput name="foo" validate={fooValidators} /> */ export const maxValue = memoize((max, message = 'ra.validation.maxValue') => (value, values) => !isEmpty(value) && value > max ? getMessage(message, { max }, value, values) : undefined); /** * Number validator * * Returns an error if the value is not a number * * @param {string|Function} message * * @example * * const ageValidators = [number('Must be a number')]; * <TextInput name="age" validate={ageValidators} /> */ export const number = memoize((message = 'ra.validation.number') => (value, values) => !isEmpty(value) && isNaN(Number(value)) ? getMessage(message, undefined, value, values) : undefined); /** * Regular expression validator * * Returns an error if the value does not match the pattern given as parameter * * @param {RegExp} pattern * @param {string|Function} message * * @example * * const zipValidators = [regex(/^\d{5}(?:[-\s]\d{4})?$/, 'Must be a zip code')]; * <TextInput name="zip" validate={zipValidators} /> */ export const regex = lodashMemoize((pattern, message = 'ra.validation.regex') => (value, values) => !isEmpty(value) && typeof value === 'string' && !pattern.test(value) ? getMessage(message, { pattern }, value, values) : undefined, (pattern, message) => { return pattern.toString() + message; }); /** * Email validator * * Returns an error if the value is not a valid email * * @param {string|Function} message * * @example * * const emailValidators = [email('Must be an email')]; * <TextInput name="email" validate={emailValidators} /> */ export const email = memoize((message = 'ra.validation.email') => regex(EMAIL_REGEX, message)); const oneOfTypeMessage = ({ args }) => ({ message: 'ra.validation.oneOf', args, }); /** * Choices validator * * Returns an error if the value is not among the list passed as parameter * * @param {array} list * @param {string|Function} message * * @example * * const genderValidators = [choices(['male', 'female'], 'Must be either Male or Female')]; * <TextInput name="gender" validate={genderValidators} /> */ export const choices = memoize((list, message = oneOfTypeMessage) => (value, values) => !isEmpty(value) && list.indexOf(value) === -1 ? getMessage(message, { list }, value, values) : undefined); /** * Given a validator, returns a boolean indicating whether the field is required or not. */ export const isRequired = validate => { if (validate && validate.isRequired) { return true; } if (Array.isArray(validate)) { return !!validate.find(it => it.isRequired); } return false; }; //# sourceMappingURL=validate.js.map