use valueOrValues to simplify type definitions

This commit is contained in:
Joseph Garrone 2024-05-04 21:27:08 +02:00
parent 6846a683b0
commit 52a6edc9ca

View File

@ -39,22 +39,11 @@ export namespace FormFieldError {
} }
} }
export type FormFieldState = FormFieldState.Simple | FormFieldState.MultiValued; export type FormFieldState = {
attribute: Attribute;
export namespace FormFieldState { displayableErrors: FormFieldError[];
export type Common = { valueOrValues: string | string[];
attribute: Attribute; };
displayableErrors: FormFieldError[];
};
export type Simple = Common & {
value: string;
};
export type MultiValued = Common & {
values: string[];
};
}
export type FormState = { export type FormState = {
isFormSubmittable: boolean; isFormSubmittable: boolean;
@ -65,21 +54,12 @@ export type FormAction =
| { | {
action: "update"; action: "update";
name: string; name: string;
value: string; valueOrValues: string | string[];
}
| {
action: "update multi-valued";
name: string;
values: string[];
} }
| { | {
action: "focus lost"; action: "focus lost";
name: string; name: string;
} fieldIndex: number | undefined;
| {
action: "multi-valued text input focus lost";
name: string;
fieldIndex: number;
}; };
export type KcContextLike = { export type KcContextLike = {
@ -104,23 +84,12 @@ export type ReturnTypeOfUseUserProfileForm = {
}; };
namespace internal { namespace internal {
export type FormFieldState = FormFieldState.Simple | FormFieldState.MultiValued; export type FormFieldState = {
attribute: Attribute;
export namespace FormFieldState { errors: FormFieldError[];
export type Common = { hasLostFocusAtLeastOnce: boolean | boolean[];
attribute: Attribute; valueOrValues: string | string[];
errors: FormFieldError[]; };
hasLostFocusAtLeastOnce: boolean | boolean[];
};
export type Simple = Common & {
value: string;
};
export type MultiValued = Common & {
values: string[];
};
}
export type State = { export type State = {
formFieldStates: FormFieldState[]; formFieldStates: FormFieldState[];
@ -193,24 +162,11 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
(() => { (() => {
switch (params.action) { switch (params.action) {
case "update": case "update":
case "update multi-valued": formFieldState.valueOrValues = params.valueOrValues;
(() => {
switch (params.action) {
case "update":
assert("value" in formFieldState);
formFieldState.value = params.value;
return;
case "update multi-valued":
assert("values" in formFieldState);
formFieldState.values = params.values;
return;
}
assert<Equals<typeof params, never>>(false);
})();
formFieldState.errors = getErrors({ formFieldState.errors = getErrors({
"attributeName": params.name, "attributeName": params.name,
"fieldValues": state.formFieldStates "formFieldStates": state.formFieldStates
}); });
update_password_confirm: { update_password_confirm: {
@ -222,24 +178,24 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
break update_password_confirm; break update_password_confirm;
} }
assert(params.action === "update");
state = reducer(state, { state = reducer(state, {
"action": "update", "action": "update",
"name": "password-confirm", "name": "password-confirm",
"value": params.value "valueOrValues": params.valueOrValues
}); });
} }
return; return;
case "focus lost": case "focus lost":
assert(typeof formFieldState.hasLostFocusAtLeastOnce === "boolean"); if (formFieldState.hasLostFocusAtLeastOnce instanceof Array) {
const { fieldIndex } = params;
assert(fieldIndex !== undefined);
formFieldState.hasLostFocusAtLeastOnce[fieldIndex] = true;
return;
}
formFieldState.hasLostFocusAtLeastOnce = true; formFieldState.hasLostFocusAtLeastOnce = true;
return; return;
case "multi-valued text input focus lost":
assert(formFieldState.hasLostFocusAtLeastOnce instanceof Array);
formFieldState.hasLostFocusAtLeastOnce[params.fieldIndex] = true;
return;
} }
assert<Equals<typeof params, never>>(false); assert<Equals<typeof params, never>>(false);
})(); })();
@ -247,8 +203,8 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
return state; return state;
}, },
useMemo(function getInitialState(): internal.State { useMemo(function getInitialState(): internal.State {
const initialFormFieldValues = (() => { const initialFormFieldState = (() => {
const initialFormFieldValues: ({ attribute: Attribute } & ({ value: string } | { values: string[] }))[] = []; const out: { attribute: Attribute; valueOrValues: string | string[] }[] = [];
for (const attribute of attributesWithPassword) { for (const attribute of attributesWithPassword) {
handle_multi_valued_attribute: { handle_multi_valued_attribute: {
@ -286,32 +242,32 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
} }
} }
initialFormFieldValues.push({ out.push({
attribute, attribute,
values "valueOrValues": values
}); });
continue; continue;
} }
initialFormFieldValues.push({ out.push({
"value": attribute.value ?? "", attribute,
attribute "valueOrValues": attribute.value ?? ""
}); });
} }
return initialFormFieldValues; return out;
})(); })();
const initialState: internal.State = { const initialState: internal.State = {
"formFieldStates": initialFormFieldValues.map(({ attribute, ...valueOrValuesWrap }) => ({ "formFieldStates": initialFormFieldState.map(({ attribute, valueOrValues }) => ({
attribute, attribute,
"errors": getErrors({ "errors": getErrors({
"attributeName": attribute.name, "attributeName": attribute.name,
"fieldValues": initialFormFieldValues "formFieldStates": initialFormFieldState
}), }),
"hasLostFocusAtLeastOnce": "values" in valueOrValuesWrap ? valueOrValuesWrap.values.map(() => false) : false, "hasLostFocusAtLeastOnce": valueOrValues instanceof Array ? valueOrValues.map(() => false) : false,
...valueOrValuesWrap "valueOrValues": valueOrValues
})) }))
}; };
@ -401,17 +357,14 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
const { msg, msgStr, advancedMsg, advancedMsgStr } = i18n; const { msg, msgStr, advancedMsg, advancedMsgStr } = i18n;
const getErrors = useConstCallback( const getErrors = useConstCallback(
(params: { (params: { attributeName: string; formFieldStates: { attribute: Attribute; valueOrValues: string | string[] }[] }): FormFieldError[] => {
attributeName: string; const { attributeName, formFieldStates } = params;
fieldValues: ({ attribute: Attribute } & ({ value: string } | { values: string[] }))[];
}): FormFieldError[] => {
const { attributeName, fieldValues } = params;
const fieldValue = fieldValues.find(({ attribute }) => attribute.name === attributeName); const formFieldState = formFieldStates.find(({ attribute }) => attribute.name === attributeName);
assert(fieldValue !== undefined); assert(formFieldState !== undefined);
const { attribute } = fieldValue; const { attribute } = formFieldState;
assert(attribute !== undefined); assert(attribute !== undefined);
@ -419,9 +372,9 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
if (attribute.multivalued) { if (attribute.multivalued) {
const defaultValues = attribute.values ?? [""]; const defaultValues = attribute.values ?? [""];
assert("values" in fieldValue); assert(formFieldState.valueOrValues instanceof Array);
const { values } = fieldValue; const values = formFieldState.valueOrValues;
if (JSON.stringify(defaultValues) !== JSON.stringify(values.slice(0, defaultValues.length))) { if (JSON.stringify(defaultValues) !== JSON.stringify(values.slice(0, defaultValues.length))) {
break server_side_error; break server_side_error;
@ -429,9 +382,9 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
} else { } else {
const defaultValue = attribute.value ?? ""; const defaultValue = attribute.value ?? "";
assert("value" in fieldValue); assert(typeof formFieldState.valueOrValues === "string");
const { value } = fieldValue; const value = formFieldState.valueOrValues;
if (defaultValue !== value) { if (defaultValue !== value) {
break server_side_error; break server_side_error;
@ -477,16 +430,16 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
break handle_multi_valued_multi_fields; break handle_multi_valued_multi_fields;
} }
assert("values" in fieldValue); assert(formFieldState.valueOrValues instanceof Array);
const { values } = fieldValue; const values = formFieldState.valueOrValues;
const errors = values const errors = values
.map((value, index) => { .map((value, index) => {
const specificValueErrors = getErrors({ const specificValueErrors = getErrors({
attributeName, attributeName,
"fieldValues": fieldValues.map(fieldValue => { "formFieldStates": formFieldStates.map(formFieldState => {
if (fieldValue.attribute.name === attributeName) { if (formFieldState.attribute.name === attributeName) {
return { return {
"attribute": { "attribute": {
...attribute, ...attribute,
@ -495,11 +448,11 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
"inputType": undefined "inputType": undefined
} }
}, },
value "valueOrValues": value
}; };
} }
return fieldValue; return formFieldState;
}) })
}); });
@ -569,9 +522,9 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
assert(!isNaN(max)); assert(!isNaN(max));
assert("values" in fieldValue); assert(formFieldState.valueOrValues instanceof Array);
const { values } = fieldValue; const values = formFieldState.valueOrValues;
if (min <= values.length && values.length <= max) { if (min <= values.length && values.length <= max) {
return []; return [];
@ -592,9 +545,9 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
]; ];
} }
assert("value" in fieldValue); assert(typeof formFieldState.valueOrValues === "string");
const { value } = fieldValue; const value = formFieldState.valueOrValues;
const errors: FormFieldError[] = []; const errors: FormFieldError[] = [];
@ -770,16 +723,20 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
break check_password_policy_x; break check_password_policy_x;
} }
const usernameFieldValue = fieldValues.find(fieldValue => fieldValue.attribute.name === "username"); const usernameFormFieldState = formFieldStates.find(formFieldState => formFieldState.attribute.name === "username");
if (usernameFieldValue === undefined) { if (usernameFormFieldState === undefined) {
break check_password_policy_x; break check_password_policy_x;
} }
assert("value" in usernameFieldValue); assert(typeof usernameFormFieldState.valueOrValues === "string");
if (value !== usernameFieldValue.value) { {
break check_password_policy_x; const usernameValue = usernameFormFieldState.valueOrValues;
if (value !== usernameValue) {
break check_password_policy_x;
}
} }
const msgArgs = ["invalidPasswordNotUsernameMessage"] as const; const msgArgs = ["invalidPasswordNotUsernameMessage"] as const;
@ -804,16 +761,20 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
break check_password_policy_x; break check_password_policy_x;
} }
const emailFieldValue = fieldValues.find(fieldValue => fieldValue.attribute.name === "email"); const emailFormFieldState = formFieldStates.find(formFieldState => formFieldState.attribute.name === "email");
if (emailFieldValue === undefined) { if (emailFormFieldState === undefined) {
break check_password_policy_x; break check_password_policy_x;
} }
assert("value" in emailFieldValue); assert(typeof emailFormFieldState.valueOrValues === "string");
if (value !== emailFieldValue.value) { {
break check_password_policy_x; const emailValue = emailFormFieldState.valueOrValues;
if (value !== emailValue) {
break check_password_policy_x;
}
} }
const msgArgs = ["invalidPasswordNotEmailMessage"] as const; const msgArgs = ["invalidPasswordNotEmailMessage"] as const;
@ -835,14 +796,18 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
break password_confirm_matches_password; break password_confirm_matches_password;
} }
const passwordFieldValue = fieldValues.find(fieldValue => fieldValue.attribute.name === "password"); const passwordFormFieldState = formFieldStates.find(formFieldState => formFieldState.attribute.name === "password");
assert(passwordFieldValue !== undefined); assert(passwordFormFieldState !== undefined);
assert("value" in passwordFieldValue); assert(typeof passwordFormFieldState.valueOrValues === "string");
if (passwordFieldValue.value === value) { {
break password_confirm_matches_password; const passwordValue = passwordFormFieldState.valueOrValues;
if (value === passwordValue) {
break password_confirm_matches_password;
}
} }
const msgArgs = ["invalidPasswordConfirmMessage"] as const; const msgArgs = ["invalidPasswordConfirmMessage"] as const;