UNPKG

@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
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 };