required can have a value that is a boolean when used with controls (#34251)

* required is a boolean when used with controls

fixes: #33614
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* simplified rules declaration

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* added missing messages

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* use value when it's present

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

---------

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
This commit is contained in:
Erik Jan de Wit 2024-10-25 15:24:09 +02:00 committed by GitHub
parent 624817bdc1
commit 2f64f43266
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 43 additions and 127 deletions

View file

@ -84,7 +84,7 @@ export default class ProviderBaseGeneralSettingsPage extends PageObject {
#jwksUrl = "config.jwksUrl";
#pkceSwitch = "#config\\.pkceEnabled";
#pkceMethod = "#pkceMethod";
#clientAuth = "#clientAuthentication";
#clientAuth = "#clientAuthMethod";
#clientAssertionSigningAlg = "#clientAssertionSigningAlg";
#clientAssertionAudienceInput = "#clientAssertionAudience";
#jwtX509HeadersSwitch = "#jwtX509HeadersEnabled";

View file

@ -84,7 +84,7 @@ export const EditFlow = ({ execution, onRowChange }: EditFlowProps) => {
name="displayName"
label={t("name")}
labelIcon={t("flowNameHelp")}
rules={{ required: { value: true, message: t("required") } }}
rules={{ required: t("required") }}
/>
<TextAreaControl
name="description"

View file

@ -161,7 +161,7 @@ export const ExecutionConfigModal = ({
name="alias"
label={t("alias")}
labelIcon={t("authenticationAliasHelp")}
rules={{ required: { value: true, message: t("required") } }}
rules={{ required: t("required") }}
isDisabled={!!config}
/>
<DynamicComponents

View file

@ -90,7 +90,7 @@ export const AddSubFlowModal = ({
name="name"
label={t("name")}
labelIcon={t("clientIdHelp")}
rules={{ required: { value: true, message: t("required") } }}
rules={{ required: t("required") }}
/>
<TextControl
name="description"

View file

@ -10,7 +10,7 @@ export const NameDescription = () => {
name="alias"
label={t("name")}
labelIcon={t("flowNameHelp")}
rules={{ required: { value: true, message: t("required") } }}
rules={{ required: t("required") }}
/>
<TextControl
name="description"

View file

@ -100,10 +100,7 @@ export const CibaPolicy = ({ realm, realmUpdated }: CibaPolicyProps) => {
value: CIBA_EXPIRES_IN_MAX,
message: t("lessThan", { value: CIBA_EXPIRES_IN_MAX }),
},
required: {
value: true,
message: t("required"),
},
required: t("required"),
}}
/>
<TextControl
@ -124,10 +121,7 @@ export const CibaPolicy = ({ realm, realmUpdated }: CibaPolicyProps) => {
value: CIBA_INTERVAL_MAX,
message: t("lessThan", { value: CIBA_INTERVAL_MAX }),
},
required: {
value: true,
message: t("required"),
},
required: t("required"),
}}
/>
<SelectControl

View file

@ -161,7 +161,7 @@ export const WebauthnPolicy = ({
name={`${namePrefix}RpEntityName`}
label={t("webAuthnPolicyRpEntityName")}
labelIcon={t("webAuthnPolicyRpEntityNameHelp")}
rules={{ required: { value: true, message: t("required") } }}
rules={{ required: t("required") }}
/>
<WebauthnSelect
name={`${namePrefix}SignatureAlgorithms`}

View file

@ -217,7 +217,7 @@ export default function MappingDetails() {
label={t("name")}
labelIcon={t("mapperNameHelp")}
readOnlyVariant={isUpdating ? "default" : undefined}
rules={{ required: { value: true, message: t("required") } }}
rules={{ required: t("required") }}
/>
<DynamicComponents
properties={mapping?.properties || []}

View file

@ -79,10 +79,7 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
label={t("name")}
labelIcon={t("scopeNameHelp")}
rules={{
required: {
value: true,
message: t("required"),
},
required: t("required"),
onChange: (e) => {
if (isDynamicScopesEnabled)
setDynamicRegex(e.target.validated, true);

View file

@ -19,7 +19,7 @@ export const ClientDescription = ({
name="clientId"
label={t("clientId")}
labelIcon={t("clientIdHelp")}
rules={{ required: { value: true, message: t("required") } }}
rules={{ required: t("required") }}
/>
<TextControl
name="name"

View file

@ -1,11 +1,5 @@
import {
HelpItem,
KeycloakSelect,
SelectVariant,
} from "@keycloak/keycloak-ui-shared";
import { FormGroup, SelectOption } from "@patternfly/react-core";
import { useState } from "react";
import { Controller, useFormContext, useWatch } from "react-hook-form";
import { SelectControl } from "@keycloak/keycloak-ui-shared";
import { useFormContext, useWatch } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
import { sortProviders } from "../../util";
@ -25,8 +19,6 @@ export const OIDCAuthentication = ({ create = true }: { create?: boolean }) => {
const { t } = useTranslation();
const { control } = useFormContext();
const [openClientAuth, setOpenClientAuth] = useState(false);
const [openClientAuthSigAlg, setOpenClientAuthSigAlg] = useState(false);
const clientAuthMethod = useWatch({
control: control,
@ -35,96 +27,34 @@ export const OIDCAuthentication = ({ create = true }: { create?: boolean }) => {
return (
<>
<FormGroup
label={t("clientAuthentication")}
labelIcon={
<HelpItem
helpText={t("clientAuthenticationHelp")}
fieldLabelId="clientAuthentication"
/>
}
fieldId="clientAuthentication"
>
<Controller
<SelectControl
name="config.clientAuthMethod"
defaultValue={clientAuthentications[0]}
control={control}
render={({ field }) => (
<KeycloakSelect
toggleId="clientAuthentication"
onToggle={() => setOpenClientAuth(!openClientAuth)}
onSelect={(value) => {
field.onChange(value as string);
setOpenClientAuth(false);
label={t("clientAuthentication")}
labelIcon={t("clientAuthenticationHelp")}
options={clientAuthentications.map((auth) => ({
key: auth,
value: t(`clientAuthentications.${auth}`),
}))}
controller={{
defaultValue: clientAuthentications[0],
}}
selections={field.value}
variant={SelectVariant.single}
aria-label={t("clientAuthentication")}
isOpen={openClientAuth}
>
{clientAuthentications.map((option) => (
<SelectOption
selected={option === field.value}
key={option}
value={option}
>
{t(`clientAuthentications.${option}`)}
</SelectOption>
))}
</KeycloakSelect>
)}
/>
</FormGroup>
<ClientIdSecret
secretRequired={clientAuthMethod !== "private_key_jwt"}
create={create}
/>
<FormGroup
label={t("clientAssertionSigningAlg")}
labelIcon={
<HelpItem
helpText={t("clientAssertionSigningAlgHelp")}
fieldLabelId="clientAssertionSigningAlg"
/>
}
fieldId="clientAssertionSigningAlg"
>
<Controller
<SelectControl
name="config.clientAssertionSigningAlg"
defaultValue=""
control={control}
render={({ field }) => (
<KeycloakSelect
maxHeight={200}
toggleId="clientAssertionSigningAlg"
onToggle={() => setOpenClientAuthSigAlg(!openClientAuthSigAlg)}
onSelect={(value) => {
field.onChange(value.toString());
setOpenClientAuthSigAlg(false);
}}
selections={field.value || t("algorithmNotSpecified")}
variant={SelectVariant.single}
aria-label={t("selectClientAssertionSigningAlg")}
isOpen={openClientAuthSigAlg}
>
{[
<SelectOption selected={field.value === ""} key="" value="">
{t("algorithmNotSpecified")}
</SelectOption>,
...sortProviders(providers).map((option) => (
<SelectOption
selected={option === field.value}
key={option}
value={option}
>
{option}
</SelectOption>
)),
label={t("clientAssertionSigningAlg")}
labelIcon={t("clientAssertionSigningAlgHelp")}
options={[
{ key: "", value: t("algorithmNotSpecified") },
...sortProviders(providers).map((p) => ({ key: p, value: p })),
]}
</KeycloakSelect>
)}
controller={{
defaultValue: "",
}}
/>
</FormGroup>
{(clientAuthMethod === "private_key_jwt" ||
clientAuthMethod === "client_secret_jwt") && (
<TextField

View file

@ -476,7 +476,7 @@ export default function NewClientPolicy() {
name="name"
label={t("name")}
rules={{
required: { value: true, message: t("required") },
required: t("required"),
validate: (value) =>
policies.some((policy) => policy.name === value)
? t("createClientProfileNameHelperText").toString()

View file

@ -467,10 +467,7 @@ export default function AttributesGroupForm() {
labelIcon={t("nameHintHelp")}
isDisabled={!!matchingGroup || editMode}
rules={{
required: {
value: true,
message: t("required"),
},
required: t("required"),
onChange: (event) => {
handleAttributesGroupNameChange(event, event.target.value);
},

View file

@ -332,10 +332,7 @@ export const AddTranslationsDialog = ({
label={t("translationValue")}
data-testid={`translation-value-${rowIndex}`}
rules={{
required: {
value: true,
message: t("required"),
},
required: t("required"),
}}
/>
)}

View file

@ -41,7 +41,7 @@ export const LdapSettingsSearching = ({
controller={{
defaultValue: "",
rules: {
required: { value: true, message: t("validateEditMode") },
required: t("validateEditMode"),
},
}}
options={["", "READ_ONLY", "WRITABLE", "UNSYNCED"]}

View file

@ -11,6 +11,7 @@ import {
UseControllerProps,
useController,
} from "react-hook-form";
import { getRuleValue } from "../utils/getRuleValue";
import { FormLabel } from "./FormLabel";
import { PasswordInput, PasswordInputProps } from "./PasswordInput";
@ -32,7 +33,7 @@ export const PasswordControl = <
props: PasswordControlProps<T, P>,
) => {
const { labelIcon, ...rest } = props;
const required = !!props.rules?.required;
const required = !!getRuleValue(props.rules?.required);
const defaultValue = props.defaultValue ?? ("" as PathValue<T, P>);
const { field, fieldState } = useController({

View file

@ -14,7 +14,7 @@ import {
UseControllerProps,
useController,
} from "react-hook-form";
import { getRuleValue } from "../utils/getRuleValue";
import { FormLabel } from "./FormLabel";
export type TextControlProps<
@ -36,7 +36,7 @@ export const TextControl = <
props: TextControlProps<T, P>,
) => {
const { labelIcon, helperText, ...rest } = props;
const required = !!props.rules?.required;
const required = !!getRuleValue(props.rules?.required);
const defaultValue = props.defaultValue ?? ("" as PathValue<T, P>);
const { field, fieldState } = useController({