Add support for options validator

This commit is contained in:
garronej 2022-03-18 00:46:12 +01:00
parent 02e2ad89ec
commit bccb56ed61
5 changed files with 87 additions and 22 deletions

View File

@ -30,6 +30,10 @@
<img src="https://user-images.githubusercontent.com/6702424/110260457-a1c3d380-7fac-11eb-853a-80459b65626b.png"> <img src="https://user-images.githubusercontent.com/6702424/110260457-a1c3d380-7fac-11eb-853a-80459b65626b.png">
</p> </p>
> New in v4.7:
> Register with user profile enabled: Out of the box `options` validator support.
> [Example](https://user-images.githubusercontent.com/6702424/158911163-81e6bbe8-feb0-4dc8-abff-de199d7a678e.mov)
# Motivations # Motivations
Keycloak provides [theme support](https://www.keycloak.org/docs/latest/server_development/#_themes) for web pages. This allows customizing the look and feel of end-user facing pages so they can be integrated with your applications. Keycloak provides [theme support](https://www.keycloak.org/docs/latest/server_development/#_themes) for web pages. This allows customizing the look and feel of end-user facing pages so they can be integrated with your applications.
@ -474,6 +478,11 @@ and `kcRegisterContext["authorizedMailDomains"]` to validate on.
# Changelog highlights # Changelog highlights
# v4.7.0
Register with user profile enabled: Out of the box `options` validator support.
[Example](https://user-images.githubusercontent.com/6702424/158911163-81e6bbe8-feb0-4dc8-abff-de199d7a678e.mov)
# v4.6.0 # v4.6.0
`tss-react` and `powerhooks` are no longer peer dependencies of `keycloakify`. `tss-react` and `powerhooks` are no longer peer dependencies of `keycloakify`.

View File

@ -95,7 +95,7 @@ const UserProfileFormFields = memo(({ kcContext, onIsFormSubmittableValueChange,
{ {
target: { value }, target: { value },
}, },
]: [React.ChangeEvent<HTMLInputElement>], ]: [React.ChangeEvent<HTMLInputElement | HTMLSelectElement>],
) => ) =>
formValidationReducer({ formValidationReducer({
"action": "update value", "action": "update value",
@ -148,26 +148,50 @@ const UserProfileFormFields = memo(({ kcContext, onIsFormSubmittableValueChange,
{attribute.required && <>*</>} {attribute.required && <>*</>}
</div> </div>
<div className={cx(props.kcInputWrapperClass)}> <div className={cx(props.kcInputWrapperClass)}>
<input {(() => {
type={(() => { const { options } = attribute.validators;
switch (attribute.name) {
case "password-confirm": if (options !== undefined) {
case "password": return (
return "password"; <select
default: id={attribute.name}
return "text"; name={attribute.name}
} onChange={onChangeFactory(attribute.name)}
})()} onBlur={onBlurFactory(attribute.name)}
id={attribute.name} value={value}
name={attribute.name} >
value={value} {options.options.map(option => (
onChange={onChangeFactory(attribute.name)} <option key={option} value={option}>
className={cx(props.kcInputClass)} {option}
aria-invalid={displayableErrors.length !== 0} </option>
disabled={attribute.readOnly} ))}
autoComplete={attribute.autocomplete} </select>
onBlur={onBlurFactory(attribute.name)} );
/> }
return (
<input
type={(() => {
switch (attribute.name) {
case "password-confirm":
case "password":
return "password";
default:
return "text";
}
})()}
id={attribute.name}
name={attribute.name}
value={value}
onChange={onChangeFactory(attribute.name)}
className={cx(props.kcInputClass)}
aria-invalid={displayableErrors.length !== 0}
disabled={attribute.readOnly}
autoComplete={attribute.autocomplete}
onBlur={onBlurFactory(attribute.name)}
/>
);
})()}
{displayableErrors.length !== 0 && ( {displayableErrors.length !== 0 && (
<span <span
id={`input-error-${attribute.name}`} id={`input-error-${attribute.name}`}

View File

@ -315,6 +315,7 @@ export type Validators = Partial<{
name: string; name: string;
shouldBe: "equal" | "different"; shouldBe: "equal" | "different";
}; };
options: Validators.Options;
}>; }>;
export declare namespace Validators { export declare namespace Validators {
@ -331,6 +332,9 @@ export declare namespace Validators {
min?: `${number}`; min?: `${number}`;
max?: `${number}`; max?: `${number}`;
}; };
export type Options = {
options: string[];
};
} }
assert<Equals<KcContextBase["pageId"], PageId>>(); assert<Equals<KcContextBase["pageId"], PageId>>();

View File

@ -10,6 +10,7 @@ const kcMessages = {
"shouldBeDifferent": "{0} should be different to {1}", "shouldBeDifferent": "{0} should be different to {1}",
"shouldMatchPattern": "Pattern should match: `/{0}/`", "shouldMatchPattern": "Pattern should match: `/{0}/`",
"mustBeAnInteger": "Must be an integer", "mustBeAnInteger": "Must be an integer",
"notAValidOption": "Not a valid option",
}, },
"fr": { "fr": {
...kcMessagesBase["fr"], ...kcMessagesBase["fr"],
@ -18,6 +19,7 @@ const kcMessages = {
"shouldBeDifferent": "{0} doit être différent de {1}", "shouldBeDifferent": "{0} doit être différent de {1}",
"shouldMatchPattern": "Dois respecter le schéma: `/{0}/`", "shouldMatchPattern": "Dois respecter le schéma: `/{0}/`",
"mustBeAnInteger": "Doit être un nombre entiers", "mustBeAnInteger": "Doit être un nombre entiers",
"notAValidOption": "N'est pas une option valide",
/* spell-checker: enable */ /* spell-checker: enable */
}, },
}; };

View File

@ -213,7 +213,7 @@ export function useGetErrors(params: {
break scope; break scope;
} }
const msgArgs = ["invalidEmailMessage"] as const; const msgArgs = [id<MessageKey>("invalidEmailMessage")] as const;
errors.push({ errors.push({
validatorName, validatorName,
@ -276,6 +276,32 @@ export function useGetErrors(params: {
} }
} }
scope: {
const validatorName = "options";
const validator = validators[validatorName];
if (validator === undefined) {
break scope;
}
if (value === "") {
break scope;
}
if (validator.options.indexOf(value) >= 0) {
break scope;
}
const msgArgs = [id<MessageKey>("notAValidOption")] as const;
errors.push({
validatorName,
"errorMessage": <Fragment key={errors.length}>{advancedMsg(...msgArgs)}</Fragment>,
"errorMessageStr": advancedMsgStr(...msgArgs),
});
}
//TODO: Implement missing validators. //TODO: Implement missing validators.
return errors; return errors;