2023-03-18 06:14:05 +01:00
|
|
|
import { useEffect, Fragment } from "react";
|
|
|
|
import type { ClassKey } from "keycloakify/pages/PageProps";
|
|
|
|
import { clsx } from "keycloakify/tools/clsx";
|
|
|
|
import { useFormValidation } from "keycloakify/lib/useFormValidation";
|
|
|
|
import type { Attribute } from "keycloakify/kcContext";
|
|
|
|
import type { I18nBase as I18n } from "../../i18n";
|
2022-09-09 02:07:29 +02:00
|
|
|
|
|
|
|
export type UserProfileFormFieldsProps = {
|
2023-03-08 22:24:51 +01:00
|
|
|
kcContext: Parameters<typeof useFormValidation>[0]["kcContext"];
|
2023-03-18 06:14:05 +01:00
|
|
|
i18n: I18n;
|
|
|
|
getClassName: (classKey: ClassKey) => string;
|
|
|
|
onIsFormSubmittableValueChange: (isFormSubmittable: boolean) => void;
|
|
|
|
BeforeField?: (props: { attribute: Attribute }) => JSX.Element | null;
|
|
|
|
AfterField?: (props: { attribute: Attribute }) => JSX.Element | null;
|
|
|
|
};
|
|
|
|
|
|
|
|
export function UserProfileFormFields(props: UserProfileFormFieldsProps) {
|
|
|
|
const { kcContext, onIsFormSubmittableValueChange, i18n, getClassName, BeforeField, AfterField } = props;
|
2022-09-09 02:07:29 +02:00
|
|
|
|
2023-02-25 18:11:23 +01:00
|
|
|
const { advancedMsg } = i18n;
|
|
|
|
|
|
|
|
const {
|
|
|
|
formValidationState: { fieldStateByAttributeName, isFormSubmittable },
|
2023-03-08 22:24:51 +01:00
|
|
|
formValidationDispatch,
|
2023-02-25 18:11:23 +01:00
|
|
|
attributesWithPassword
|
2023-03-08 22:24:51 +01:00
|
|
|
} = useFormValidation({
|
2023-02-25 18:11:23 +01:00
|
|
|
kcContext,
|
|
|
|
i18n
|
|
|
|
});
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
onIsFormSubmittableValueChange(isFormSubmittable);
|
|
|
|
}, [isFormSubmittable]);
|
|
|
|
|
|
|
|
let currentGroup = "";
|
2022-09-09 02:07:29 +02:00
|
|
|
|
2023-02-25 18:11:23 +01:00
|
|
|
return (
|
|
|
|
<>
|
|
|
|
{attributesWithPassword.map((attribute, i) => {
|
|
|
|
const { group = "", groupDisplayHeader = "", groupDisplayDescription = "" } = attribute;
|
2022-09-09 02:07:29 +02:00
|
|
|
|
2023-02-25 18:11:23 +01:00
|
|
|
const { value, displayableErrors } = fieldStateByAttributeName[attribute.name];
|
2022-09-09 02:07:29 +02:00
|
|
|
|
2023-03-18 06:14:05 +01:00
|
|
|
const formGroupClassName = clsx(
|
|
|
|
getClassName("kcFormGroupClass"),
|
|
|
|
displayableErrors.length !== 0 && getClassName("kcFormGroupErrorClass")
|
|
|
|
);
|
2022-09-09 02:07:29 +02:00
|
|
|
|
2023-02-25 18:11:23 +01:00
|
|
|
return (
|
|
|
|
<Fragment key={i}>
|
|
|
|
{group !== currentGroup && (currentGroup = group) !== "" && (
|
2022-09-09 02:07:29 +02:00
|
|
|
<div className={formGroupClassName}>
|
2023-03-18 06:14:05 +01:00
|
|
|
<div className={getClassName("kcContentWrapperClass")}>
|
|
|
|
<label id={`header-${group}`} className={getClassName("kcFormGroupHeader")}>
|
2023-02-25 18:11:23 +01:00
|
|
|
{advancedMsg(groupDisplayHeader) || currentGroup}
|
2022-09-09 02:07:29 +02:00
|
|
|
</label>
|
|
|
|
</div>
|
2023-02-25 18:11:23 +01:00
|
|
|
{groupDisplayDescription !== "" && (
|
2023-03-18 06:14:05 +01:00
|
|
|
<div className={getClassName("kcLabelWrapperClass")}>
|
|
|
|
<label id={`description-${group}`} className={getClassName("kcLabelClass")}>
|
2023-02-25 18:11:23 +01:00
|
|
|
{advancedMsg(groupDisplayDescription)}
|
|
|
|
</label>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
)}
|
2022-09-09 02:07:29 +02:00
|
|
|
|
2023-02-25 18:11:23 +01:00
|
|
|
{BeforeField && <BeforeField attribute={attribute} />}
|
|
|
|
|
|
|
|
<div className={formGroupClassName}>
|
2023-03-18 06:14:05 +01:00
|
|
|
<div className={getClassName("kcLabelWrapperClass")}>
|
|
|
|
<label htmlFor={attribute.name} className={getClassName("kcLabelClass")}>
|
2023-02-25 18:11:23 +01:00
|
|
|
{advancedMsg(attribute.displayName ?? "")}
|
|
|
|
</label>
|
|
|
|
{attribute.required && <>*</>}
|
|
|
|
</div>
|
2023-03-18 06:14:05 +01:00
|
|
|
<div className={getClassName("kcInputWrapperClass")}>
|
2023-02-25 18:11:23 +01:00
|
|
|
{(() => {
|
|
|
|
const { options } = attribute.validators;
|
|
|
|
|
|
|
|
if (options !== undefined) {
|
2022-09-09 02:07:29 +02:00
|
|
|
return (
|
2023-02-25 18:11:23 +01:00
|
|
|
<select
|
2022-09-09 02:07:29 +02:00
|
|
|
id={attribute.name}
|
|
|
|
name={attribute.name}
|
2023-03-08 22:24:51 +01:00
|
|
|
onChange={event =>
|
|
|
|
formValidationDispatch({
|
|
|
|
"action": "update value",
|
|
|
|
"name": attribute.name,
|
|
|
|
"newValue": event.target.value
|
|
|
|
})
|
|
|
|
}
|
|
|
|
onBlur={() =>
|
|
|
|
formValidationDispatch({
|
|
|
|
"action": "focus lost",
|
|
|
|
"name": attribute.name
|
|
|
|
})
|
|
|
|
}
|
2023-02-25 18:11:23 +01:00
|
|
|
value={value}
|
|
|
|
>
|
|
|
|
{options.options.map(option => (
|
|
|
|
<option key={option} value={option}>
|
|
|
|
{option}
|
|
|
|
</option>
|
|
|
|
))}
|
|
|
|
</select>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<input
|
|
|
|
type={(() => {
|
|
|
|
switch (attribute.name) {
|
|
|
|
case "password-confirm":
|
|
|
|
case "password":
|
|
|
|
return "password";
|
|
|
|
default:
|
|
|
|
return "text";
|
|
|
|
}
|
|
|
|
})()}
|
|
|
|
id={attribute.name}
|
|
|
|
name={attribute.name}
|
|
|
|
value={value}
|
2023-03-08 22:24:51 +01:00
|
|
|
onChange={event =>
|
|
|
|
formValidationDispatch({
|
|
|
|
"action": "update value",
|
|
|
|
"name": attribute.name,
|
|
|
|
"newValue": event.target.value
|
|
|
|
})
|
|
|
|
}
|
|
|
|
onBlur={() =>
|
|
|
|
formValidationDispatch({
|
|
|
|
"action": "focus lost",
|
|
|
|
"name": attribute.name
|
|
|
|
})
|
|
|
|
}
|
2023-03-18 06:14:05 +01:00
|
|
|
className={getClassName("kcInputClass")}
|
2023-02-25 18:11:23 +01:00
|
|
|
aria-invalid={displayableErrors.length !== 0}
|
|
|
|
disabled={attribute.readOnly}
|
|
|
|
autoComplete={attribute.autocomplete}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
})()}
|
|
|
|
{displayableErrors.length !== 0 &&
|
|
|
|
(() => {
|
|
|
|
const divId = `input-error-${attribute.name}`;
|
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<style>{`#${divId} > span: { display: block; }`}</style>
|
|
|
|
<span
|
|
|
|
id={divId}
|
2023-03-18 06:14:05 +01:00
|
|
|
className={getClassName("kcInputErrorMessageClass")}
|
2023-02-25 18:11:23 +01:00
|
|
|
style={{
|
|
|
|
"position": displayableErrors.length === 1 ? "absolute" : undefined
|
|
|
|
}}
|
|
|
|
aria-live="polite"
|
|
|
|
>
|
|
|
|
{displayableErrors.map(({ errorMessage }) => errorMessage)}
|
|
|
|
</span>
|
|
|
|
</>
|
2022-09-09 02:07:29 +02:00
|
|
|
);
|
|
|
|
})()}
|
|
|
|
</div>
|
2023-02-25 18:11:23 +01:00
|
|
|
</div>
|
|
|
|
{AfterField && <AfterField attribute={attribute} />}
|
|
|
|
</Fragment>
|
|
|
|
);
|
|
|
|
})}
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
}
|