react-hooks-form-validator
Version:
One react hook for all your form validations
149 lines (137 loc) • 4.12 kB
JavaScript
import { getSchemaValidator } from './schema-validator';
import { PER_FIELD_SCHEMA, FIELD_CONFIG_SCHEMA } from './constants';
const fieldSchemaValidator = getSchemaValidator(FIELD_CONFIG_SCHEMA);
const perFieldSchemaValidator = getSchemaValidator(PER_FIELD_SCHEMA);
export function getDefaultValues(formFieldConfig) {
const defaultValues = {};
if (!formFieldConfig) {
return defaultValues;
}
Object.entries(formFieldConfig).forEach(([fieldName, fieldConfig]) => {
defaultValues[fieldName] = fieldConfig.defaultValue;
});
return defaultValues;
}
export function getDefaultErrors(formFieldConfig) {
const defaultErrors = {};
if (!formFieldConfig) {
return defaultErrors;
}
Object.entries(formFieldConfig).forEach(([fieldName, fieldConfig]) => {
defaultErrors[fieldName] = {
isValid: !fieldConfig.required,
};
});
return defaultErrors;
}
export function getFieldError(formState, fieldName, formFieldConfig) {
const { required, min, max, patterns, validate } = formFieldConfig[fieldName];
const fieldValue = formState[fieldName];
if (validate) {
try {
const [valid, errorMsg] = validate(formState[fieldName], formState);
if (!valid) {
return errorMsg || 'Invalid input';
}
} catch (error) {
return `Couldn't validate`;
}
}
if (!required && !fieldValue) {
return '';
}
if (required && !fieldValue) {
if (typeof required === 'object' && required.errorMsg) {
return required.errorMsg;
}
return 'This is a required field';
}
if (min) {
let minLength = min;
let errorMsg = '';
if (typeof min === 'object') {
errorMsg = min.errorMsg;
minLength = min.length;
}
if (
typeof fieldValue === 'string' &&
fieldValue.trim().length < minLength
) {
return errorMsg || `Atleast ${min} characters required`;
}
}
if (max) {
let maxLength = max;
let errorMsg = '';
if (typeof max === 'object') {
errorMsg = max.errorMsg;
maxLength = max.length;
}
if (fieldValue.trim().length > maxLength) {
return errorMsg || `Should not be more than ${max} characters`;
}
}
if (patterns) {
const failedRegex = patterns.find(
(pattern) => !pattern.regex.test(fieldValue),
);
if (failedRegex) {
return failedRegex.errorMsg || `Required pattern doesn't match`;
}
}
return '';
}
export function validatePerFieldSchema(fieldConfig) {
if (!fieldConfig) {
throw new Error(`Field configuration can't be null/undefined`);
}
const validationResult = perFieldSchemaValidator(fieldConfig);
if (validationResult !== true) {
const error = new Error('Invalid schema for field configuration');
throw error;
}
}
export function validateFieldConfig(formFieldConfig) {
if (!formFieldConfig) {
throw new Error(`Field configuration can't be null/undefined`);
}
const configs = Object.entries(formFieldConfig).map(([, config]) => config);
const validationResult = fieldSchemaValidator({ configs });
if (validationResult !== true) {
const error = new Error('Invalid schema for field configuration');
throw error;
}
}
export function getFormValidity(formErrors) {
return Object.entries(formErrors).every(
([, errorState]) => errorState.isValid,
);
}
export function getFormErrors(fieldConfig, formValues) {
const errors = {};
const allFieldsNames = Object.keys(fieldConfig);
const validationResults = allFieldsNames.map((fieldName) =>
getFieldError(formValues, fieldName, fieldConfig),
);
validationResults.forEach((errorMsg, index) => {
const fieldName = allFieldsNames[index];
errors[fieldName] = {
isValid: !errorMsg,
errorMsg,
};
});
return [getFormValidity(errors), errors];
}
export function twoLevelCopy(object) {
const newObject = {};
Object.entries(object).forEach(([key, value]) => {
newObject[key] = { ...value };
});
return newObject;
}
export function getFieldConfig(config, hasConfigRef) {
if (!hasConfigRef.current) {
return twoLevelCopy(config);
}
return null;
}