@freshworks/crayons
Version:
Crayons Web Components library
277 lines (276 loc) • 9.75 kB
JavaScript
/* 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);
}, {});
};