useFormValidationSlice: update when params have changed

This commit is contained in:
garronej 2021-10-26 18:09:37 +02:00
parent 5bc84b621c
commit 6c874c01b7
3 changed files with 69 additions and 67 deletions

View File

@ -25,8 +25,6 @@
- Out of the box [frontend form validation](#user-profile-and-frontend-form-validation) 🥳 - Out of the box [frontend form validation](#user-profile-and-frontend-form-validation) 🥳
- Improvements (and breaking changes in `import { useKcMessage } from "keycloakify"`. - Improvements (and breaking changes in `import { useKcMessage } from "keycloakify"`.
**NOTICE**: [`powerhooks`](https://www.npmjs.com/package/powerhooks) have to be updated ([it's a peerDependency since v3](#v3))
# 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.

View File

@ -56,7 +56,7 @@
"homepage": "https://github.com/garronej/keycloakify", "homepage": "https://github.com/garronej/keycloakify",
"peerDependencies": { "peerDependencies": {
"@emotion/react": "^11.4.1", "@emotion/react": "^11.4.1",
"powerhooks": "^0.11.0", "powerhooks": "^0.10.0",
"react": "^16.8.0 || ^17.0.0", "react": "^16.8.0 || ^17.0.0",
"tss-react": "^1.1.0" "tss-react": "^1.1.0"
}, },

View File

@ -5,7 +5,6 @@ import { useKcMessage } from "./i18n/useKcMessage";
import { useConstCallback } from "powerhooks/useConstCallback"; import { useConstCallback } from "powerhooks/useConstCallback";
import { id } from "tsafe/id"; import { id } from "tsafe/id";
import type { MessageKey } from "./i18n/useKcMessage"; import type { MessageKey } from "./i18n/useKcMessage";
import { useConst } from "powerhooks/useConst";
import { emailRegexp } from "./tools/emailRegExp"; import { emailRegexp } from "./tools/emailRegExp";
export type KcContextLike = { export type KcContextLike = {
@ -278,6 +277,7 @@ export function useFormValidationSlice(params: {
passwordRequired: boolean; passwordRequired: boolean;
realm: { registrationEmailAsUsername: boolean }; realm: { registrationEmailAsUsername: boolean };
}; };
/** NOTE: Try to avoid passing a new ref every render for better performances. */
passwordValidators?: Validators; passwordValidators?: Validators;
}) { }) {
const { const {
@ -290,49 +290,51 @@ export function useFormValidationSlice(params: {
}, },
} = params; } = params;
const attributesWithPassword = useConst(() => const attributesWithPassword = useMemo(
!kcContext.passwordRequired () =>
? kcContext.profile.attributes !kcContext.passwordRequired
: (() => { ? kcContext.profile.attributes
const name = kcContext.realm.registrationEmailAsUsername ? "email" : "username"; : (() => {
const name = kcContext.realm.registrationEmailAsUsername ? "email" : "username";
return kcContext.profile.attributes.reduce<Attribute[]>( return kcContext.profile.attributes.reduce<Attribute[]>(
(prev, curr) => [ (prev, curr) => [
...prev, ...prev,
...(curr.name !== name ...(curr.name !== name
? [curr] ? [curr]
: [ : [
curr, curr,
id<Attribute>({ id<Attribute>({
"name": "password", "name": "password",
"displayName": id<`\${${MessageKey}}`>("${password}"), "displayName": id<`\${${MessageKey}}`>("${password}"),
"required": true, "required": true,
"readOnly": false, "readOnly": false,
"validators": passwordValidators, "validators": passwordValidators,
"annotations": {}, "annotations": {},
"groupAnnotations": {}, "groupAnnotations": {},
}), }),
id<Attribute>({ id<Attribute>({
"name": "password-confirm", "name": "password-confirm",
"displayName": id<`\${${MessageKey}}`>("${passwordConfirm}"), "displayName": id<`\${${MessageKey}}`>("${passwordConfirm}"),
"required": true, "required": true,
"readOnly": false, "readOnly": false,
"validators": { "validators": {
"_compareToOther": { "_compareToOther": {
"name": "password", "name": "password",
"ignore.empty.value": true, "ignore.empty.value": true,
"shouldBe": "equal", "shouldBe": "equal",
"error-message": id<`\${${MessageKey}}`>("${invalidPasswordConfirmMessage}"), "error-message": id<`\${${MessageKey}}`>("${invalidPasswordConfirmMessage}"),
},
}, },
}, "annotations": {},
"annotations": {}, "groupAnnotations": {},
"groupAnnotations": {}, }),
}), ]),
]), ],
], [],
[], );
); })(),
})(), [kcContext, passwordValidators],
); );
const { getErrors } = useGetErrors({ const { getErrors } = useGetErrors({
@ -344,27 +346,29 @@ export function useFormValidationSlice(params: {
}, },
}); });
const initialInternalState = useConst(() => const initialInternalState = useMemo(
Object.fromEntries( () =>
attributesWithPassword Object.fromEntries(
.map(attribute => ({ attributesWithPassword
attribute, .map(attribute => ({
"errors": getErrors({ attribute,
"name": attribute.name, "errors": getErrors({
"fieldValueByAttributeName": Object.fromEntries( "name": attribute.name,
attributesWithPassword.map(({ name, value }) => [name, { "value": value ?? "" }]), "fieldValueByAttributeName": Object.fromEntries(
), attributesWithPassword.map(({ name, value }) => [name, { "value": value ?? "" }]),
}), ),
})) }),
.map(({ attribute, errors }) => [ }))
attribute.name, .map(({ attribute, errors }) => [
{ attribute.name,
"value": attribute.value ?? "", {
errors, "value": attribute.value ?? "",
"doDisplayPotentialErrorMessages": errors.length !== 0, errors,
}, "doDisplayPotentialErrorMessages": errors.length !== 0,
]), },
), ]),
),
[attributesWithPassword],
); );
type InternalState = typeof initialInternalState; type InternalState = typeof initialInternalState;
@ -421,7 +425,7 @@ export function useFormValidationSlice(params: {
errors.length === 0 && (value !== "" || !attributesWithPassword.find(attribute => attribute.name === name)!.required), errors.length === 0 && (value !== "" || !attributesWithPassword.find(attribute => attribute.name === name)!.required),
), ),
}), }),
[formValidationInternalState], [formValidationInternalState, attributesWithPassword],
); );
return { formValidationState, formValidationReducer, attributesWithPassword }; return { formValidationState, formValidationReducer, attributesWithPassword };