houdini/app/javascript/hooks/useYup.ts

115 lines
3 KiB
TypeScript
Raw Permalink Normal View History

// License: LGPL-3.0-or-later
import get from 'lodash/get';
import set from 'lodash/set';
import { useEffect } from 'react';
import { useIntl } from '../components/intl';
import type { IntlShape } from '../components/intl';
import * as yup from 'yup';
/**
* Simply yup but with the default validation messages set to the current locale from `IntlProvider`
* @description
* @date 2020-11-05
* @export
* @returns yup
*/
export default function useYup(): typeof yup {
const intl = useIntl();
const { locale, messages } = intl;
useEffect(() => {
yup.setLocale(generateYupLocale(messages));
}, [locale, messages]);
return yup;
}
function generateYupLocale(messages: IntlShape['messages']) : typeof yupValidationMessagesToTranslationKeys {
const newLocale:Partial<typeof yupValidationMessagesToTranslationKeys> = {};
for (const childKey in yupValidationMessagesToTranslationKeys) {
const child = get(yupValidationMessagesToTranslationKeys, childKey);
const newChild= {};
for (const subchildKey in child) {
const subchild = get(child, subchildKey);
const result = ((messages[subchild] as string) || subchild).replace(/{/gi, "${");
set(newChild, subchildKey, result);
}
set(newLocale, childKey, newChild);
}
return newLocale as typeof yupValidationMessagesToTranslationKeys;
}
const yupValidationMessagesToTranslationKeys = {
mixed: {
default: "yup.mixed.default",
required: "yup.mixed.required",
oneOf: "yup.mixed.oneOf",
notOneOf: "yup.mixed.notOneOf",
},
string: {
length: "yup.string.length",
min: "yup.string.min",
max: "yup.string.max",
matches: "yup.string.regex",
email: "yup.string.email",
url: "yup.string.url",
uuid: "yup.string.uuid",
trim: "yup.string.trim",
lowercase: "yup.string.lowercase",
uppercase: "yup.string.uppercase",
},
number: {
min: "yup.number.min",
max: "yup.string.max",
lessThan: "yup.number.lessThan",
moreThan: "yup.number.moreThan",
positive: "yup.number.postive",
negative: "yup.number.negative",
integer: "yup.number.integer",
},
date: {
min: "yup.date.min",
max: "yup.data.max",
},
boolean: {
},
object: {
noUnknown: "yup.object.noUnknown",
},
array: {
min: "yup.array.min",
max: "yup.array.max",
},
};
type RequiredValueProps = {path:string};
/**
* A spec
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type TestOptionsMessageFn<Extra extends Record<string, any> = Record<string, any>, R = any> =
| ((params: Extra & Partial<yup.TestMessageParams> & RequiredValueProps) => R);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type ToPropResult<Extra extends Record<string, any> = Record<string, any>> = TestOptionsMessageFn<Extra>;
/**
* A function used for improving typescript safety when manually creating your
* own validation messages
* @param fn a function
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function createMessage<Extra extends Record<string, any> = Record<string, any>>(fn: ToPropResult<Extra>): ToPropResult<Extra> {
return fn;
}