@adyen/kyc-components
Version:
This guide assumes that you have already an account with Adyen. A legalEntity needs to be created, and you need to have a `legalEntityId` to instatiate a Component.
503 lines (502 loc) • 15.4 kB
JavaScript
try {
let e = "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof globalThis ? globalThis : "undefined" != typeof self ? self : {}, n = new e.Error().stack;
n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "2e46a393-5c19-4eba-9afc-5efa8239ee15", e._sentryDebugIdIdentifier = "sentry-dbid-2e46a393-5c19-4eba-9afc-5efa8239ee15");
} catch (e) {}
import { o as createLogger } from "./translation-BFxyJ1c5.js";
import { i as doArraysMatch } from "./useAnalyticsContext-BVFDMrVE.js";
import { y as isEmpty } from "./validatorUtils-DRapRJ6z.js";
import { i as omitKeys, n as addKeys, t as ValidationResult } from "./validationResult-D3sPVzMw.js";
import { useCallback, useEffect, useMemo, useReducer, useState } from "preact/hooks";
//#region src/hooks/useAsyncValidator.ts
var logger = createLogger();
var useAsyncValidator = (asyncRules) => {
const [asyncValidationResults, setAsyncValidationResults] = useState({});
const clearAsyncValidationResults = useCallback((field) => {
setAsyncValidationResults((existingResults) => ({
...existingResults,
[field]: void 0
}));
}, []);
return {
asyncValidationResults,
triggerAsyncValidation: useCallback((field, formState, mode) => {
const rule = asyncRules?.[field];
if (!rule || !rule.modes.includes(mode)) {
clearAsyncValidationResults(field);
return;
}
const value = formState.data[field];
return rule.asyncValidate(formState.data[field], { state: formState }).then((isValid) => {
setAsyncValidationResults((existingResults) => ({
...existingResults,
[field]: new ValidationResult([{
isValid,
errorMessage: typeof rule.errorMessage === "function" ? rule.errorMessage(value, { state: formState }) : rule.errorMessage,
hasError: !isValid
}])
}));
}).catch(logger.error);
}, [asyncRules, clearAsyncValidationResults]),
clearAsyncValidationResults
};
};
//#endregion
//#region src/hooks/useStaticValidator/useStaticValidator.ts
var useStaticValidator = (rules) => {
const getRulesForField = useCallback((field) => {
const fieldRules = rules?.[field] ?? [{
validate: () => true,
modes: ["blur", "input"]
}];
return Array.isArray(fieldRules) ? fieldRules : [fieldRules];
}, [rules]);
return { triggerStaticValidation: useCallback(({ key, value, mode = "blur" }, context) => {
const fieldRules = getRulesForField(key);
const isFieldOptional = !!context?.state.optionalFields?.includes(key);
const isFieldObscured = !!context?.state.obscuredFields?.includes(key);
return new ValidationResult(fieldRules.map((rule) => {
const shouldValidate = rule.modes.includes(mode) && !isFieldObscured;
const isValid = isFieldOptional && isEmpty(value) ? true : rule.validate(value, context);
return {
isValid,
errorMessage: typeof rule.errorMessage === "function" ? rule.errorMessage(value, context) : rule.errorMessage,
hasError: shouldValidate && !isValid
};
}));
}, [getRulesForField]) };
};
//#endregion
//#region src/hooks/useForm/reducer.ts
/** Formats and validates a field */
var processField = ({ key, value = null, mode, defaultData, fieldContext, formatters, staticValidate, asyncValidate }) => {
const formatterFn = formatters?.[key]?.formatter ? formatters[key]?.formatter : formatters?.[key];
const formattedValue = formatterFn && typeof formatterFn === "function" ? formatterFn(value, fieldContext) : value;
const shouldValidateField = !(defaultData?.[key] === value && !!defaultData?.[key]);
if (shouldValidateField && value) asyncValidate(key, fieldContext.state, mode);
return [formattedValue, shouldValidateField ? staticValidate({
key,
value: formattedValue,
mode
}, fieldContext) : new ValidationResult([{
isValid: true,
hasError: false
}])];
};
/**
* Processes default data and sets initial state
*/
function init({ schema, defaultData, fieldProblems, obscuredFields, optionalFields, trustedFields, formatters, staticValidate, asyncValidate }) {
const getProcessedState = (fieldKey) => {
const [formattedValue, validationResult] = processField({
key: fieldKey,
value: defaultData?.[fieldKey],
mode: "blur",
formatters,
defaultData,
fieldContext: { state: {
data: defaultData,
obscuredFields,
optionalFields,
trustedFields
} },
staticValidate,
asyncValidate
});
if (typeof defaultData?.[fieldKey] === "undefined") return {
valid: validationResult.isValid && !fieldProblems?.[fieldKey] || false,
errors: null,
data: null,
fieldProblems: fieldProblems?.[fieldKey] ?? false
};
return {
valid: validationResult.isValid && !fieldProblems?.[fieldKey] || false,
errors: validationResult.getError() ?? null,
data: formattedValue,
fieldProblems: fieldProblems?.[fieldKey] ?? false
};
};
return (optionalFields ? [...schema, ...optionalFields] : schema).reduce((acc, field) => {
const { valid, errors, data, fieldProblems: processedFieldProblems } = getProcessedState(field);
return {
...acc,
data: {
...acc.data,
[field]: data
},
valid: {
...acc.valid,
[field]: valid
},
errors: {
...acc.errors,
[field]: errors
},
fieldProblems: {
...acc.fieldProblems,
[field]: processedFieldProblems
}
};
}, {
schema,
data: {},
valid: {},
errors: {},
fieldProblems: {},
obscuredFields,
optionalFields,
trustedFields
});
}
function reducer({ staticValidate, asyncValidate, obscuredFields, optionalFields, trustedFields, formatters }) {
return (state, { type, key, mode, value, schema, selectedSchema, formValue, fieldProblems, defaultData }) => {
const validationSchema = selectedSchema || state.schema;
switch (type) {
case "setData":
if (!key) return state;
return {
...state,
data: {
...state.data,
[key]: value
}
};
case "setValid":
if (!key) return state;
return {
...state,
valid: {
...state.valid,
[key]: value
}
};
case "setErrors":
if (!key) return state;
return {
...state,
errors: {
...state.errors,
[key]: value
}
};
case "setFieldProblems": return state?.schema?.reduce((acc, field) => ({
...acc,
fieldProblems: {
...state.fieldProblems,
[field]: fieldProblems?.[field] ?? false
},
valid: {
...state.valid,
[field]: state.valid?.[field] && !fieldProblems?.[field]
}
}), state);
case "setSchema": {
if (!schema) return state;
const defaultState = init({
schema,
defaultData,
fieldProblems,
obscuredFields,
optionalFields,
trustedFields,
staticValidate,
asyncValidate
});
const removedFields = state.optionalFields?.length ? [...state.schema.filter((field) => !schema.includes(field)), ...state.optionalFields.filter((field) => !optionalFields?.includes(field))] : state.schema.filter((field) => !schema.includes(field));
const newFields = optionalFields?.length ? [...schema.filter((field) => !state.schema?.includes(field)), ...optionalFields.filter((field) => !state.optionalFields?.includes(field))] : schema.filter((field) => !state.schema?.includes(field));
const local = {
data: omitKeys(state.data, newFields),
errors: omitKeys(state.errors, newFields),
valid: omitKeys(state.valid, newFields),
fieldProblems: omitKeys(state.fieldProblems, newFields)
};
const updatedData = addKeys(omitKeys(state.data, removedFields), newFields, null, defaultState.data, state.local?.data);
const updatedValid = addKeys(omitKeys(state.valid, removedFields), newFields, false, defaultState.valid, state.local?.valid);
const updatedErrors = addKeys(omitKeys(state.errors, removedFields), newFields, null, defaultState.errors, state.local?.errors);
const updatedFieldProblems = addKeys(omitKeys(state.fieldProblems, removedFields), newFields, false, defaultState.fieldProblems, state.local?.fieldProblems);
return {
...state,
schema,
data: updatedData,
valid: updatedValid,
errors: updatedErrors,
fieldProblems: updatedFieldProblems,
obscuredFields,
optionalFields,
trustedFields,
local
};
}
case "updateField": {
if (!key || !mode) return state;
const [formattedValue, validation] = processField({
key,
value,
mode,
defaultData,
formatters,
fieldContext: { state },
staticValidate,
asyncValidate
});
const clearFieldProblems = state.data[key] !== formattedValue;
return {
...state,
data: {
...state.data,
[key]: formattedValue
},
errors: {
...state.errors,
[key]: validation.getError() ?? null
},
valid: {
...state.valid,
[key]: validation.isValid && !state.fieldProblems?.[key] || false
},
fieldProblems: {
...state.fieldProblems,
[key]: clearFieldProblems ? false : state.fieldProblems?.[key] ?? false
}
};
}
case "mergeForm": {
const mergedState = {
...state,
data: {
...state.data,
...formValue?.data
},
errors: {
...state.errors,
...formValue?.errors
},
valid: {
...state.valid,
...formValue?.valid
},
fieldProblems: {
...state.fieldProblems,
...formValue?.fieldProblems
}
};
if (mergedState.valid) mergedState.isValid = Object.values(mergedState.valid).every((isValid) => isValid);
return mergedState;
}
case "validateForm": {
const formValidation = validationSchema?.reduce((acc, cur) => {
const [formattedValue, validation] = processField({
key: cur,
value: state.data[cur],
mode: "blur",
defaultData,
formatters,
fieldContext: { state },
staticValidate,
asyncValidate
});
const clearFieldProblems = state.data[cur] !== formattedValue;
return {
valid: {
...acc.valid,
[cur]: validation.isValid && !state.fieldProblems?.[cur] || false
},
errors: {
...acc.errors,
[cur]: validation.getError() ?? null
},
fieldProblems: {
...acc.fieldProblems,
[cur]: clearFieldProblems ? false : state.fieldProblems?.[cur] ?? false
}
};
}, {
valid: state.valid,
errors: state.errors,
fieldProblems: state.fieldProblems
});
return {
...state,
valid: formValidation?.valid,
errors: formValidation?.errors,
fieldProblems: formValidation?.fieldProblems
};
}
default: throw new Error("Undefined useForm action");
}
};
}
//#endregion
//#region src/hooks/useForm/useForm.ts
var mergeStaticAndAsyncErrorsState = (staticValidationErrors, asyncValidationErrors, schema, setValid, optionalFields) => {
const fields = optionalFields ? [...schema, ...optionalFields] : schema;
if (!fields.length) return staticValidationErrors;
return fields.reduce((acc, field) => {
if (asyncValidationErrors[field]?.getError()) setValid(field, false);
return {
...acc,
[field]: staticValidationErrors[field] ?? asyncValidationErrors[field]?.getError() ?? null
};
}, {});
};
function useForm({ schema, defaultData, rules, asyncRules, fieldProblems, obscuredFields, optionalFields, trustedFields, formatters, shouldValidate = false }) {
const { triggerStaticValidation } = useStaticValidator(rules);
const { triggerAsyncValidation, asyncValidationResults } = useAsyncValidator(asyncRules);
const getRequiredFields = useMemo(() => typeof schema === "function" ? schema : () => schema, [schema]);
const getSchema = useMemo(() => defaultData ? getRequiredFields(defaultData) : [], [defaultData, getRequiredFields]);
const getReducer = useCallback(() => reducer({
staticValidate: triggerStaticValidation,
asyncValidate: triggerAsyncValidation,
obscuredFields,
optionalFields,
trustedFields,
formatters
}), [
triggerStaticValidation,
triggerAsyncValidation,
obscuredFields,
optionalFields,
trustedFields,
formatters
]);
const initialData = useMemo(() => ({
schema: getSchema,
defaultData,
fieldProblems,
obscuredFields,
optionalFields,
trustedFields,
formatters,
staticValidate: triggerStaticValidation,
asyncValidate: triggerAsyncValidation
}), [
getSchema,
defaultData,
fieldProblems,
obscuredFields,
optionalFields,
trustedFields,
formatters,
triggerStaticValidation,
triggerAsyncValidation
]);
const [state, dispatch] = useReducer(getReducer(), initialData, init);
const isValid = useMemo(() => state.schema?.every((key) => state.valid[key]), [state.schema, state.valid]);
const getTargetValue = useCallback((key, e) => {
if (!e?.target) return e;
if (e.target.type === "checkbox") return !state.data[key];
return e.target.value;
}, [state.data]);
const setErrors = useCallback((key, value) => {
dispatch({
type: "setErrors",
key,
value
});
}, []);
const setValid = useCallback((key, value) => {
dispatch({
type: "setValid",
key,
value
});
}, []);
const setData = useCallback((key, value) => {
dispatch({
type: "setData",
key,
value
});
}, []);
const setSchema = useCallback((newSchema) => {
dispatch({
type: "setSchema",
schema: newSchema,
defaultData
});
}, [defaultData]);
const setFieldProblems = useCallback((newFieldProblems) => {
dispatch({
type: "setFieldProblems",
fieldProblems: newFieldProblems
});
}, []);
const mergeForm = useCallback((formValue) => {
dispatch({
type: "mergeForm",
formValue
});
}, []);
const handleChangeFor = useCallback((key, mode = "blur") => (e) => {
dispatch({
type: "updateField",
key,
value: getTargetValue(key, e),
mode,
defaultData
});
}, [defaultData, getTargetValue]);
const triggerValidation = useCallback((selectedSchema) => {
dispatch({
type: "validateForm",
selectedSchema,
defaultData
});
}, [defaultData]);
const resetToDefaultData = useCallback(() => {
getSchema.forEach((field) => {
if (!defaultData || typeof defaultData[field] !== "undefined") {
handleChangeFor(field)(defaultData?.[field]);
return;
}
setValid(field, false);
setErrors(field, null);
setData(field, defaultData[field]);
});
}, [
defaultData,
getSchema,
handleChangeFor,
setData,
setErrors,
setValid
]);
useEffect(() => {
const newSchema = getRequiredFields(state.data);
if (!doArraysMatch(state.schema, newSchema)) setSchema(newSchema);
}, [
state.schema,
state.data,
setSchema,
getRequiredFields
]);
useEffect(() => {
if (shouldValidate) triggerValidation();
}, [shouldValidate, state.schema]);
useEffect(() => {
if (fieldProblems) setFieldProblems(fieldProblems);
}, [JSON.stringify(fieldProblems), setFieldProblems]);
const formErrors = useMemo(() => mergeStaticAndAsyncErrorsState(state.errors, asyncValidationResults, state.schema, setValid, state.optionalFields), [
asyncValidationResults,
state.errors,
state.optionalFields,
state.schema
]);
return {
mergeForm,
setData,
setValid,
setErrors,
setFieldProblems,
handleChangeFor,
triggerValidation,
resetToDefaultData,
isValid,
schema: state.schema,
valid: state.valid,
errors: formErrors,
data: state.data,
fieldProblems: state.fieldProblems
};
}
//#endregion
export { useForm as t };