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:
parent
624817bdc1
commit
2f64f43266
17 changed files with 43 additions and 127 deletions
|
@ -84,7 +84,7 @@ export default class ProviderBaseGeneralSettingsPage extends PageObject {
|
||||||
#jwksUrl = "config.jwksUrl";
|
#jwksUrl = "config.jwksUrl";
|
||||||
#pkceSwitch = "#config\\.pkceEnabled";
|
#pkceSwitch = "#config\\.pkceEnabled";
|
||||||
#pkceMethod = "#pkceMethod";
|
#pkceMethod = "#pkceMethod";
|
||||||
#clientAuth = "#clientAuthentication";
|
#clientAuth = "#clientAuthMethod";
|
||||||
#clientAssertionSigningAlg = "#clientAssertionSigningAlg";
|
#clientAssertionSigningAlg = "#clientAssertionSigningAlg";
|
||||||
#clientAssertionAudienceInput = "#clientAssertionAudience";
|
#clientAssertionAudienceInput = "#clientAssertionAudience";
|
||||||
#jwtX509HeadersSwitch = "#jwtX509HeadersEnabled";
|
#jwtX509HeadersSwitch = "#jwtX509HeadersEnabled";
|
||||||
|
|
|
@ -84,7 +84,7 @@ export const EditFlow = ({ execution, onRowChange }: EditFlowProps) => {
|
||||||
name="displayName"
|
name="displayName"
|
||||||
label={t("name")}
|
label={t("name")}
|
||||||
labelIcon={t("flowNameHelp")}
|
labelIcon={t("flowNameHelp")}
|
||||||
rules={{ required: { value: true, message: t("required") } }}
|
rules={{ required: t("required") }}
|
||||||
/>
|
/>
|
||||||
<TextAreaControl
|
<TextAreaControl
|
||||||
name="description"
|
name="description"
|
||||||
|
|
|
@ -161,7 +161,7 @@ export const ExecutionConfigModal = ({
|
||||||
name="alias"
|
name="alias"
|
||||||
label={t("alias")}
|
label={t("alias")}
|
||||||
labelIcon={t("authenticationAliasHelp")}
|
labelIcon={t("authenticationAliasHelp")}
|
||||||
rules={{ required: { value: true, message: t("required") } }}
|
rules={{ required: t("required") }}
|
||||||
isDisabled={!!config}
|
isDisabled={!!config}
|
||||||
/>
|
/>
|
||||||
<DynamicComponents
|
<DynamicComponents
|
||||||
|
|
|
@ -90,7 +90,7 @@ export const AddSubFlowModal = ({
|
||||||
name="name"
|
name="name"
|
||||||
label={t("name")}
|
label={t("name")}
|
||||||
labelIcon={t("clientIdHelp")}
|
labelIcon={t("clientIdHelp")}
|
||||||
rules={{ required: { value: true, message: t("required") } }}
|
rules={{ required: t("required") }}
|
||||||
/>
|
/>
|
||||||
<TextControl
|
<TextControl
|
||||||
name="description"
|
name="description"
|
||||||
|
|
|
@ -10,7 +10,7 @@ export const NameDescription = () => {
|
||||||
name="alias"
|
name="alias"
|
||||||
label={t("name")}
|
label={t("name")}
|
||||||
labelIcon={t("flowNameHelp")}
|
labelIcon={t("flowNameHelp")}
|
||||||
rules={{ required: { value: true, message: t("required") } }}
|
rules={{ required: t("required") }}
|
||||||
/>
|
/>
|
||||||
<TextControl
|
<TextControl
|
||||||
name="description"
|
name="description"
|
||||||
|
|
|
@ -100,10 +100,7 @@ export const CibaPolicy = ({ realm, realmUpdated }: CibaPolicyProps) => {
|
||||||
value: CIBA_EXPIRES_IN_MAX,
|
value: CIBA_EXPIRES_IN_MAX,
|
||||||
message: t("lessThan", { value: CIBA_EXPIRES_IN_MAX }),
|
message: t("lessThan", { value: CIBA_EXPIRES_IN_MAX }),
|
||||||
},
|
},
|
||||||
required: {
|
required: t("required"),
|
||||||
value: true,
|
|
||||||
message: t("required"),
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<TextControl
|
<TextControl
|
||||||
|
@ -124,10 +121,7 @@ export const CibaPolicy = ({ realm, realmUpdated }: CibaPolicyProps) => {
|
||||||
value: CIBA_INTERVAL_MAX,
|
value: CIBA_INTERVAL_MAX,
|
||||||
message: t("lessThan", { value: CIBA_INTERVAL_MAX }),
|
message: t("lessThan", { value: CIBA_INTERVAL_MAX }),
|
||||||
},
|
},
|
||||||
required: {
|
required: t("required"),
|
||||||
value: true,
|
|
||||||
message: t("required"),
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<SelectControl
|
<SelectControl
|
||||||
|
|
|
@ -161,7 +161,7 @@ export const WebauthnPolicy = ({
|
||||||
name={`${namePrefix}RpEntityName`}
|
name={`${namePrefix}RpEntityName`}
|
||||||
label={t("webAuthnPolicyRpEntityName")}
|
label={t("webAuthnPolicyRpEntityName")}
|
||||||
labelIcon={t("webAuthnPolicyRpEntityNameHelp")}
|
labelIcon={t("webAuthnPolicyRpEntityNameHelp")}
|
||||||
rules={{ required: { value: true, message: t("required") } }}
|
rules={{ required: t("required") }}
|
||||||
/>
|
/>
|
||||||
<WebauthnSelect
|
<WebauthnSelect
|
||||||
name={`${namePrefix}SignatureAlgorithms`}
|
name={`${namePrefix}SignatureAlgorithms`}
|
||||||
|
|
|
@ -217,7 +217,7 @@ export default function MappingDetails() {
|
||||||
label={t("name")}
|
label={t("name")}
|
||||||
labelIcon={t("mapperNameHelp")}
|
labelIcon={t("mapperNameHelp")}
|
||||||
readOnlyVariant={isUpdating ? "default" : undefined}
|
readOnlyVariant={isUpdating ? "default" : undefined}
|
||||||
rules={{ required: { value: true, message: t("required") } }}
|
rules={{ required: t("required") }}
|
||||||
/>
|
/>
|
||||||
<DynamicComponents
|
<DynamicComponents
|
||||||
properties={mapping?.properties || []}
|
properties={mapping?.properties || []}
|
||||||
|
|
|
@ -79,10 +79,7 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
|
||||||
label={t("name")}
|
label={t("name")}
|
||||||
labelIcon={t("scopeNameHelp")}
|
labelIcon={t("scopeNameHelp")}
|
||||||
rules={{
|
rules={{
|
||||||
required: {
|
required: t("required"),
|
||||||
value: true,
|
|
||||||
message: t("required"),
|
|
||||||
},
|
|
||||||
onChange: (e) => {
|
onChange: (e) => {
|
||||||
if (isDynamicScopesEnabled)
|
if (isDynamicScopesEnabled)
|
||||||
setDynamicRegex(e.target.validated, true);
|
setDynamicRegex(e.target.validated, true);
|
||||||
|
|
|
@ -19,7 +19,7 @@ export const ClientDescription = ({
|
||||||
name="clientId"
|
name="clientId"
|
||||||
label={t("clientId")}
|
label={t("clientId")}
|
||||||
labelIcon={t("clientIdHelp")}
|
labelIcon={t("clientIdHelp")}
|
||||||
rules={{ required: { value: true, message: t("required") } }}
|
rules={{ required: t("required") }}
|
||||||
/>
|
/>
|
||||||
<TextControl
|
<TextControl
|
||||||
name="name"
|
name="name"
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
import {
|
import { SelectControl } from "@keycloak/keycloak-ui-shared";
|
||||||
HelpItem,
|
import { useFormContext, useWatch } from "react-hook-form";
|
||||||
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 { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
|
import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
|
||||||
import { sortProviders } from "../../util";
|
import { sortProviders } from "../../util";
|
||||||
|
@ -25,8 +19,6 @@ export const OIDCAuthentication = ({ create = true }: { create?: boolean }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { control } = useFormContext();
|
const { control } = useFormContext();
|
||||||
const [openClientAuth, setOpenClientAuth] = useState(false);
|
|
||||||
const [openClientAuthSigAlg, setOpenClientAuthSigAlg] = useState(false);
|
|
||||||
|
|
||||||
const clientAuthMethod = useWatch({
|
const clientAuthMethod = useWatch({
|
||||||
control: control,
|
control: control,
|
||||||
|
@ -35,96 +27,34 @@ export const OIDCAuthentication = ({ create = true }: { create?: boolean }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FormGroup
|
<SelectControl
|
||||||
|
name="config.clientAuthMethod"
|
||||||
label={t("clientAuthentication")}
|
label={t("clientAuthentication")}
|
||||||
labelIcon={
|
labelIcon={t("clientAuthenticationHelp")}
|
||||||
<HelpItem
|
options={clientAuthentications.map((auth) => ({
|
||||||
helpText={t("clientAuthenticationHelp")}
|
key: auth,
|
||||||
fieldLabelId="clientAuthentication"
|
value: t(`clientAuthentications.${auth}`),
|
||||||
/>
|
}))}
|
||||||
}
|
controller={{
|
||||||
fieldId="clientAuthentication"
|
defaultValue: clientAuthentications[0],
|
||||||
>
|
}}
|
||||||
<Controller
|
/>
|
||||||
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);
|
|
||||||
}}
|
|
||||||
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
|
<ClientIdSecret
|
||||||
secretRequired={clientAuthMethod !== "private_key_jwt"}
|
secretRequired={clientAuthMethod !== "private_key_jwt"}
|
||||||
create={create}
|
create={create}
|
||||||
/>
|
/>
|
||||||
<FormGroup
|
<SelectControl
|
||||||
|
name="config.clientAssertionSigningAlg"
|
||||||
label={t("clientAssertionSigningAlg")}
|
label={t("clientAssertionSigningAlg")}
|
||||||
labelIcon={
|
labelIcon={t("clientAssertionSigningAlgHelp")}
|
||||||
<HelpItem
|
options={[
|
||||||
helpText={t("clientAssertionSigningAlgHelp")}
|
{ key: "", value: t("algorithmNotSpecified") },
|
||||||
fieldLabelId="clientAssertionSigningAlg"
|
...sortProviders(providers).map((p) => ({ key: p, value: p })),
|
||||||
/>
|
]}
|
||||||
}
|
controller={{
|
||||||
fieldId="clientAssertionSigningAlg"
|
defaultValue: "",
|
||||||
>
|
}}
|
||||||
<Controller
|
/>
|
||||||
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>
|
|
||||||
)),
|
|
||||||
]}
|
|
||||||
</KeycloakSelect>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
{(clientAuthMethod === "private_key_jwt" ||
|
{(clientAuthMethod === "private_key_jwt" ||
|
||||||
clientAuthMethod === "client_secret_jwt") && (
|
clientAuthMethod === "client_secret_jwt") && (
|
||||||
<TextField
|
<TextField
|
||||||
|
|
|
@ -476,7 +476,7 @@ export default function NewClientPolicy() {
|
||||||
name="name"
|
name="name"
|
||||||
label={t("name")}
|
label={t("name")}
|
||||||
rules={{
|
rules={{
|
||||||
required: { value: true, message: t("required") },
|
required: t("required"),
|
||||||
validate: (value) =>
|
validate: (value) =>
|
||||||
policies.some((policy) => policy.name === value)
|
policies.some((policy) => policy.name === value)
|
||||||
? t("createClientProfileNameHelperText").toString()
|
? t("createClientProfileNameHelperText").toString()
|
||||||
|
|
|
@ -467,10 +467,7 @@ export default function AttributesGroupForm() {
|
||||||
labelIcon={t("nameHintHelp")}
|
labelIcon={t("nameHintHelp")}
|
||||||
isDisabled={!!matchingGroup || editMode}
|
isDisabled={!!matchingGroup || editMode}
|
||||||
rules={{
|
rules={{
|
||||||
required: {
|
required: t("required"),
|
||||||
value: true,
|
|
||||||
message: t("required"),
|
|
||||||
},
|
|
||||||
onChange: (event) => {
|
onChange: (event) => {
|
||||||
handleAttributesGroupNameChange(event, event.target.value);
|
handleAttributesGroupNameChange(event, event.target.value);
|
||||||
},
|
},
|
||||||
|
|
|
@ -332,10 +332,7 @@ export const AddTranslationsDialog = ({
|
||||||
label={t("translationValue")}
|
label={t("translationValue")}
|
||||||
data-testid={`translation-value-${rowIndex}`}
|
data-testid={`translation-value-${rowIndex}`}
|
||||||
rules={{
|
rules={{
|
||||||
required: {
|
required: t("required"),
|
||||||
value: true,
|
|
||||||
message: t("required"),
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -41,7 +41,7 @@ export const LdapSettingsSearching = ({
|
||||||
controller={{
|
controller={{
|
||||||
defaultValue: "",
|
defaultValue: "",
|
||||||
rules: {
|
rules: {
|
||||||
required: { value: true, message: t("validateEditMode") },
|
required: t("validateEditMode"),
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
options={["", "READ_ONLY", "WRITABLE", "UNSYNCED"]}
|
options={["", "READ_ONLY", "WRITABLE", "UNSYNCED"]}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
UseControllerProps,
|
UseControllerProps,
|
||||||
useController,
|
useController,
|
||||||
} from "react-hook-form";
|
} from "react-hook-form";
|
||||||
|
import { getRuleValue } from "../utils/getRuleValue";
|
||||||
import { FormLabel } from "./FormLabel";
|
import { FormLabel } from "./FormLabel";
|
||||||
import { PasswordInput, PasswordInputProps } from "./PasswordInput";
|
import { PasswordInput, PasswordInputProps } from "./PasswordInput";
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ export const PasswordControl = <
|
||||||
props: PasswordControlProps<T, P>,
|
props: PasswordControlProps<T, P>,
|
||||||
) => {
|
) => {
|
||||||
const { labelIcon, ...rest } = props;
|
const { labelIcon, ...rest } = props;
|
||||||
const required = !!props.rules?.required;
|
const required = !!getRuleValue(props.rules?.required);
|
||||||
const defaultValue = props.defaultValue ?? ("" as PathValue<T, P>);
|
const defaultValue = props.defaultValue ?? ("" as PathValue<T, P>);
|
||||||
|
|
||||||
const { field, fieldState } = useController({
|
const { field, fieldState } = useController({
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {
|
||||||
UseControllerProps,
|
UseControllerProps,
|
||||||
useController,
|
useController,
|
||||||
} from "react-hook-form";
|
} from "react-hook-form";
|
||||||
|
import { getRuleValue } from "../utils/getRuleValue";
|
||||||
import { FormLabel } from "./FormLabel";
|
import { FormLabel } from "./FormLabel";
|
||||||
|
|
||||||
export type TextControlProps<
|
export type TextControlProps<
|
||||||
|
@ -36,7 +36,7 @@ export const TextControl = <
|
||||||
props: TextControlProps<T, P>,
|
props: TextControlProps<T, P>,
|
||||||
) => {
|
) => {
|
||||||
const { labelIcon, helperText, ...rest } = props;
|
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 defaultValue = props.defaultValue ?? ("" as PathValue<T, P>);
|
||||||
|
|
||||||
const { field, fieldState } = useController({
|
const { field, fieldState } = useController({
|
||||||
|
|
Loading…
Reference in a new issue