keycloak_theme/src/login/pages/shared/UserProfileFormFields.tsx

178 lines
9.3 KiB
TypeScript
Raw Normal View History

2023-03-18 06:14:05 +01:00
import { useEffect, Fragment } from "react";
2023-03-21 05:27:31 +01:00
import type { ClassKey } from "keycloakify/login/TemplateProps";
2023-03-18 06:14:05 +01:00
import { clsx } from "keycloakify/tools/clsx";
2023-03-19 23:12:45 +01:00
import { useFormValidation } from "keycloakify/login/lib/useFormValidation";
import type { Attribute } from "keycloakify/login/kcContext/KcContext";
import type { 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-06-19 03:16:31 +02:00
const { advancedMsg, msg } = i18n;
const {
formValidationState: { fieldStateByAttributeName, isFormSubmittable },
2023-03-08 22:24:51 +01:00
formValidationDispatch,
attributesWithPassword
2023-03-08 22:24:51 +01:00
} = useFormValidation({
kcContext,
i18n
});
useEffect(() => {
onIsFormSubmittableValueChange(isFormSubmittable);
}, [isFormSubmittable]);
let currentGroup = "";
2022-09-09 02:07:29 +02:00
return (
<>
{attributesWithPassword.map((attribute, i) => {
const { group = "", groupDisplayHeader = "", groupDisplayDescription = "" } = attribute;
2022-09-09 02:07:29 +02: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
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")}>
{advancedMsg(groupDisplayHeader) || currentGroup}
2022-09-09 02:07:29 +02:00
</label>
</div>
{groupDisplayDescription !== "" && (
2023-03-18 06:14:05 +01:00
<div className={getClassName("kcLabelWrapperClass")}>
<label id={`description-${group}`} className={getClassName("kcLabelClass")}>
{advancedMsg(groupDisplayDescription)}
</label>
</div>
)}
</div>
)}
2022-09-09 02:07:29 +02: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")}>
{advancedMsg(attribute.displayName ?? "")}
</label>
{attribute.required && <>*</>}
</div>
2023-03-18 06:14:05 +01:00
<div className={getClassName("kcInputWrapperClass")}>
{(() => {
const { options } = attribute.validators;
if (options !== undefined) {
2022-09-09 02:07:29 +02:00
return (
<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
})
}
value={value}
>
2023-06-19 03:16:31 +02:00
<>
<option value="" selected disabled hidden>
{msg("selectAnOption")}
</option>
2023-06-19 03:16:31 +02:00
{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")}
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")}
style={{
"position": displayableErrors.length === 1 ? "absolute" : undefined
}}
aria-live="polite"
>
{displayableErrors.map(({ errorMessage }) => errorMessage)}
</span>
</>
2022-09-09 02:07:29 +02:00
);
})()}
</div>
</div>
{AfterField && <AfterField attribute={attribute} />}
</Fragment>
);
})}
</>
);
}