UNPKG

@freshworks/crayons

Version:
277 lines (276 loc) 9.75 kB
/* eslint-disable no-case-declarations */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import isPlainObject from 'lodash/isPlainObject'; import clone from 'lodash/clone'; import toPath from 'lodash/toPath'; import * as Yup from 'yup'; import { TranslationController } from '../../global/Translation'; export const isSelectType = (type) => !!type && type === 'select'; export const getElementValue = (_type, event, result) => { let value = result && result.value; if (!result) { value = event && event.target && event.target.value; } return value; }; /** * Recursively prepare values. */ export function prepareDataForValidation(values) { const data = Array.isArray(values) ? [] : {}; for (const k in values) { if (Object.prototype.hasOwnProperty.call(values, k)) { const key = String(k); if (Array.isArray(values[key]) === true) { data[key] = values[key].map((value) => { if (Array.isArray(value) === true || isPlainObject(value)) { return prepareDataForValidation(value); } else { return value !== '' ? value : undefined; } }); } else if (isPlainObject(values[key])) { data[key] = prepareDataForValidation(values[key]); } else { data[key] = values[key] !== '' ? values[key] : undefined; } } } return data; } export function validateYupSchema(values, schema) { const validateData = prepareDataForValidation(values); return schema['validate'](validateData, { abortEarly: false, }); } export function yupToFormErrors(yupError) { let errors = {}; if (yupError.inner) { if (yupError.inner.length === 0) { return setIn(errors, yupError.path, yupError.message); } for (const err of yupError.inner) { if (!getIn(errors, err.path)) { errors = setIn(errors, err.path, err.message); } } } return errors || {}; } /** @private is the given object an Object? */ export const isObject = (obj) => obj !== null && typeof obj === 'object'; /** @private is the given object an integer? */ export const isInteger = (obj) => String(Math.floor(Number(obj))) === obj; /** * Deeply get a value from an object via its path. */ export function getIn(obj, key, def, p = 0) { const path = toPath(key); while (obj && p < path.length) { obj = obj[path[p++]]; } return obj === undefined ? def : obj; } /** set values recursively on the object based on the given path */ export function setIn(obj, path, value) { const res = clone(obj); let resVal = res; let i = 0; const pathArray = toPath(path); for (; i < pathArray.length - 1; i++) { const currentPath = pathArray[i]; const currentObj = getIn(obj, pathArray.slice(0, i + 1)); if (currentObj && (isObject(currentObj) || Array.isArray(currentObj))) { resVal = resVal[currentPath] = clone(currentObj); } else { const nextPath = pathArray[i + 1]; resVal = resVal[currentPath] = isInteger(nextPath) && Number(nextPath) >= 0 ? [] : {}; } } // Return original object if new value is the same as current if ((i === 0 ? obj : resVal)[pathArray[i]] === value) { return obj; } if (value === undefined) { delete resVal[pathArray[i]]; } else { resVal[pathArray[i]] = value; } // If the path array has a single element, the loop did not run. // Deleting on `resVal` had no effect in this scenario, so we delete on the result instead. if (i === 0 && value === undefined) { delete res[pathArray[i]]; } return res; } /** * Recursively a set the same value for all keys and arrays nested object, cloning * @param object * @param value * @param visited * @param response */ export function setNestedObjectValues(object, value, visited = new WeakMap(), response = {}) { for (const k of Object.keys(object)) { const val = object[k]; if (isObject(val)) { if (!visited.get(val)) { visited.set(val, true); // In order to keep array values consistent for both dot path and // bracket syntax, we need to check if this is an array so that // this will output { friends: [true] } and not { friends: { "0": true } } response[k] = Array.isArray(val) ? [] : {}; setNestedObjectValues(val, value, visited, response[k]); } } else { response[k] = value; } } return response; } function mergeSchema(first = {}, second = {}) { return first.concat(second); } function createYupSchema(schema, config) { const { type, required, name } = config; let yupType; switch (type) { case 'TEXT': case 'PARAGRAPH': case 'DATE': case 'TIME': case 'RADIO': case 'EMAIL': case 'TEL': case 'URL': case 'DROPDOWN': yupType = 'string'; break; case 'MULTI_SELECT': case 'RELATIONSHIP': yupType = 'array'; break; case 'NUMBER': case 'DECIMAL': yupType = 'number'; break; case 'CHECKBOX': yupType = 'boolean'; break; default: yupType = 'string'; } if (!Yup[yupType]) { return schema; } const yupMethod = yupType; let validator = Yup[yupMethod]; validator = validator().nullable(); if (required) validator = validator['required']('form.required'); else validator = validator['notRequired'](); if (type === 'URL') validator = validator['url']('form.invalidUrl'); if (type === 'EMAIL') validator = validator['email']('form.invalidEmail'); if (type === 'NUMBER') validator = validator['integer']('form.invalidNumber'); if (type === 'CHECKBOX' && required) validator = validator['oneOf']([true], `form.required`); if ((type === 'DROPDOWN' || type === 'MULTI_SELECT' || type === 'RELATIONSHIP') && required) validator = validator.min(1, `form.required`); if (type === 'RELATIONSHIP') validator = validator.transform((_value, originalVal) => { return Array.isArray(originalVal) ? originalVal : originalVal !== '' && originalVal !== null && originalVal !== undefined ? [originalVal] : []; }); schema[name] = validator; return schema; } export const generateDynamicValidationSchema = (formSchema = {}, validationSchema = {}) => { var _a; const yupSchema = (_a = formSchema === null || formSchema === void 0 ? void 0 : formSchema.fields) === null || _a === void 0 ? void 0 : _a.reduce(createYupSchema, {}); const dynamicValidationSchema = yupSchema && Yup.object().shape(yupSchema); const formValidationSchema = mergeSchema(dynamicValidationSchema || Yup.object(), validationSchema && Object.keys(validationSchema).length ? validationSchema : Yup.object()); return formValidationSchema; }; export const generateDynamicInitialValues = (formSchema, initialValues = {}) => { var _a; const dynamicInitialValues = ((_a = formSchema === null || formSchema === void 0 ? void 0 : formSchema.fields) === null || _a === void 0 ? void 0 : _a.reduce((acc, field) => { return Object.assign(Object.assign({}, acc), { [field.name]: field.type === 'CHECKBOX' ? false : undefined }); }, {})) || {}; const formInitialValues = Object.assign(Object.assign({}, dynamicInitialValues), initialValues); return formInitialValues; }; export const serializeForm = (values = {}, fields = {}) => { let newValues = Object.assign({}, values); newValues = Object.entries(newValues).reduce((acc, [key, val]) => { var _a; const type = (_a = fields[key]) === null || _a === void 0 ? void 0 : _a.type; switch (type) { case 'NUMBER': case 'DECIMAL': const parsed = parseFloat(val); return Object.assign(Object.assign({}, acc), { [key]: isNaN(parsed) ? undefined : parsed }); case 'DATE': if (!val) return Object.assign(Object.assign({}, acc), { [key]: undefined }); const date = new Date(val); if (date.toString() === 'Invalid Date') { return Object.assign(Object.assign({}, acc), { [key]: undefined }); } const year = date.getFullYear(); let month = date.getMonth() + 1; let dt = date.getDate(); /** prepend 0 if the date/month is less than 10 */ dt = ('0' + dt).slice(-2); month = ('0' + month).slice(-2); return Object.assign(Object.assign({}, acc), { [key]: `${year}-${month}-${dt}` }); case 'RELATIONSHIP': if (Array.isArray(val) && typeof val[0] === 'object') { if (val.length > 1) { // multiselect return Object.assign(Object.assign({}, acc), { [key]: val === null || val === void 0 ? void 0 : val.map((v) => v.value) }); } return Object.assign(Object.assign({}, acc), { [key]: val === null || val === void 0 ? void 0 : val.map((v) => v.value)[0] }); } return Object.assign(Object.assign({}, acc), { [key]: val }); default: return Object.assign(Object.assign({}, acc), { [key]: val }); } }, {}); return newValues; }; export const translateErrors = async (errors = {}, fields) => { var _a; if (!errors) return {}; return (_a = Object.keys(errors)) === null || _a === void 0 ? void 0 : _a.reduce((acc, key) => { var _a, _b; if (key && errors[key]) { return Object.assign(Object.assign({}, acc), { [key]: TranslationController.t(errors[key], { field: ((_a = fields === null || fields === void 0 ? void 0 : fields[key]) === null || _a === void 0 ? void 0 : _a.label) || ((_b = fields === null || fields === void 0 ? void 0 : fields[key]) === null || _b === void 0 ? void 0 : _b.name) || '', }) }); } return Object.assign({}, acc); }, {}); };