@thunderid/react 1.12.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/auth/executeEmbeddedSelfFlow.d.ts +36 -0
- package/dist/api/auth/executeEmbeddedSelfFlow.d.ts.map +1 -0
- package/dist/api/auth/executeEmbeddedSelfFlow.js +30 -0
- package/dist/api/auth/executeEmbeddedSelfFlow.js.map +1 -0
- package/dist/cjs/api/auth/executeEmbeddedSelfFlow.cjs +32 -0
- package/dist/cjs/api/auth/executeEmbeddedSelfFlow.cjs.map +1 -0
- package/dist/cjs/components/account/UserProfile/BaseUserProfile.cjs +195 -46
- package/dist/cjs/components/account/UserProfile/BaseUserProfile.cjs.map +1 -1
- package/dist/cjs/components/account/UserProfile/BaseUserProfile.styles.cjs +57 -4
- package/dist/cjs/components/account/UserProfile/BaseUserProfile.styles.cjs.map +1 -1
- package/dist/cjs/components/account/UserProfile/UserProfile.cjs +13 -1
- package/dist/cjs/components/account/UserProfile/UserProfile.cjs.map +1 -1
- package/dist/cjs/components/account/VerifyAttribute/BaseVerifyAttribute.cjs +358 -0
- package/dist/cjs/components/account/VerifyAttribute/BaseVerifyAttribute.cjs.map +1 -0
- package/dist/cjs/components/account/VerifyAttribute/BaseVerifyAttribute.styles.cjs +84 -0
- package/dist/cjs/components/account/VerifyAttribute/BaseVerifyAttribute.styles.cjs.map +1 -0
- package/dist/cjs/components/account/VerifyAttribute/VerifyAttribute.cjs +94 -0
- package/dist/cjs/components/account/VerifyAttribute/VerifyAttribute.cjs.map +1 -0
- package/dist/cjs/components/auth/flow/AuthOptionFactory.cjs +26 -3
- package/dist/cjs/components/auth/flow/AuthOptionFactory.cjs.map +1 -1
- package/dist/cjs/components/auth/flow/FieldFactory.cjs +2 -1
- package/dist/cjs/components/auth/flow/FieldFactory.cjs.map +1 -1
- package/dist/cjs/components/primitives/OtpField/OtpField.cjs +10 -5
- package/dist/cjs/components/primitives/OtpField/OtpField.cjs.map +1 -1
- package/dist/cjs/components/primitives/OtpField/OtpField.styles.cjs +16 -8
- package/dist/cjs/components/primitives/OtpField/OtpField.styles.cjs.map +1 -1
- package/dist/cjs/contexts/ComponentRenderer/ComponentRendererContext.cjs.map +1 -1
- package/dist/cjs/contexts/ThunderID/ThunderIDProvider.cjs +14 -0
- package/dist/cjs/contexts/ThunderID/ThunderIDProvider.cjs.map +1 -1
- package/dist/cjs/index.cjs +30 -0
- package/dist/components/account/UserProfile/BaseUserProfile.d.ts +27 -1
- package/dist/components/account/UserProfile/BaseUserProfile.d.ts.map +1 -1
- package/dist/components/account/UserProfile/BaseUserProfile.js +197 -48
- package/dist/components/account/UserProfile/BaseUserProfile.js.map +1 -1
- package/dist/components/account/UserProfile/BaseUserProfile.styles.d.ts.map +1 -1
- package/dist/components/account/UserProfile/BaseUserProfile.styles.js +57 -4
- package/dist/components/account/UserProfile/BaseUserProfile.styles.js.map +1 -1
- package/dist/components/account/UserProfile/UserProfile.d.ts +7 -1
- package/dist/components/account/UserProfile/UserProfile.d.ts.map +1 -1
- package/dist/components/account/UserProfile/UserProfile.js +14 -2
- package/dist/components/account/UserProfile/UserProfile.js.map +1 -1
- package/dist/components/account/VerifyAttribute/BaseVerifyAttribute.d.ts +113 -0
- package/dist/components/account/VerifyAttribute/BaseVerifyAttribute.d.ts.map +1 -0
- package/dist/components/account/VerifyAttribute/BaseVerifyAttribute.js +354 -0
- package/dist/components/account/VerifyAttribute/BaseVerifyAttribute.js.map +1 -0
- package/dist/components/account/VerifyAttribute/BaseVerifyAttribute.styles.d.ts +10 -0
- package/dist/components/account/VerifyAttribute/BaseVerifyAttribute.styles.d.ts.map +1 -0
- package/dist/components/account/VerifyAttribute/BaseVerifyAttribute.styles.js +82 -0
- package/dist/components/account/VerifyAttribute/BaseVerifyAttribute.styles.js.map +1 -0
- package/dist/components/account/VerifyAttribute/VerifyAttribute.d.ts +41 -0
- package/dist/components/account/VerifyAttribute/VerifyAttribute.d.ts.map +1 -0
- package/dist/components/account/VerifyAttribute/VerifyAttribute.js +90 -0
- package/dist/components/account/VerifyAttribute/VerifyAttribute.js.map +1 -0
- package/dist/components/auth/flow/AuthOptionFactory.d.ts +5 -1
- package/dist/components/auth/flow/AuthOptionFactory.d.ts.map +1 -1
- package/dist/components/auth/flow/AuthOptionFactory.js +26 -4
- package/dist/components/auth/flow/AuthOptionFactory.js.map +1 -1
- package/dist/components/auth/flow/FieldFactory.d.ts +4 -0
- package/dist/components/auth/flow/FieldFactory.d.ts.map +1 -1
- package/dist/components/auth/flow/FieldFactory.js +2 -1
- package/dist/components/auth/flow/FieldFactory.js.map +1 -1
- package/dist/components/primitives/OtpField/OtpField.d.ts +4 -0
- package/dist/components/primitives/OtpField/OtpField.d.ts.map +1 -1
- package/dist/components/primitives/OtpField/OtpField.js +11 -6
- package/dist/components/primitives/OtpField/OtpField.js.map +1 -1
- package/dist/components/primitives/OtpField/OtpField.styles.d.ts +1 -1
- package/dist/components/primitives/OtpField/OtpField.styles.d.ts.map +1 -1
- package/dist/components/primitives/OtpField/OtpField.styles.js +16 -8
- package/dist/components/primitives/OtpField/OtpField.styles.js.map +1 -1
- package/dist/contexts/ComponentRenderer/ComponentRendererContext.d.ts +1 -1
- package/dist/contexts/ComponentRenderer/ComponentRendererContext.d.ts.map +1 -1
- package/dist/contexts/ComponentRenderer/ComponentRendererContext.js.map +1 -1
- package/dist/contexts/ThunderID/ThunderIDProvider.d.ts.map +1 -1
- package/dist/contexts/ThunderID/ThunderIDProvider.js +14 -0
- package/dist/contexts/ThunderID/ThunderIDProvider.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/package.json +2 -2
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { cx } from "../../../styles/emotion.js";
|
|
2
|
+
import ComponentRendererContext_default from "../../../contexts/ComponentRenderer/ComponentRendererContext.js";
|
|
3
|
+
import useThunderID_default from "../../../contexts/ThunderID/useThunderID.js";
|
|
4
|
+
import useTheme_default from "../../../contexts/Theme/useTheme.js";
|
|
5
|
+
import ComponentPreferencesContext_default from "../../../contexts/I18n/ComponentPreferencesContext.js";
|
|
6
|
+
import useTranslation_default from "../../../hooks/useTranslation.js";
|
|
7
|
+
import { extractErrorMessage, normalizeFlowResponse } from "../../../utils/flowTransformer.js";
|
|
8
|
+
import { useForm } from "../../../hooks/useForm.js";
|
|
9
|
+
import Spinner_default from "../../primitives/Spinner/Spinner.js";
|
|
10
|
+
import Button_default from "../../primitives/Button/Button.js";
|
|
11
|
+
import Typography_default from "../../primitives/Typography/Typography.js";
|
|
12
|
+
import composeAffixedInputs_default from "../../../utils/composeAffixedInputs.js";
|
|
13
|
+
import Alert_default from "../../primitives/Alert/Alert.js";
|
|
14
|
+
import { renderVerificationComponents } from "../../auth/flow/AuthOptionFactory.js";
|
|
15
|
+
import getAuthComponentHeadings_default from "../../../utils/getAuthComponentHeadings.js";
|
|
16
|
+
import BaseVerifyAttribute_styles_default from "./BaseVerifyAttribute.styles.js";
|
|
17
|
+
import { AttributeVerificationConstants, EmbeddedFlowComponentType, EmbeddedSelfFlowStatus, bem, buildValidatorFromRules, getFlowStepComponents, resolveFlowTemplateLiterals, withVendorCSSClassPrefix } from "@thunderid/browser";
|
|
18
|
+
import { cloneElement, isValidElement, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
19
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
20
|
+
|
|
21
|
+
//#region src/components/account/VerifyAttribute/BaseVerifyAttribute.tsx
|
|
22
|
+
const collectFlowIds = ({ actions, inputs }) => ({
|
|
23
|
+
actions: new Set(actions.map((component) => component.id)),
|
|
24
|
+
fields: new Map(inputs.map((component) => [component.ref ?? component.id, component.type]))
|
|
25
|
+
});
|
|
26
|
+
const withFlowTestIds = (node, ids) => {
|
|
27
|
+
if (Array.isArray(node)) return node.map((child) => withFlowTestIds(child, ids));
|
|
28
|
+
if (!isValidElement(node)) return node;
|
|
29
|
+
const props = node.props;
|
|
30
|
+
if (typeof props.name === "string" && ids.fields.has(props.name)) {
|
|
31
|
+
const testId$1 = `thunderid-field-${props.name}`;
|
|
32
|
+
if (ids.fields.get(props.name) === EmbeddedFlowComponentType.OtpInput) return /* @__PURE__ */ jsx("div", {
|
|
33
|
+
"data-testid": testId$1,
|
|
34
|
+
children: node
|
|
35
|
+
}, node.key);
|
|
36
|
+
return cloneElement(node, { "data-testid": testId$1 });
|
|
37
|
+
}
|
|
38
|
+
const testId = node.key !== null && ids.actions.has(String(node.key)) ? `thunderid-action-${String(node.key)}` : void 0;
|
|
39
|
+
const children = props.children === void 0 ? void 0 : withFlowTestIds(props.children, ids);
|
|
40
|
+
if (!testId && children === props.children) return node;
|
|
41
|
+
return cloneElement(node, {
|
|
42
|
+
...testId && { "data-testid": testId },
|
|
43
|
+
...props.children !== void 0 && { children }
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Internal component that renders the verification steps and manages flow state.
|
|
48
|
+
*
|
|
49
|
+
* @internal
|
|
50
|
+
*/
|
|
51
|
+
const BaseVerifyAttributeContent = ({ attributeDisplayName = "", buttonClassName = "", children = void 0, className = "", inputClassName = "", isInitialized = true, onCancel = void 0, onComplete = void 0, onDone = void 0, onError = void 0, onFlowChange = void 0, onInitialize = void 0, onSubmit = void 0, showSubtitle = true, showTitle = true, size = "medium", variant = "outlined" }) => {
|
|
52
|
+
const { theme, colorScheme } = useTheme_default();
|
|
53
|
+
const customRenderers = useContext(ComponentRendererContext_default);
|
|
54
|
+
const { t } = useTranslation_default();
|
|
55
|
+
const { meta, vendor } = useThunderID_default();
|
|
56
|
+
const styles = BaseVerifyAttribute_styles_default(theme, colorScheme);
|
|
57
|
+
const [state, setState] = useState("loading");
|
|
58
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
59
|
+
const [currentFlow, setCurrentFlow] = useState(null);
|
|
60
|
+
const [error, setError] = useState(null);
|
|
61
|
+
const isSubmittingRef = useRef(false);
|
|
62
|
+
const initializationAttemptedRef = useRef(false);
|
|
63
|
+
const challengeTokenRef = useRef(null);
|
|
64
|
+
const labelParams = { attribute: attributeDisplayName };
|
|
65
|
+
const normalize = useCallback((response) => {
|
|
66
|
+
if (!response?.data || Array.isArray(response.data.components)) return response;
|
|
67
|
+
const { components } = normalizeFlowResponse(response, t, {
|
|
68
|
+
defaultErrorKey: "user.verification.error",
|
|
69
|
+
resolveTranslations: false,
|
|
70
|
+
throwOnError: false
|
|
71
|
+
}, meta);
|
|
72
|
+
return {
|
|
73
|
+
...response,
|
|
74
|
+
data: {
|
|
75
|
+
...response.data,
|
|
76
|
+
components
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}, [t, meta]);
|
|
80
|
+
const extractFormFields = useCallback((components) => getFlowStepComponents(components).inputs.map((component) => {
|
|
81
|
+
const ruleValidator = buildValidatorFromRules(component.validation);
|
|
82
|
+
return {
|
|
83
|
+
initialValue: "",
|
|
84
|
+
name: component.ref ?? component.id,
|
|
85
|
+
required: component.required ?? false,
|
|
86
|
+
validator: (value) => {
|
|
87
|
+
if (component.required && (!value || value.trim() === "")) return t("validations.required.field.error");
|
|
88
|
+
if (ruleValidator && value) {
|
|
89
|
+
const ruleMessage = ruleValidator(value);
|
|
90
|
+
if (ruleMessage) return t(ruleMessage);
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}), [t]);
|
|
96
|
+
const formFields = useMemo(() => extractFormFields(currentFlow?.data?.components ?? []), [currentFlow, extractFormFields]);
|
|
97
|
+
const { values: formValues, touched: touchedFields, errors: formErrors, isValid: isFormValid, setValue: setFormValue, setTouched: setFormTouched, setTouchedFields, setErrors: setFormErrors, clearErrors: clearFormErrors, validateForm, touchAllFields, reset: resetForm } = useForm({
|
|
98
|
+
fields: formFields,
|
|
99
|
+
initialValues: {},
|
|
100
|
+
requiredMessage: t("validations.required.field.error"),
|
|
101
|
+
validateOnBlur: true,
|
|
102
|
+
validateOnChange: false
|
|
103
|
+
});
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
clearFormErrors();
|
|
106
|
+
const responseFieldErrors = currentFlow?.data?.fieldErrors;
|
|
107
|
+
if (!responseFieldErrors || responseFieldErrors.length === 0) return;
|
|
108
|
+
const errors = {};
|
|
109
|
+
const touched = {};
|
|
110
|
+
responseFieldErrors.forEach((fe) => {
|
|
111
|
+
if (!(fe.identifier in errors)) {
|
|
112
|
+
errors[fe.identifier] = fe.message;
|
|
113
|
+
touched[fe.identifier] = true;
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
setTouchedFields(touched);
|
|
117
|
+
setFormErrors(errors);
|
|
118
|
+
}, [
|
|
119
|
+
currentFlow,
|
|
120
|
+
setFormErrors,
|
|
121
|
+
setTouchedFields,
|
|
122
|
+
clearFormErrors
|
|
123
|
+
]);
|
|
124
|
+
const handleResponse = useCallback((rawResponse) => {
|
|
125
|
+
const response = normalize(rawResponse);
|
|
126
|
+
onFlowChange?.(response);
|
|
127
|
+
if (response.challengeToken !== void 0) challengeTokenRef.current = response.challengeToken ?? null;
|
|
128
|
+
if (response.flowStatus === EmbeddedSelfFlowStatus.Error) {
|
|
129
|
+
const message = extractErrorMessage(response, t, "user.verification.error");
|
|
130
|
+
setError(message);
|
|
131
|
+
setState("failed");
|
|
132
|
+
onError?.(new Error(message));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (response.flowStatus === EmbeddedSelfFlowStatus.Complete) {
|
|
136
|
+
setError(null);
|
|
137
|
+
setState("complete");
|
|
138
|
+
onComplete?.(response);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
setCurrentFlow((previous) => response.data?.additionalData || !previous?.data?.additionalData ? response : {
|
|
142
|
+
...response,
|
|
143
|
+
data: {
|
|
144
|
+
...response.data,
|
|
145
|
+
additionalData: previous.data.additionalData
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
resetForm();
|
|
149
|
+
setError(response.error ? extractErrorMessage(response, t, "user.verification.error") : null);
|
|
150
|
+
setState("active");
|
|
151
|
+
}, [
|
|
152
|
+
normalize,
|
|
153
|
+
onFlowChange,
|
|
154
|
+
onError,
|
|
155
|
+
onComplete,
|
|
156
|
+
resetForm,
|
|
157
|
+
t
|
|
158
|
+
]);
|
|
159
|
+
const handleFailure = useCallback((caughtError) => {
|
|
160
|
+
setError(t("user.verification.error"));
|
|
161
|
+
setState("failed");
|
|
162
|
+
onError?.(caughtError instanceof Error ? caughtError : new Error(String(caughtError)));
|
|
163
|
+
}, [onError, t]);
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
if (!isInitialized || initializationAttemptedRef.current) return;
|
|
166
|
+
initializationAttemptedRef.current = true;
|
|
167
|
+
(async () => {
|
|
168
|
+
try {
|
|
169
|
+
const response = await onInitialize?.();
|
|
170
|
+
if (response) handleResponse(response);
|
|
171
|
+
} catch (caughtError) {
|
|
172
|
+
handleFailure(caughtError);
|
|
173
|
+
}
|
|
174
|
+
})();
|
|
175
|
+
}, [
|
|
176
|
+
isInitialized,
|
|
177
|
+
onInitialize,
|
|
178
|
+
handleResponse,
|
|
179
|
+
handleFailure
|
|
180
|
+
]);
|
|
181
|
+
const handleInputChange = (name, value) => {
|
|
182
|
+
setFormValue(name, value);
|
|
183
|
+
};
|
|
184
|
+
const handleInputBlur = (name) => {
|
|
185
|
+
setFormTouched(name, true);
|
|
186
|
+
};
|
|
187
|
+
const handleSubmit = async (component, data, skipValidation) => {
|
|
188
|
+
if (!currentFlow || isSubmittingRef.current) return;
|
|
189
|
+
if (!skipValidation) {
|
|
190
|
+
touchAllFields();
|
|
191
|
+
if (!validateForm().isValid) return;
|
|
192
|
+
}
|
|
193
|
+
isSubmittingRef.current = true;
|
|
194
|
+
setIsSubmitting(true);
|
|
195
|
+
setError(null);
|
|
196
|
+
try {
|
|
197
|
+
const composedData = data ? composeAffixedInputs_default(data, vendor) : {};
|
|
198
|
+
const inputs = Object.fromEntries(Object.entries(composedData).filter(([, value]) => value !== null && value !== void 0 && value !== ""));
|
|
199
|
+
const response = await onSubmit?.({
|
|
200
|
+
executionId: currentFlow.executionId,
|
|
201
|
+
...component.id && { action: component.id },
|
|
202
|
+
...challengeTokenRef.current ? { challengeToken: challengeTokenRef.current } : {},
|
|
203
|
+
inputs
|
|
204
|
+
});
|
|
205
|
+
if (response) handleResponse(response);
|
|
206
|
+
} catch (caughtError) {
|
|
207
|
+
handleFailure(caughtError);
|
|
208
|
+
} finally {
|
|
209
|
+
isSubmittingRef.current = false;
|
|
210
|
+
setIsSubmitting(false);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
const additionalData = currentFlow?.data?.additionalData ?? {};
|
|
214
|
+
const maskedDestination = additionalData[AttributeVerificationConstants.MASKED_DESTINATION_KEY];
|
|
215
|
+
const destination = typeof maskedDestination === "string" && maskedDestination ? maskedDestination : null;
|
|
216
|
+
const headings = getAuthComponentHeadings_default(currentFlow?.data?.components ?? [], void 0, void 0, t("user.verification.heading", labelParams), t("user.verification.subheading.generic"));
|
|
217
|
+
const { componentsWithoutHeadings } = headings;
|
|
218
|
+
const step = getFlowStepComponents(componentsWithoutHeadings ?? []);
|
|
219
|
+
const isAwaitingLink = state === "active" && formFields.length === 0 && step.actions.length === 0;
|
|
220
|
+
const title = resolveFlowTemplateLiterals(headings.title, {
|
|
221
|
+
meta,
|
|
222
|
+
t
|
|
223
|
+
});
|
|
224
|
+
const hasFlowSubtitle = !!headings.headingComponents.subheading;
|
|
225
|
+
let subtitle = resolveFlowTemplateLiterals(headings.subtitle, {
|
|
226
|
+
meta,
|
|
227
|
+
t
|
|
228
|
+
});
|
|
229
|
+
let destinationText = null;
|
|
230
|
+
if (destination && hasFlowSubtitle) destinationText = t("user.verification.destination", { destination });
|
|
231
|
+
else if (destination) subtitle = t(isAwaitingLink ? "user.verification.subheading.link" : "user.verification.subheading", { destination });
|
|
232
|
+
const isLoading = state === "loading" || isSubmitting;
|
|
233
|
+
const containerClasses = cx(withVendorCSSClassPrefix("verify-attribute"), withVendorCSSClassPrefix(`verify-attribute--${size}`), withVendorCSSClassPrefix(`verify-attribute--${variant}`), styles["root"], variant === "outlined" ? styles["card"] : "", className);
|
|
234
|
+
if (children) {
|
|
235
|
+
const resend = state === "active" ? step.resend : void 0;
|
|
236
|
+
return /* @__PURE__ */ jsx("div", {
|
|
237
|
+
className: containerClasses,
|
|
238
|
+
children: children({
|
|
239
|
+
additionalData,
|
|
240
|
+
components: componentsWithoutHeadings ?? [],
|
|
241
|
+
destination,
|
|
242
|
+
destinationText,
|
|
243
|
+
error,
|
|
244
|
+
fieldErrors: formErrors,
|
|
245
|
+
handleInputChange,
|
|
246
|
+
handleResend: resend ? () => handleSubmit(resend, void 0, true) : void 0,
|
|
247
|
+
handleSubmit,
|
|
248
|
+
isAwaitingLink,
|
|
249
|
+
isComplete: state === "complete",
|
|
250
|
+
isFailed: state === "failed",
|
|
251
|
+
isLoading,
|
|
252
|
+
isValid: isFormValid,
|
|
253
|
+
onCancel,
|
|
254
|
+
onDone: isAwaitingLink ? onDone : void 0,
|
|
255
|
+
subtitle,
|
|
256
|
+
successMessage: t("user.verification.success", labelParams),
|
|
257
|
+
title,
|
|
258
|
+
touched: touchedFields,
|
|
259
|
+
values: formValues
|
|
260
|
+
})
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
const alertClasses = cx(withVendorCSSClassPrefix(bem("verify-attribute", "alert")), styles["alert"]);
|
|
264
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
265
|
+
className: containerClasses,
|
|
266
|
+
"aria-busy": isLoading,
|
|
267
|
+
children: [
|
|
268
|
+
state === "active" && (showTitle || showSubtitle) && /* @__PURE__ */ jsxs("div", {
|
|
269
|
+
className: cx(withVendorCSSClassPrefix(bem("verify-attribute", "header")), styles["header"]),
|
|
270
|
+
children: [showTitle && /* @__PURE__ */ jsx(Typography_default, {
|
|
271
|
+
variant: "h5",
|
|
272
|
+
className: cx(withVendorCSSClassPrefix(bem("verify-attribute", "heading")), styles["heading"]),
|
|
273
|
+
children: title
|
|
274
|
+
}), showSubtitle && /* @__PURE__ */ jsxs(Typography_default, {
|
|
275
|
+
variant: "body2",
|
|
276
|
+
className: cx(withVendorCSSClassPrefix(bem("verify-attribute", "subheading")), styles["subheading"]),
|
|
277
|
+
children: [subtitle, destinationText && /* @__PURE__ */ jsx("span", {
|
|
278
|
+
className: withVendorCSSClassPrefix(bem("verify-attribute", "destination")),
|
|
279
|
+
children: ` ${destinationText}`
|
|
280
|
+
})]
|
|
281
|
+
})]
|
|
282
|
+
}),
|
|
283
|
+
state === "loading" && /* @__PURE__ */ jsx("div", {
|
|
284
|
+
className: cx(withVendorCSSClassPrefix(bem("verify-attribute", "loading")), styles["loading"]),
|
|
285
|
+
children: /* @__PURE__ */ jsx(Spinner_default, { size: "medium" })
|
|
286
|
+
}),
|
|
287
|
+
error && /* @__PURE__ */ jsx(Alert_default, {
|
|
288
|
+
variant: "error",
|
|
289
|
+
className: alertClasses,
|
|
290
|
+
children: /* @__PURE__ */ jsx(Alert_default.Description, { children: error })
|
|
291
|
+
}),
|
|
292
|
+
state === "complete" && /* @__PURE__ */ jsx(Alert_default, {
|
|
293
|
+
variant: "success",
|
|
294
|
+
className: alertClasses,
|
|
295
|
+
children: /* @__PURE__ */ jsx(Alert_default.Description, { children: t("user.verification.success", labelParams) })
|
|
296
|
+
}),
|
|
297
|
+
state === "active" && /* @__PURE__ */ jsx("div", {
|
|
298
|
+
className: cx(withVendorCSSClassPrefix(bem("verify-attribute", "content")), styles["content"]),
|
|
299
|
+
children: withFlowTestIds(renderVerificationComponents(componentsWithoutHeadings ?? [], formValues, touchedFields, formErrors, isLoading, isFormValid, resetForm, handleInputChange, {
|
|
300
|
+
_customRenderers: customRenderers,
|
|
301
|
+
_theme: theme,
|
|
302
|
+
additionalData,
|
|
303
|
+
buttonClassName: cx(withVendorCSSClassPrefix(bem("verify-attribute", "button")), buttonClassName),
|
|
304
|
+
inputClassName: cx(withVendorCSSClassPrefix(bem("verify-attribute", "input")), inputClassName),
|
|
305
|
+
meta,
|
|
306
|
+
onInputBlur: handleInputBlur,
|
|
307
|
+
onSubmit: (component, data, skip) => {
|
|
308
|
+
handleSubmit(component, data, skip);
|
|
309
|
+
},
|
|
310
|
+
size,
|
|
311
|
+
t,
|
|
312
|
+
variant,
|
|
313
|
+
vendor
|
|
314
|
+
}), collectFlowIds(step))
|
|
315
|
+
}),
|
|
316
|
+
(!!onCancel || isAwaitingLink && !!onDone) && /* @__PURE__ */ jsxs("div", {
|
|
317
|
+
className: cx(withVendorCSSClassPrefix(bem("verify-attribute", "actions")), styles["actions"]),
|
|
318
|
+
children: [isAwaitingLink && onDone && /* @__PURE__ */ jsx(Button_default, {
|
|
319
|
+
size: "small",
|
|
320
|
+
color: "primary",
|
|
321
|
+
variant: "solid",
|
|
322
|
+
onClick: onDone,
|
|
323
|
+
"data-testid": "thunderid-action-verificationDone",
|
|
324
|
+
children: t("user.verification.buttons.done.text")
|
|
325
|
+
}), onCancel && /* @__PURE__ */ jsx(Button_default, {
|
|
326
|
+
size: "small",
|
|
327
|
+
color: "secondary",
|
|
328
|
+
variant: "outline",
|
|
329
|
+
onClick: onCancel,
|
|
330
|
+
disabled: isSubmitting,
|
|
331
|
+
"data-testid": "thunderid-action-verificationCancel",
|
|
332
|
+
children: state === "active" || state === "loading" ? t("user.verification.buttons.cancel.text") : t("user.verification.buttons.close.text")
|
|
333
|
+
})]
|
|
334
|
+
})
|
|
335
|
+
]
|
|
336
|
+
});
|
|
337
|
+
};
|
|
338
|
+
/**
|
|
339
|
+
* BaseVerifyAttribute renders the server-described steps of a self attribute verification flow.
|
|
340
|
+
* Accepts API functions as props to stay framework independent.
|
|
341
|
+
*/
|
|
342
|
+
const BaseVerifyAttribute = ({ preferences = void 0,...rest }) => {
|
|
343
|
+
const content = /* @__PURE__ */ jsx(BaseVerifyAttributeContent, { ...rest });
|
|
344
|
+
if (!preferences) return content;
|
|
345
|
+
return /* @__PURE__ */ jsx(ComponentPreferencesContext_default.Provider, {
|
|
346
|
+
value: preferences,
|
|
347
|
+
children: content
|
|
348
|
+
});
|
|
349
|
+
};
|
|
350
|
+
var BaseVerifyAttribute_default = BaseVerifyAttribute;
|
|
351
|
+
|
|
352
|
+
//#endregion
|
|
353
|
+
export { BaseVerifyAttribute_default as default };
|
|
354
|
+
//# sourceMappingURL=BaseVerifyAttribute.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseVerifyAttribute.js","names":["props: {children?: ReactNode; name?: unknown}","testId","testId: string | undefined","children: ReactNode","BaseVerifyAttributeContent: FC<Omit<BaseVerifyAttributeProps, 'preferences'>>","useTheme","customRenderers: ComponentRendererMap","ComponentRendererContext","useTranslation","useThunderID","styles: Record<string, string>","useStyles","isSubmittingRef: MutableRefObject<boolean>","initializationAttemptedRef: MutableRefObject<boolean>","challengeTokenRef: MutableRefObject<string | null>","labelParams: Record<string, string>","formFields: FormField[]","responseFieldErrors: FieldError[] | undefined","errors: Record<string, string>","touched: Record<string, boolean>","response: EmbeddedSelfFlowResponse","message: string","response: EmbeddedSelfFlowResponse | undefined","composedData: Record<string, string>","composeAffixedInputs","inputs: Record<string, string>","additionalData: Record<string, unknown>","maskedDestination: unknown","destination: string | null","headings: ReturnType<typeof getAuthComponentHeadings>","getAuthComponentHeadings","step: FlowStepComponents","isAwaitingLink: boolean","title: string","subtitle: string","destinationText: string | null","isLoading: boolean","containerClasses: string","resend: EmbeddedFlowComponent | undefined","alertClasses: string","Typography","Spinner","AlertPrimitive","Button","BaseVerifyAttribute: FC<BaseVerifyAttributeProps>","content: ReactElement","ComponentPreferencesContext"],"sources":["../../../../src/components/account/VerifyAttribute/BaseVerifyAttribute.tsx"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n AttributeVerificationConstants,\n EmbeddedFlowComponent,\n EmbeddedFlowComponentType,\n EmbeddedSelfFlowRequest,\n EmbeddedSelfFlowResponse,\n EmbeddedSelfFlowStatus,\n FieldError,\n FlowStepComponents,\n Preferences,\n bem,\n buildValidatorFromRules,\n getFlowStepComponents,\n resolveFlowTemplateLiterals,\n withVendorCSSClassPrefix,\n} from '@thunderid/browser';\nimport {\n FC,\n MutableRefObject,\n ReactElement,\n ReactNode,\n cloneElement,\n isValidElement,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport useStyles from './BaseVerifyAttribute.styles';\nimport ComponentRendererContext, {\n ComponentRendererMap,\n} from '../../../contexts/ComponentRenderer/ComponentRendererContext';\nimport ComponentPreferencesContext from '../../../contexts/I18n/ComponentPreferencesContext';\nimport useTheme from '../../../contexts/Theme/useTheme';\nimport useThunderID from '../../../contexts/ThunderID/useThunderID';\nimport {FormField, useForm} from '../../../hooks/useForm';\nimport useTranslation from '../../../hooks/useTranslation';\nimport {cx} from '../../../styles/emotion';\nimport composeAffixedInputs from '../../../utils/composeAffixedInputs';\nimport {extractErrorMessage, normalizeFlowResponse} from '../../../utils/flowTransformer';\nimport getAuthComponentHeadings from '../../../utils/getAuthComponentHeadings';\nimport {renderVerificationComponents} from '../../auth/flow/AuthOptionFactory';\nimport AlertPrimitive from '../../primitives/Alert/Alert';\nimport Button from '../../primitives/Button/Button';\nimport Spinner from '../../primitives/Spinner/Spinner';\nimport Typography from '../../primitives/Typography/Typography';\n\ntype VerificationState = 'loading' | 'active' | 'complete' | 'failed';\n\ninterface FlowIds {\n actions: Set<string>;\n // Input name (`ref`, else `id`) to component type.\n fields: Map<string, string>;\n}\n\n// Flow identifiers of the step's inputs and actions.\nconst collectFlowIds = ({actions, inputs}: FlowStepComponents): FlowIds => ({\n actions: new Set(actions.map((component: EmbeddedFlowComponent) => component.id)),\n fields: new Map(inputs.map((component: EmbeddedFlowComponent) => [component.ref ?? component.id, component.type])),\n});\n\n// Tags the elements rendered from the flow with `thunderid-field-<ref>` and `thunderid-action-<id>`.\n// The shared factory keys an element by its component id and names an input by its ref.\nconst withFlowTestIds = (node: ReactNode, ids: FlowIds): ReactNode => {\n if (Array.isArray(node)) return node.map((child: ReactNode) => withFlowTestIds(child, ids));\n if (!isValidElement(node)) return node;\n\n const props: {children?: ReactNode; name?: unknown} = node.props as {children?: ReactNode; name?: unknown};\n if (typeof props.name === 'string' && ids.fields.has(props.name)) {\n const testId = `thunderid-field-${props.name}`;\n\n // The OTP field renders one input per character and takes no test identifier, so wrap it.\n if (ids.fields.get(props.name) === EmbeddedFlowComponentType.OtpInput) {\n return (\n <div key={node.key} data-testid={testId}>\n {node}\n </div>\n );\n }\n\n return cloneElement(node as ReactElement<Record<string, unknown>>, {'data-testid': testId});\n }\n\n const testId: string | undefined =\n node.key !== null && ids.actions.has(String(node.key)) ? `thunderid-action-${String(node.key)}` : undefined;\n const children: ReactNode = props.children === undefined ? undefined : withFlowTestIds(props.children, ids);\n if (!testId && children === props.children) return node;\n\n return cloneElement(node as ReactElement<Record<string, unknown>>, {\n ...(testId && {'data-testid': testId}),\n ...(props.children !== undefined && {children}),\n });\n};\n\n/**\n * What {@link BaseVerifyAttribute} hands its `children` render function, so the steps can be drawn\n * with any UI.\n */\nexport interface BaseVerifyAttributeRenderProps {\n additionalData: Record<string, unknown>;\n /**\n * The step's components, without its headings.\n */\n components: EmbeddedFlowComponent[];\n /**\n * Masked destination the code or link was sent to, when the flow reports it.\n */\n destination: string | null;\n /**\n * Translated \"Sent to …\" line, set when the flow has its own prompt in `subtitle`.\n */\n destinationText: string | null;\n /**\n * Translated error of the last request, or the refusal.\n */\n error: string | null;\n fieldErrors: Record<string, string>;\n handleInputChange: (name: string, value: string) => void;\n /**\n * Sends a new code. Set only while the step has a resend action.\n */\n handleResend?: () => Promise<void>;\n /**\n * Submits a step. Pass `skipValidation` for an action, such as a resend, that sends no inputs.\n */\n handleSubmit: (\n component: EmbeddedFlowComponent,\n data?: Record<string, string>,\n skipValidation?: boolean,\n ) => Promise<void>;\n isAwaitingLink: boolean;\n isComplete: boolean;\n /**\n * Whether the server refused the verification or the request failed; the flow cannot continue.\n */\n isFailed: boolean;\n isLoading: boolean;\n isValid: boolean;\n /**\n * Closes the panel. Set only when the app passes `onCancel`.\n */\n onCancel?: () => void;\n /**\n * Confirms a link the user opened outside the app. Set only while the step waits for a link and\n * the app passes `onDone`.\n */\n onDone?: () => void;\n subtitle: string;\n /**\n * Translated message for a recorded verification.\n */\n successMessage: string;\n title: string;\n touched: Record<string, boolean>;\n values: Record<string, string>;\n}\n\n/**\n * Props for the BaseVerifyAttribute component.\n */\nexport interface BaseVerifyAttributeProps {\n /**\n * Label of the attribute being verified, used in the default heading and success message.\n */\n attributeDisplayName?: string;\n buttonClassName?: string;\n /**\n * Renders custom UI in place of the default steps. Receives the step, the values and their setters,\n * submit, resend, cancel, the destination, and the loading, error and completed state.\n */\n children?: (props: BaseVerifyAttributeRenderProps) => ReactNode;\n className?: string;\n inputClassName?: string;\n isInitialized?: boolean;\n /**\n * Called when the user closes the panel. The close action is shown only when this is set.\n */\n onCancel?: () => void;\n /**\n * Called once the server has recorded the verification.\n */\n onComplete?: (response: EmbeddedSelfFlowResponse) => void;\n /**\n * Called when the user selects Done after the flow sent a link, which they confirm outside the app.\n * The Done action is shown only when this is set.\n */\n onDone?: () => void;\n /**\n * Called when the server refuses the verification or the request fails.\n */\n onError?: (error: Error) => void;\n onFlowChange?: (response: EmbeddedSelfFlowResponse) => void;\n onInitialize?: () => Promise<EmbeddedSelfFlowResponse>;\n onSubmit?: (payload: EmbeddedSelfFlowRequest) => Promise<EmbeddedSelfFlowResponse>;\n /**\n * Component-level preferences to override global i18n and theme settings.\n */\n preferences?: Preferences;\n showSubtitle?: boolean;\n showTitle?: boolean;\n size?: 'small' | 'medium' | 'large';\n variant?: 'outlined' | 'flat';\n}\n\n/**\n * Internal component that renders the verification steps and manages flow state.\n *\n * @internal\n */\nconst BaseVerifyAttributeContent: FC<Omit<BaseVerifyAttributeProps, 'preferences'>> = ({\n attributeDisplayName = '',\n buttonClassName = '',\n children = undefined,\n className = '',\n inputClassName = '',\n isInitialized = true,\n onCancel = undefined,\n onComplete = undefined,\n onDone = undefined,\n onError = undefined,\n onFlowChange = undefined,\n onInitialize = undefined,\n onSubmit = undefined,\n showSubtitle = true,\n showTitle = true,\n size = 'medium',\n variant = 'outlined',\n}: Omit<BaseVerifyAttributeProps, 'preferences'>): ReactElement => {\n const {theme, colorScheme} = useTheme();\n const customRenderers: ComponentRendererMap = useContext(ComponentRendererContext);\n const {t} = useTranslation();\n const {meta, vendor} = useThunderID();\n const styles: Record<string, string> = useStyles(theme, colorScheme);\n\n const [state, setState] = useState<VerificationState>('loading');\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [currentFlow, setCurrentFlow] = useState<EmbeddedSelfFlowResponse | null>(null);\n const [error, setError] = useState<string | null>(null);\n // Set before the first await, so a second call from a custom render cannot send a second code.\n const isSubmittingRef: MutableRefObject<boolean> = useRef(false);\n\n // Guards against a second initiation, which would send a second code.\n const initializationAttemptedRef: MutableRefObject<boolean> = useRef(false);\n const challengeTokenRef: MutableRefObject<string | null> = useRef<string | null>(null);\n\n const labelParams: Record<string, string> = {attribute: attributeDisplayName};\n\n const normalize = useCallback(\n (response: EmbeddedSelfFlowResponse): EmbeddedSelfFlowResponse => {\n if (!response?.data || Array.isArray(response.data.components)) {\n return response;\n }\n\n const {components} = normalizeFlowResponse(\n response,\n t,\n {defaultErrorKey: 'user.verification.error', resolveTranslations: false, throwOnError: false},\n meta,\n );\n\n return {...response, data: {...response.data, components}};\n },\n [t, meta],\n );\n\n const extractFormFields = useCallback(\n (components: EmbeddedFlowComponent[]): FormField[] =>\n getFlowStepComponents(components).inputs.map((component: EmbeddedFlowComponent) => {\n const ruleValidator = buildValidatorFromRules(component.validation);\n return {\n initialValue: '',\n name: component.ref ?? component.id,\n required: component.required ?? false,\n validator: (value: string) => {\n if (component.required && (!value || value.trim() === '')) {\n return t('validations.required.field.error');\n }\n if (ruleValidator && value) {\n const ruleMessage = ruleValidator(value);\n if (ruleMessage) {\n return t(ruleMessage);\n }\n }\n return null;\n },\n };\n }),\n [t],\n );\n\n const formFields: FormField[] = useMemo(\n () => extractFormFields(currentFlow?.data?.components ?? []),\n [currentFlow, extractFormFields],\n );\n\n const {\n values: formValues,\n touched: touchedFields,\n errors: formErrors,\n isValid: isFormValid,\n setValue: setFormValue,\n setTouched: setFormTouched,\n setTouchedFields,\n setErrors: setFormErrors,\n clearErrors: clearFormErrors,\n validateForm,\n touchAllFields,\n reset: resetForm,\n } = useForm<Record<string, string>>({\n fields: formFields,\n initialValues: {},\n requiredMessage: t('validations.required.field.error'),\n validateOnBlur: true,\n validateOnChange: false,\n });\n\n // Project server-side field errors into form state.\n useEffect(() => {\n clearFormErrors();\n const responseFieldErrors: FieldError[] | undefined = currentFlow?.data?.fieldErrors;\n if (!responseFieldErrors || responseFieldErrors.length === 0) {\n return;\n }\n const errors: Record<string, string> = {};\n const touched: Record<string, boolean> = {};\n responseFieldErrors.forEach((fe: FieldError) => {\n if (!(fe.identifier in errors)) {\n errors[fe.identifier] = fe.message;\n touched[fe.identifier] = true;\n }\n });\n setTouchedFields(touched);\n setFormErrors(errors);\n }, [currentFlow, setFormErrors, setTouchedFields, clearFormErrors]);\n\n const handleResponse = useCallback(\n (rawResponse: EmbeddedSelfFlowResponse): void => {\n const response: EmbeddedSelfFlowResponse = normalize(rawResponse);\n onFlowChange?.(response);\n\n if (response.challengeToken !== undefined) {\n challengeTokenRef.current = response.challengeToken ?? null;\n }\n\n if (response.flowStatus === EmbeddedSelfFlowStatus.Error) {\n const message: string = extractErrorMessage(response, t, 'user.verification.error');\n setError(message);\n setState('failed');\n onError?.(new Error(message));\n return;\n }\n\n if (response.flowStatus === EmbeddedSelfFlowStatus.Complete) {\n setError(null);\n setState('complete');\n onComplete?.(response);\n return;\n }\n\n // INCOMPLETE: render the step; a rejected code comes back with an error and without\n // additionalData, so keep the destination and code length of the step that sent the code.\n setCurrentFlow(\n (previous: EmbeddedSelfFlowResponse | null): EmbeddedSelfFlowResponse =>\n response.data?.additionalData || !previous?.data?.additionalData\n ? response\n : {...response, data: {...response.data, additionalData: previous.data.additionalData}},\n );\n resetForm();\n setError(response.error ? extractErrorMessage(response, t, 'user.verification.error') : null);\n setState('active');\n },\n [normalize, onFlowChange, onError, onComplete, resetForm, t],\n );\n\n const handleFailure = useCallback(\n (caughtError: unknown): void => {\n // Show a generic message; an API error body is not meant for the user.\n setError(t('user.verification.error'));\n setState('failed');\n onError?.(caughtError instanceof Error ? caughtError : new Error(String(caughtError)));\n },\n [onError, t],\n );\n\n useEffect(() => {\n if (!isInitialized || initializationAttemptedRef.current) {\n return;\n }\n initializationAttemptedRef.current = true;\n\n void (async (): Promise<void> => {\n try {\n const response: EmbeddedSelfFlowResponse | undefined = await onInitialize?.();\n if (response) {\n handleResponse(response);\n }\n } catch (caughtError) {\n handleFailure(caughtError);\n }\n })();\n }, [isInitialized, onInitialize, handleResponse, handleFailure]);\n\n const handleInputChange = (name: string, value: string): void => {\n setFormValue(name, value);\n };\n\n const handleInputBlur = (name: string): void => {\n setFormTouched(name, true);\n };\n\n const handleSubmit = async (\n component: EmbeddedFlowComponent,\n data?: Record<string, string>,\n skipValidation?: boolean,\n ): Promise<void> => {\n if (!currentFlow || isSubmittingRef.current) return;\n\n if (!skipValidation) {\n touchAllFields();\n if (!validateForm().isValid) return;\n }\n\n isSubmittingRef.current = true;\n setIsSubmitting(true);\n setError(null);\n\n try {\n const composedData: Record<string, string> = data ? composeAffixedInputs(data, vendor) : {};\n const inputs: Record<string, string> = Object.fromEntries(\n Object.entries(composedData).filter(\n ([, value]: [string, string]) => value !== null && value !== undefined && value !== '',\n ),\n );\n\n const response: EmbeddedSelfFlowResponse | undefined = await onSubmit?.({\n executionId: currentFlow.executionId,\n ...(component.id && {action: component.id}),\n ...(challengeTokenRef.current ? {challengeToken: challengeTokenRef.current} : {}),\n inputs,\n });\n\n if (response) {\n handleResponse(response);\n }\n } catch (caughtError) {\n handleFailure(caughtError);\n } finally {\n isSubmittingRef.current = false;\n setIsSubmitting(false);\n }\n };\n\n const additionalData: Record<string, unknown> = currentFlow?.data?.additionalData ?? {};\n const maskedDestination: unknown = additionalData[AttributeVerificationConstants.MASKED_DESTINATION_KEY];\n const destination: string | null =\n typeof maskedDestination === 'string' && maskedDestination ? maskedDestination : null;\n const headings: ReturnType<typeof getAuthComponentHeadings> = getAuthComponentHeadings(\n currentFlow?.data?.components ?? [],\n undefined,\n undefined,\n t('user.verification.heading', labelParams),\n t('user.verification.subheading.generic'),\n );\n const {componentsWithoutHeadings} = headings;\n const step: FlowStepComponents = getFlowStepComponents(componentsWithoutHeadings ?? []);\n // A step with nothing to enter or submit waits for the user to open a link sent to them.\n const isAwaitingLink: boolean = state === 'active' && formFields.length === 0 && step.actions.length === 0;\n const title: string = resolveFlowTemplateLiterals(headings.title, {meta, t});\n // The flow's own prompt wins; the destination is added beside it, or fills in when the flow has none.\n const hasFlowSubtitle = !!headings.headingComponents.subheading;\n let subtitle: string = resolveFlowTemplateLiterals(headings.subtitle, {meta, t});\n let destinationText: string | null = null;\n if (destination && hasFlowSubtitle) {\n destinationText = t('user.verification.destination', {destination});\n } else if (destination) {\n subtitle = t(isAwaitingLink ? 'user.verification.subheading.link' : 'user.verification.subheading', {destination});\n }\n\n const isLoading: boolean = state === 'loading' || isSubmitting;\n\n const containerClasses: string = cx(\n withVendorCSSClassPrefix('verify-attribute'),\n withVendorCSSClassPrefix(`verify-attribute--${size}`),\n withVendorCSSClassPrefix(`verify-attribute--${variant}`),\n styles['root'],\n variant === 'outlined' ? styles['card'] : '',\n className,\n );\n\n if (children) {\n const resend: EmbeddedFlowComponent | undefined = state === 'active' ? step.resend : undefined;\n\n return (\n <div className={containerClasses}>\n {children({\n additionalData,\n components: componentsWithoutHeadings ?? [],\n destination,\n destinationText,\n error,\n fieldErrors: formErrors,\n handleInputChange,\n handleResend: resend ? (): Promise<void> => handleSubmit(resend, undefined, true) : undefined,\n handleSubmit,\n isAwaitingLink,\n isComplete: state === 'complete',\n isFailed: state === 'failed',\n isLoading,\n isValid: isFormValid,\n onCancel,\n onDone: isAwaitingLink ? onDone : undefined,\n subtitle,\n successMessage: t('user.verification.success', labelParams),\n title,\n touched: touchedFields,\n values: formValues,\n })}\n </div>\n );\n }\n\n const alertClasses: string = cx(withVendorCSSClassPrefix(bem('verify-attribute', 'alert')), styles['alert']);\n\n return (\n <div className={containerClasses} aria-busy={isLoading}>\n {state === 'active' && (showTitle || showSubtitle) && (\n <div className={cx(withVendorCSSClassPrefix(bem('verify-attribute', 'header')), styles['header'])}>\n {showTitle && (\n <Typography\n variant=\"h5\"\n className={cx(withVendorCSSClassPrefix(bem('verify-attribute', 'heading')), styles['heading'])}\n >\n {title}\n </Typography>\n )}\n {showSubtitle && (\n <Typography\n variant=\"body2\"\n className={cx(withVendorCSSClassPrefix(bem('verify-attribute', 'subheading')), styles['subheading'])}\n >\n {subtitle}\n {destinationText && (\n <span className={withVendorCSSClassPrefix(bem('verify-attribute', 'destination'))}>\n {` ${destinationText}`}\n </span>\n )}\n </Typography>\n )}\n </div>\n )}\n\n {state === 'loading' && (\n <div className={cx(withVendorCSSClassPrefix(bem('verify-attribute', 'loading')), styles['loading'])}>\n <Spinner size=\"medium\" />\n </div>\n )}\n\n {error && (\n <AlertPrimitive variant=\"error\" className={alertClasses}>\n <AlertPrimitive.Description>{error}</AlertPrimitive.Description>\n </AlertPrimitive>\n )}\n\n {state === 'complete' && (\n <AlertPrimitive variant=\"success\" className={alertClasses}>\n <AlertPrimitive.Description>{t('user.verification.success', labelParams)}</AlertPrimitive.Description>\n </AlertPrimitive>\n )}\n\n {state === 'active' && (\n <div className={cx(withVendorCSSClassPrefix(bem('verify-attribute', 'content')), styles['content'])}>\n {withFlowTestIds(\n renderVerificationComponents(\n componentsWithoutHeadings ?? [],\n formValues,\n touchedFields,\n formErrors,\n isLoading,\n isFormValid,\n resetForm,\n handleInputChange,\n {\n _customRenderers: customRenderers,\n _theme: theme,\n additionalData,\n buttonClassName: cx(withVendorCSSClassPrefix(bem('verify-attribute', 'button')), buttonClassName),\n inputClassName: cx(withVendorCSSClassPrefix(bem('verify-attribute', 'input')), inputClassName),\n meta,\n onInputBlur: handleInputBlur,\n onSubmit: (component: EmbeddedFlowComponent, data?: Record<string, string>, skip?: boolean): void => {\n void handleSubmit(component, data, skip);\n },\n size,\n t,\n variant,\n vendor,\n },\n ),\n collectFlowIds(step),\n )}\n </div>\n )}\n\n {(!!onCancel || (isAwaitingLink && !!onDone)) && (\n <div className={cx(withVendorCSSClassPrefix(bem('verify-attribute', 'actions')), styles['actions'])}>\n {isAwaitingLink && onDone && (\n <Button\n size=\"small\"\n color=\"primary\"\n variant=\"solid\"\n onClick={onDone}\n data-testid=\"thunderid-action-verificationDone\"\n >\n {t('user.verification.buttons.done.text')}\n </Button>\n )}\n {onCancel && (\n <Button\n size=\"small\"\n color=\"secondary\"\n variant=\"outline\"\n onClick={onCancel}\n disabled={isSubmitting}\n data-testid=\"thunderid-action-verificationCancel\"\n >\n {state === 'active' || state === 'loading'\n ? t('user.verification.buttons.cancel.text')\n : t('user.verification.buttons.close.text')}\n </Button>\n )}\n </div>\n )}\n </div>\n );\n};\n\n/**\n * BaseVerifyAttribute renders the server-described steps of a self attribute verification flow.\n * Accepts API functions as props to stay framework independent.\n */\nconst BaseVerifyAttribute: FC<BaseVerifyAttributeProps> = ({\n preferences = undefined,\n ...rest\n}: BaseVerifyAttributeProps): ReactElement => {\n const content: ReactElement = <BaseVerifyAttributeContent {...rest} />;\n\n if (!preferences) return content;\n\n return <ComponentPreferencesContext.Provider value={preferences}>{content}</ComponentPreferencesContext.Provider>;\n};\n\nexport default BaseVerifyAttribute;\n"],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Theme } from '@thunderid/browser';
|
|
2
|
+
/**
|
|
3
|
+
* Creates styles for the BaseVerifyAttribute component
|
|
4
|
+
* @param theme - The theme object containing design tokens
|
|
5
|
+
* @param colorScheme - The current color scheme (used for memoization)
|
|
6
|
+
* @returns Object containing CSS class names for component styling
|
|
7
|
+
*/
|
|
8
|
+
declare const useStyles: (theme: Theme, colorScheme: string) => Record<string, string>;
|
|
9
|
+
export default useStyles;
|
|
10
|
+
//# sourceMappingURL=BaseVerifyAttribute.styles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseVerifyAttribute.styles.d.ts","sourceRoot":"","sources":["../../../../src/components/account/VerifyAttribute/BaseVerifyAttribute.styles.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,KAAK,EAA2B,MAAM,oBAAoB,CAAC;AAGnE;;;;;GAKG;AACH,QAAA,MAAM,SAAS,GAAI,OAAO,KAAK,EAAE,aAAa,MAAM,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CA4E3E,CAAC;AAEF,eAAe,SAAS,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { css } from "../../../styles/emotion.js";
|
|
2
|
+
import { withVendorCSSClassPrefix } from "@thunderid/browser";
|
|
3
|
+
|
|
4
|
+
//#region src/components/account/VerifyAttribute/BaseVerifyAttribute.styles.ts
|
|
5
|
+
/**
|
|
6
|
+
* Creates styles for the BaseVerifyAttribute component
|
|
7
|
+
* @param theme - The theme object containing design tokens
|
|
8
|
+
* @param colorScheme - The current color scheme (used for memoization)
|
|
9
|
+
* @returns Object containing CSS class names for component styling
|
|
10
|
+
*/
|
|
11
|
+
const useStyles = (theme, colorScheme) => {
|
|
12
|
+
const root = css`
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
gap: calc(${theme.vars.spacing.unit} * 2);
|
|
16
|
+
width: 100%;
|
|
17
|
+
font-family: ${theme.vars.typography.fontFamily};
|
|
18
|
+
`;
|
|
19
|
+
const card = css`
|
|
20
|
+
padding: calc(${theme.vars.spacing.unit} * 2);
|
|
21
|
+
border: 1px solid ${theme.vars.colors.border};
|
|
22
|
+
border-radius: ${theme.vars.borderRadius.medium};
|
|
23
|
+
`;
|
|
24
|
+
const header = css`
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-direction: column;
|
|
27
|
+
gap: calc(${theme.vars.spacing.unit} / 2);
|
|
28
|
+
`;
|
|
29
|
+
const heading = css`
|
|
30
|
+
margin: 0;
|
|
31
|
+
`;
|
|
32
|
+
const subheading = css`
|
|
33
|
+
margin: 0;
|
|
34
|
+
color: ${theme.vars.colors.text.secondary};
|
|
35
|
+
`;
|
|
36
|
+
const content = css`
|
|
37
|
+
display: flex;
|
|
38
|
+
flex-direction: column;
|
|
39
|
+
gap: calc(${theme.vars.spacing.unit} * 1.5);
|
|
40
|
+
|
|
41
|
+
.${withVendorCSSClassPrefix("otp-field__input-container")} {
|
|
42
|
+
justify-content: flex-start;
|
|
43
|
+
|
|
44
|
+
> input {
|
|
45
|
+
box-sizing: border-box;
|
|
46
|
+
flex: 1 1 calc(${theme.vars.spacing.unit} * 4);
|
|
47
|
+
max-width: calc(${theme.vars.spacing.unit} * 6);
|
|
48
|
+
min-width: 0;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
`;
|
|
52
|
+
const alert = css`
|
|
53
|
+
width: 100%;
|
|
54
|
+
`;
|
|
55
|
+
const loading = css`
|
|
56
|
+
display: flex;
|
|
57
|
+
justify-content: center;
|
|
58
|
+
padding: calc(${theme.vars.spacing.unit} * 2);
|
|
59
|
+
`;
|
|
60
|
+
return {
|
|
61
|
+
actions: css`
|
|
62
|
+
display: flex;
|
|
63
|
+
gap: ${theme.vars.spacing.unit};
|
|
64
|
+
align-items: center;
|
|
65
|
+
justify-content: flex-end;
|
|
66
|
+
`,
|
|
67
|
+
alert,
|
|
68
|
+
card,
|
|
69
|
+
colorScheme,
|
|
70
|
+
content,
|
|
71
|
+
header,
|
|
72
|
+
heading,
|
|
73
|
+
loading,
|
|
74
|
+
root,
|
|
75
|
+
subheading
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
var BaseVerifyAttribute_styles_default = useStyles;
|
|
79
|
+
|
|
80
|
+
//#endregion
|
|
81
|
+
export { BaseVerifyAttribute_styles_default as default };
|
|
82
|
+
//# sourceMappingURL=BaseVerifyAttribute.styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseVerifyAttribute.styles.js","names":["root: string","card: string","header: string","heading: string","subheading: string","content: string","alert: string","loading: string"],"sources":["../../../../src/components/account/VerifyAttribute/BaseVerifyAttribute.styles.ts"],"sourcesContent":["// Copyright 2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {Theme, withVendorCSSClassPrefix} from '@thunderid/browser';\nimport {css} from '../../../styles/emotion';\n\n/**\n * Creates styles for the BaseVerifyAttribute component\n * @param theme - The theme object containing design tokens\n * @param colorScheme - The current color scheme (used for memoization)\n * @returns Object containing CSS class names for component styling\n */\nconst useStyles = (theme: Theme, colorScheme: string): Record<string, string> => {\n const root: string = css`\n display: flex;\n flex-direction: column;\n gap: calc(${theme.vars.spacing.unit} * 2);\n width: 100%;\n font-family: ${theme.vars.typography.fontFamily};\n `;\n\n const card: string = css`\n padding: calc(${theme.vars.spacing.unit} * 2);\n border: 1px solid ${theme.vars.colors.border};\n border-radius: ${theme.vars.borderRadius.medium};\n `;\n\n const header: string = css`\n display: flex;\n flex-direction: column;\n gap: calc(${theme.vars.spacing.unit} / 2);\n `;\n\n const heading: string = css`\n margin: 0;\n `;\n\n const subheading: string = css`\n margin: 0;\n color: ${theme.vars.colors.text.secondary};\n `;\n\n const content: string = css`\n display: flex;\n flex-direction: column;\n gap: calc(${theme.vars.spacing.unit} * 1.5);\n\n .${withVendorCSSClassPrefix('otp-field__input-container')} {\n justify-content: flex-start;\n\n > input {\n box-sizing: border-box;\n flex: 1 1 calc(${theme.vars.spacing.unit} * 4);\n max-width: calc(${theme.vars.spacing.unit} * 6);\n min-width: 0;\n }\n }\n `;\n\n const alert: string = css`\n width: 100%;\n `;\n\n const loading: string = css`\n display: flex;\n justify-content: center;\n padding: calc(${theme.vars.spacing.unit} * 2);\n `;\n\n const actions: string = css`\n display: flex;\n gap: ${theme.vars.spacing.unit};\n align-items: center;\n justify-content: flex-end;\n `;\n\n return {\n actions,\n alert,\n card,\n colorScheme,\n content,\n header,\n heading,\n loading,\n root,\n subheading,\n };\n};\n\nexport default useStyles;\n"],"mappings":";;;;;;;;;;AAYA,MAAM,aAAa,OAAc,gBAAgD;CAC/E,MAAMA,OAAe,GAAG;;;gBAGV,MAAM,KAAK,QAAQ,KAAK;;mBAErB,MAAM,KAAK,WAAW,WAAW;;CAGlD,MAAMC,OAAe,GAAG;oBACN,MAAM,KAAK,QAAQ,KAAK;wBACpB,MAAM,KAAK,OAAO,OAAO;qBAC5B,MAAM,KAAK,aAAa,OAAO;;CAGlD,MAAMC,SAAiB,GAAG;;;gBAGZ,MAAM,KAAK,QAAQ,KAAK;;CAGtC,MAAMC,UAAkB,GAAG;;;CAI3B,MAAMC,aAAqB,GAAG;;aAEnB,MAAM,KAAK,OAAO,KAAK,UAAU;;CAG5C,MAAMC,UAAkB,GAAG;;;gBAGb,MAAM,KAAK,QAAQ,KAAK;;OAEjC,yBAAyB,6BAA6B,CAAC;;;;;yBAKrC,MAAM,KAAK,QAAQ,KAAK;0BACvB,MAAM,KAAK,QAAQ,KAAK;;;;;CAMhD,MAAMC,QAAgB,GAAG;;;CAIzB,MAAMC,UAAkB,GAAG;;;oBAGT,MAAM,KAAK,QAAQ,KAAK;;AAU1C,QAAO;EACL,SARsB,GAAG;;WAElB,MAAM,KAAK,QAAQ,KAAK;;;;EAO/B;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;AAGH,yCAAe"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { BaseVerifyAttributeProps } from './BaseVerifyAttribute';
|
|
3
|
+
export type VerifyAttributeProps = Omit<BaseVerifyAttributeProps, 'onInitialize' | 'onSubmit' | 'isInitialized'> & {
|
|
4
|
+
/**
|
|
5
|
+
* Name of the signed-in user's attribute to verify, such as `email`. Only a top-level, single-valued
|
|
6
|
+
* `email` or `mobile` property of the user's schema can be verified; any other is refused without a
|
|
7
|
+
* request, through `onError`, with `VerifyAttribute-handleInitialize-RuntimeError-001`.
|
|
8
|
+
*/
|
|
9
|
+
attribute: string;
|
|
10
|
+
/**
|
|
11
|
+
* Handle of the `SELF` flow to run. Defaults to the server's shipped
|
|
12
|
+
* `default-self-attribute-verification-flow`.
|
|
13
|
+
*/
|
|
14
|
+
flowHandle?: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* VerifyAttribute verifies one of the signed-in user's attributes, such as their email address,
|
|
18
|
+
* by running a self attribute verification flow, named by `flowHandle`. The flow picks the email or
|
|
19
|
+
* mobile path from the attribute's type, sends a code or a link to the attribute's current value,
|
|
20
|
+
* and the component renders the steps it describes; for a link it shows a Done action that calls
|
|
21
|
+
* `onDone`. Every request carries the user's access token. Once the server records the verification,
|
|
22
|
+
* and when the user selects Done, it re-reads the user's profile so `useUser()` shows the new status.
|
|
23
|
+
*
|
|
24
|
+
* It waits for the user schema to load, and refuses an attribute the schema does not declare as an
|
|
25
|
+
* `email` or `mobile` property, since starting a verification sends a message.
|
|
26
|
+
*
|
|
27
|
+
* Mount it with a `key` per attribute: each mount starts a new verification and sends a new code
|
|
28
|
+
* or link.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```tsx
|
|
32
|
+
* <VerifyAttribute
|
|
33
|
+
* attribute="email"
|
|
34
|
+
* onComplete={() => setOpen(false)}
|
|
35
|
+
* onCancel={() => setOpen(false)}
|
|
36
|
+
* />
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
declare const VerifyAttribute: FC<VerifyAttributeProps>;
|
|
40
|
+
export default VerifyAttribute;
|
|
41
|
+
//# sourceMappingURL=VerifyAttribute.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VerifyAttribute.d.ts","sourceRoot":"","sources":["../../../../src/components/account/VerifyAttribute/VerifyAttribute.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAC,EAAE,EAA4B,MAAM,OAAO,CAAC;AACpD,OAA4B,EAAC,wBAAwB,EAAC,MAAM,uBAAuB,CAAC;AAUpF,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,wBAAwB,EAAE,cAAc,GAAG,UAAU,GAAG,eAAe,CAAC,GAAG;IACjH;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,QAAA,MAAM,eAAe,EAAE,EAAE,CAAC,oBAAoB,CA6E7C,CAAC;AAEF,eAAe,eAAe,CAAC"}
|