From b17724fdda88cd53e793ca96e9bc44c97c4835ed Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sun, 5 May 2024 18:51:33 +0200 Subject: [PATCH] Done with select tag --- .../pages/shared/UserProfileFormFields.tsx | 182 +++++++++++------- 1 file changed, 112 insertions(+), 70 deletions(-) diff --git a/src/login/pages/shared/UserProfileFormFields.tsx b/src/login/pages/shared/UserProfileFormFields.tsx index 039883dd..ea5393da 100644 --- a/src/login/pages/shared/UserProfileFormFields.tsx +++ b/src/login/pages/shared/UserProfileFormFields.tsx @@ -6,7 +6,7 @@ import { type KcContextLike, type FormAction, type FormFieldError, - FormFieldState + type FormFieldState } from "keycloakify/login/lib/useUserProfileForm"; import type { Attribute, LegacyAttribute } from "keycloakify/login/kcContext/KcContext"; import type { I18n } from "../../i18n"; @@ -23,11 +23,10 @@ export type UserProfileFormFieldsProps = { type BeforeAfterFieldProps = { attribute: Attribute; - index: number; - value: string; dispatchFormAction: React.Dispatch; - formFieldErrors: FormFieldError[]; + displayableErrors: FormFieldError[]; i18n: I18n; + valueOrValues: string | string[]; }; // NOTE: Enabled by default but it's a UX best practice to set it to false. @@ -55,14 +54,14 @@ export function UserProfileFormFields(props: UserProfileFormFieldsProps) { return ( <> - {formFieldStates.map(({ index, value, attribute, displayableErrors }) => { + {formFieldStates.map(({ attribute, displayableErrors, valueOrValues }) => { const formGroupClassName = clsx( getClassName("kcFormGroupClass"), displayableErrors.length !== 0 && getClassName("kcFormGroupErrorClass") ); return ( - + )}
*}
- {attribute.annotations.inputHelperTextBefore !== undefined && index === 0 && ( + {attribute.annotations.inputHelperTextBefore !== undefined && (
- {attribute.multivalued && ( + + {/*attribute.multivalued && ( - )} + )*/} {displayableErrors.length !== 0 && ( )} - {attribute.annotations.inputHelperTextAfter !== undefined && index === 0 && ( + {attribute.annotations.inputHelperTextAfter !== undefined && (
{advancedMsg(attribute.annotations.inputHelperTextAfter)} @@ -138,11 +137,10 @@ export function UserProfileFormFields(props: UserProfileFormFieldsProps) { {AfterField !== undefined && ( )} {/* @@ -254,27 +252,29 @@ function GroupLabel(props: { function FieldErrors(props: { attribute: Attribute; - index: number; getClassName: UserProfileFormFieldsProps["getClassName"]; displayableErrors: FormFieldError[]; + fieldIndex: number | undefined; }) { - const { attribute, index, getClassName, displayableErrors } = props; + const { attribute, getClassName, displayableErrors, fieldIndex } = props; return ( - {displayableErrors.map(({ errorMessage }, i, arr) => ( - <> - {errorMessage} - {arr.length - 1 !== i &&
} - - ))} + {displayableErrors + .filter(error => error.fieldIndex === fieldIndex) + .map(({ errorMessage }, i, arr) => ( + <> + {errorMessage} + {arr.length - 1 !== i &&
} + + ))}
); } @@ -398,8 +398,7 @@ function AddRemoveButtonsMultiValuedAttribute(props: { type PropsOfInputFiledByType = { attribute: Attribute; - index: number; - value: string; + valueOrValues: string | string[]; displayableErrors: FormFieldError[]; formValidationDispatch: React.Dispatch; getClassName: UserProfileFormFieldsProps["getClassName"]; @@ -407,7 +406,7 @@ type PropsOfInputFiledByType = { }; function InputFiledByType(props: PropsOfInputFiledByType) { - const { attribute } = props; + const { attribute, valueOrValues } = props; /* <#macro inputFieldByType attribute> @@ -445,10 +444,24 @@ function InputFiledByType(props: PropsOfInputFiledByType) { case "multiselect-checkboxes": return ; default: - return ; + if (valueOrValues instanceof Array) { + return ( + <> + {valueOrValues.map((...[, i]) => ( + + ))} + + ); + } + + return ; } } +function InputTag(props: PropsOfInputFiledByType & { fieldIndex: number | undefined }) { + return null; +} + function InputTagSelects(props: PropsOfInputFiledByType) { /* <#macro inputTagSelects attribute> @@ -485,31 +498,37 @@ function InputTagSelects(props: PropsOfInputFiledByType) { */ - const { attribute, formValidationDispatch, getClassName, index } = props; + const { attribute, formValidationDispatch, getClassName, valueOrValues } = props; const { advancedMsg } = props.i18n; - const { classDiv, classInput, classLabel, inputType } = - attribute.annotations.inputType === "select-radiobuttons" - ? { - "inputType": "radio", - "classDiv": getClassName("kcInputClassRadio"), - "classInput": getClassName("kcInputClassRadioInput"), - "classLabel": getClassName("kcInputClassRadioLabel") - } - : { - "inputType": "checkbox", - "classDiv": getClassName("kcInputClassCheckbox"), - "classInput": getClassName("kcInputClassCheckboxInput"), - "classLabel": getClassName("kcInputClassCheckboxLabel") - }; + const { classDiv, classInput, classLabel, inputType } = (() => { + const { inputType } = attribute.annotations; + + assert(inputType === "select-radiobuttons" || inputType === "multiselect-checkboxes"); + + switch (inputType) { + case "select-radiobuttons": + return { + "inputType": "radio", + "classDiv": getClassName("kcInputClassRadio"), + "classInput": getClassName("kcInputClassRadioInput"), + "classLabel": getClassName("kcInputClassRadioLabel") + }; + case "multiselect-checkboxes": + return { + "inputType": "checkbox", + "classDiv": getClassName("kcInputClassCheckbox"), + "classInput": getClassName("kcInputClassCheckboxInput"), + "classLabel": getClassName("kcInputClassCheckboxLabel") + }; + } + })(); const options = (() => { walk: { const { inputOptionsFromValidation } = attribute.annotations; - assert(typeof inputOptionsFromValidation === "string"); - if (inputOptionsFromValidation === undefined) { break walk; } @@ -536,26 +555,41 @@ function InputTagSelects(props: PropsOfInputFiledByType) {
+ checked={valueOrValues.includes(option)} + onChange={event => formValidationDispatch({ - "action": "update value", + "action": "update", "name": attribute.name, - "index": props.index, - "newValue": option + "valueOrValues": (() => { + const isChecked = event.target.checked; + + if (valueOrValues instanceof Array) { + const newValues = [...valueOrValues]; + + if (isChecked) { + newValues.push(option); + } else { + newValues.splice(newValues.indexOf(option), 1); + } + + return newValues; + } + + return event.target.checked ? option : ""; + })() }) } onBlur={() => formValidationDispatch({ "action": "focus lost", "name": attribute.name, - "index": props.index + "fieldIndex": undefined }) } /> @@ -572,11 +606,15 @@ function InputTagSelects(props: PropsOfInputFiledByType) { } function TextareaTag(props: PropsOfInputFiledByType) { - const { attribute, index, value, formValidationDispatch, getClassName, displayableErrors } = props; + const { attribute, formValidationDispatch, getClassName, displayableErrors, valueOrValues } = props; + + assert(typeof valueOrValues === "string"); + + const value = valueOrValues; return (