diff --git a/src/login/Template.tsx b/src/login/Template.tsx index 79ce2a33..bbcdd488 100644 --- a/src/login/Template.tsx +++ b/src/login/Template.tsx @@ -69,7 +69,7 @@ export default function Template(props: TemplateProps) { "htmlClassName": getClassName("kcHtmlClass"), "bodyClassName": getClassName("kcBodyClass"), "htmlLangProperty": locale?.currentLanguageTag, - "documentTitle": i18n.msgStr("loginTitle", kcContext.realm.displayName) + "documentTitle": msgStr("loginTitle", kcContext.realm.displayName) }); if (!isReady) { diff --git a/src/login/i18n/i18n.tsx b/src/login/i18n/i18n.tsx index bc6efaf5..ac5e5378 100644 --- a/src/login/i18n/i18n.tsx +++ b/src/login/i18n/i18n.tsx @@ -214,7 +214,7 @@ const keycloakifyExtraMessages = { "notAValidOption": "Not a valid option", "selectAnOption": "Select an option", "remove": "Remove", - "add value": "Add value" + "addValue": "Add value" }, "fr": { /* spell-checker: disable */ @@ -229,7 +229,7 @@ const keycloakifyExtraMessages = { "doLogout": "Se déconnecter", "selectAnOption": "Sélectionner une option", "remove": "Supprimer", - "add value": "Ajouter une valeur" + "addValue": "Ajouter une valeur" /* spell-checker: enable */ } }; diff --git a/src/login/kcContext/KcContext.ts b/src/login/kcContext/KcContext.ts index 9a7b028d..6cf37644 100644 --- a/src/login/kcContext/KcContext.ts +++ b/src/login/kcContext/KcContext.ts @@ -538,7 +538,17 @@ export type Attribute = { html5DataAnnotations: Record; readOnly: boolean; validators: Validators; - annotations: Record; + annotations: { + inputType?: string; + inputTypeSize?: `${number}`; + inputOptionsFromValidation?: string; + inputOptionLabels?: Record; + inputOptionLabelsI18nPrefix?: string; + inputTypeCols?: `${number}`; + inputTypeRows?: `${number}`; + inputTypeMaxlength?: `${number}`; + inputHelperTextBefore?: string; + }; multivalued?: boolean; autocomplete?: | "on" diff --git a/src/login/pages/shared/UserProfileFormFields.tsx b/src/login/pages/shared/UserProfileFormFields.tsx index 45c7ff77..039883dd 100644 --- a/src/login/pages/shared/UserProfileFormFields.tsx +++ b/src/login/pages/shared/UserProfileFormFields.tsx @@ -449,6 +449,128 @@ function InputFiledByType(props: PropsOfInputFiledByType) { } } +function InputTagSelects(props: PropsOfInputFiledByType) { + /* +<#macro inputTagSelects attribute> + <#if attribute.annotations.inputType=='select-radiobuttons'> + <#assign inputType='radio'> + <#assign classDiv=properties.kcInputClassRadio!> + <#assign classInput=properties.kcInputClassRadioInput!> + <#assign classLabel=properties.kcInputClassRadioLabel!> + <#else> + <#assign inputType='checkbox'> + <#assign classDiv=properties.kcInputClassCheckbox!> + <#assign classInput=properties.kcInputClassCheckboxInput!> + <#assign classLabel=properties.kcInputClassCheckboxLabel!> + + + <#if attribute.annotations.inputOptionsFromValidation?? && attribute.validators[attribute.annotations.inputOptionsFromValidation]?? && attribute.validators[attribute.annotations.inputOptionsFromValidation].options??> + <#assign options=attribute.validators[attribute.annotations.inputOptionsFromValidation].options> + <#elseif attribute.validators.options?? && attribute.validators.options.options??> + <#assign options=attribute.validators.options.options> + <#else> + <#assign options=[]> + + + <#list options as option> +
+ disabled + <#if attribute.values?seq_contains(option)>checked + /> + +
+ + + */ + + const { attribute, formValidationDispatch, getClassName, index } = 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 options = (() => { + walk: { + const { inputOptionsFromValidation } = attribute.annotations; + + assert(typeof inputOptionsFromValidation === "string"); + + if (inputOptionsFromValidation === undefined) { + break walk; + } + + const validator = (attribute.validators as Record)[inputOptionsFromValidation]; + + if (validator === undefined) { + break walk; + } + + if (validator.options === undefined) { + break walk; + } + + return validator.options; + } + + return attribute.validators.options?.options ?? []; + })(); + + return ( + <> + {options.map(option => ( +
+ + formValidationDispatch({ + "action": "update value", + "name": attribute.name, + "index": props.index, + "newValue": option + }) + } + onBlur={() => + formValidationDispatch({ + "action": "focus lost", + "name": attribute.name, + "index": props.index + }) + } + /> + +
+ ))} + + ); +} + function TextareaTag(props: PropsOfInputFiledByType) { const { attribute, index, value, formValidationDispatch, getClassName, displayableErrors } = props; @@ -481,3 +603,86 @@ function TextareaTag(props: PropsOfInputFiledByType) { /> ); } + +function SelectTag(props: PropsOfInputFiledByType) { + const { attribute, index, value, formValidationDispatch, getClassName, displayableErrors, i18n } = props; + + const { advancedMsg } = i18n; + + const isMultiple = attribute.annotations.inputType === "multiselect"; + + return ( + + ); +}