Use react-hook-form
v7 for client scope form (#3885)
This commit is contained in:
parent
49ed77b85c
commit
c2924ceb9c
5 changed files with 80 additions and 96 deletions
|
@ -272,12 +272,7 @@ describe("Client Scopes test", () => {
|
||||||
sidebarPage.waitForPageLoad();
|
sidebarPage.waitForPageLoad();
|
||||||
listingPage.goToCreateItem();
|
listingPage.goToCreateItem();
|
||||||
|
|
||||||
createClientScopePage.save().checkClientNameRequiredMessage();
|
createClientScopePage.fillClientScopeData("address").save();
|
||||||
|
|
||||||
createClientScopePage
|
|
||||||
.fillClientScopeData("address")
|
|
||||||
.save()
|
|
||||||
.checkClientNameRequiredMessage(false);
|
|
||||||
|
|
||||||
// The error should inform about duplicated name/id
|
// The error should inform about duplicated name/id
|
||||||
masthead.checkNotificationMessage(
|
masthead.checkNotificationMessage(
|
||||||
|
|
|
@ -26,7 +26,7 @@ export default class CreateClientScopePage extends CommonPage {
|
||||||
this.clientScopeDescriptionInput = "#kc-description";
|
this.clientScopeDescriptionInput = "#kc-description";
|
||||||
this.clientScopeTypeDrpDwn = "#kc-protocol";
|
this.clientScopeTypeDrpDwn = "#kc-protocol";
|
||||||
this.clientScopeTypeList = "#kc-protocol + ul";
|
this.clientScopeTypeList = "#kc-protocol + ul";
|
||||||
this.displayOnConsentInput = '[id="kc-display.on.consent.screen-switch"]';
|
this.displayOnConsentInput = '[id="kc-display-on-consent-screen"]';
|
||||||
this.displayOnConsentSwitch =
|
this.displayOnConsentSwitch =
|
||||||
this.displayOnConsentInput + " + .pf-c-switch__toggle";
|
this.displayOnConsentInput + " + .pf-c-switch__toggle";
|
||||||
this.consentScreenTextInput = "#kc-consent-screen-text";
|
this.consentScreenTextInput = "#kc-consent-screen-text";
|
||||||
|
@ -73,12 +73,6 @@ export default class CreateClientScopePage extends CommonPage {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkClientNameRequiredMessage(exist = true) {
|
|
||||||
cy.get(this.clientScopeNameError).should((!exist ? "not." : "") + "exist");
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
getSwitchDisplayOnConsentScreenInput() {
|
getSwitchDisplayOnConsentScreenInput() {
|
||||||
return cy.get(this.displayOnConsentInput);
|
return cy.get(this.displayOnConsentInput);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,36 @@
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { useParams } from "react-router-dom";
|
|
||||||
import { Link } from "react-router-dom-v5-compat";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { Controller, useForm, useWatch } from "react-hook-form";
|
|
||||||
import {
|
|
||||||
FormGroup,
|
|
||||||
ValidatedOptions,
|
|
||||||
Select,
|
|
||||||
SelectVariant,
|
|
||||||
SelectOption,
|
|
||||||
Switch,
|
|
||||||
ActionGroup,
|
|
||||||
Button,
|
|
||||||
} from "@patternfly/react-core";
|
|
||||||
|
|
||||||
import type ClientScopeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientScopeRepresentation";
|
import type ClientScopeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientScopeRepresentation";
|
||||||
import {
|
import {
|
||||||
clientScopeTypesSelectOptions,
|
ActionGroup,
|
||||||
|
Button,
|
||||||
|
FormGroup,
|
||||||
|
Select,
|
||||||
|
SelectOption,
|
||||||
|
SelectVariant,
|
||||||
|
Switch,
|
||||||
|
ValidatedOptions,
|
||||||
|
} from "@patternfly/react-core";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Controller, useForm, useWatch } from "react-hook-form-v7";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Link } from "react-router-dom-v5-compat";
|
||||||
|
|
||||||
|
import { getProtocolName } from "../../clients/utils";
|
||||||
|
import {
|
||||||
allClientScopeTypes,
|
allClientScopeTypes,
|
||||||
ClientScopeDefaultOptionalType,
|
ClientScopeDefaultOptionalType,
|
||||||
|
clientScopeTypesSelectOptions,
|
||||||
} from "../../components/client-scope/ClientScopeTypes";
|
} from "../../components/client-scope/ClientScopeTypes";
|
||||||
|
import { FormAccess } from "../../components/form-access/FormAccess";
|
||||||
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
||||||
|
import { KeycloakTextArea } from "../../components/keycloak-text-area/KeycloakTextArea";
|
||||||
|
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";
|
||||||
|
import { useRealm } from "../../context/realm-context/RealmContext";
|
||||||
import { useLoginProviders } from "../../context/server-info/ServerInfoProvider";
|
import { useLoginProviders } from "../../context/server-info/ServerInfoProvider";
|
||||||
import { convertAttributeNameToForm, convertToFormValues } from "../../util";
|
import { convertAttributeNameToForm, convertToFormValues } from "../../util";
|
||||||
import { useRealm } from "../../context/realm-context/RealmContext";
|
|
||||||
import { getProtocolName } from "../../clients/utils";
|
|
||||||
import { toClientScopes } from "../routes/ClientScopes";
|
import { toClientScopes } from "../routes/ClientScopes";
|
||||||
import { FormAccess } from "../../components/form-access/FormAccess";
|
|
||||||
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";
|
|
||||||
import { KeycloakTextArea } from "../../components/keycloak-text-area/KeycloakTextArea";
|
|
||||||
|
|
||||||
type ScopeFormProps = {
|
type ScopeFormProps = {
|
||||||
clientScope: ClientScopeRepresentation;
|
clientScope?: ClientScopeRepresentation;
|
||||||
save: (clientScope: ClientScopeDefaultOptionalType) => void;
|
save: (clientScope: ClientScopeDefaultOptionalType) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -43,32 +42,30 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
setValue,
|
setValue,
|
||||||
formState: { errors },
|
formState: { errors, isDirty, isValid },
|
||||||
} = useForm<ClientScopeRepresentation>();
|
} = useForm<ClientScopeDefaultOptionalType>({ mode: "onChange" });
|
||||||
const { realm } = useRealm();
|
const { realm } = useRealm();
|
||||||
|
|
||||||
const providers = useLoginProviders();
|
const providers = useLoginProviders();
|
||||||
const [open, isOpen] = useState(false);
|
const [open, isOpen] = useState(false);
|
||||||
const [openType, setOpenType] = useState(false);
|
const [openType, setOpenType] = useState(false);
|
||||||
const { id } = useParams<{ id: string }>();
|
|
||||||
|
|
||||||
const displayOnConsentScreen = useWatch<string>({
|
const displayOnConsentScreen: string = useWatch({
|
||||||
control,
|
control,
|
||||||
name: convertAttributeNameToForm("attributes.display.on.consent.screen"),
|
name: convertAttributeNameToForm("attributes.display.on.consent.screen"),
|
||||||
defaultValue:
|
defaultValue:
|
||||||
clientScope.attributes?.["display.on.consent.screen"] ??
|
clientScope?.attributes?.["display.on.consent.screen"] ?? "true",
|
||||||
(id ? "false" : "true"),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
convertToFormValues(clientScope, setValue);
|
convertToFormValues(clientScope ?? {}, setValue);
|
||||||
}, [clientScope]);
|
}, [clientScope]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormAccess
|
<FormAccess
|
||||||
isHorizontal
|
|
||||||
onSubmit={handleSubmit(save)}
|
|
||||||
role="manage-clients"
|
role="manage-clients"
|
||||||
|
onSubmit={handleSubmit(save)}
|
||||||
|
isHorizontal
|
||||||
>
|
>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={t("common:name")}
|
label={t("common:name")}
|
||||||
|
@ -76,24 +73,19 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
|
||||||
<HelpItem helpText="client-scopes-help:name" fieldLabelId="name" />
|
<HelpItem helpText="client-scopes-help:name" fieldLabelId="name" />
|
||||||
}
|
}
|
||||||
fieldId="kc-name"
|
fieldId="kc-name"
|
||||||
isRequired
|
|
||||||
validated={
|
validated={
|
||||||
errors.name ? ValidatedOptions.error : ValidatedOptions.default
|
errors.name ? ValidatedOptions.error : ValidatedOptions.default
|
||||||
}
|
}
|
||||||
helperTextInvalid={t("common:required")}
|
helperTextInvalid={t("common:required")}
|
||||||
|
isRequired
|
||||||
>
|
>
|
||||||
<KeycloakTextInput
|
<KeycloakTextInput
|
||||||
ref={register({
|
|
||||||
required: true,
|
|
||||||
validate: (value: string) =>
|
|
||||||
!!value.trim() || t("common:required").toString(),
|
|
||||||
})}
|
|
||||||
type="text"
|
|
||||||
id="kc-name"
|
id="kc-name"
|
||||||
name="name"
|
|
||||||
validated={
|
validated={
|
||||||
errors.name ? ValidatedOptions.error : ValidatedOptions.default
|
errors.name ? ValidatedOptions.error : ValidatedOptions.default
|
||||||
}
|
}
|
||||||
|
isRequired
|
||||||
|
{...register("name", { required: true })}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
|
@ -111,17 +103,15 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
|
||||||
helperTextInvalid={t("common:maxLength", { length: 255 })}
|
helperTextInvalid={t("common:maxLength", { length: 255 })}
|
||||||
>
|
>
|
||||||
<KeycloakTextInput
|
<KeycloakTextInput
|
||||||
ref={register({
|
id="kc-description"
|
||||||
maxLength: 255,
|
|
||||||
})}
|
|
||||||
validated={
|
validated={
|
||||||
errors.description
|
errors.description
|
||||||
? ValidatedOptions.error
|
? ValidatedOptions.error
|
||||||
: ValidatedOptions.default
|
: ValidatedOptions.default
|
||||||
}
|
}
|
||||||
type="text"
|
{...register("description", {
|
||||||
id="kc-description"
|
maxLength: 255,
|
||||||
name="description"
|
})}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
|
@ -132,21 +122,21 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
|
||||||
fieldLabelId="client-scopes:type"
|
fieldLabelId="client-scopes:type"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
fieldId="type"
|
fieldId="kc-type"
|
||||||
>
|
>
|
||||||
<Controller
|
<Controller
|
||||||
name="type"
|
name="type"
|
||||||
defaultValue={allClientScopeTypes[0]}
|
defaultValue={allClientScopeTypes[0]}
|
||||||
control={control}
|
control={control}
|
||||||
render={({ onChange, value }) => (
|
render={({ field }) => (
|
||||||
<Select
|
<Select
|
||||||
id="type"
|
toggleId="kc-type"
|
||||||
variant={SelectVariant.single}
|
variant={SelectVariant.single}
|
||||||
isOpen={openType}
|
isOpen={openType}
|
||||||
selections={value}
|
selections={field.value}
|
||||||
onToggle={setOpenType}
|
onToggle={setOpenType}
|
||||||
onSelect={(_, value) => {
|
onSelect={(_, value) => {
|
||||||
onChange(value);
|
field.onChange(value);
|
||||||
setOpenType(false);
|
setOpenType(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -155,7 +145,7 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
{!id && (
|
{!clientScope && (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={t("protocol")}
|
label={t("protocol")}
|
||||||
labelIcon={
|
labelIcon={
|
||||||
|
@ -170,23 +160,21 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
|
||||||
name="protocol"
|
name="protocol"
|
||||||
defaultValue={providers[0]}
|
defaultValue={providers[0]}
|
||||||
control={control}
|
control={control}
|
||||||
render={({ onChange, value }) => (
|
render={({ field }) => (
|
||||||
<Select
|
<Select
|
||||||
toggleId="kc-protocol"
|
toggleId="kc-protocol"
|
||||||
required
|
|
||||||
onToggle={isOpen}
|
onToggle={isOpen}
|
||||||
onSelect={(_, value) => {
|
onSelect={(_, value) => {
|
||||||
onChange(value as string);
|
field.onChange(value);
|
||||||
isOpen(false);
|
isOpen(false);
|
||||||
}}
|
}}
|
||||||
selections={value}
|
selections={field.value}
|
||||||
variant={SelectVariant.single}
|
variant={SelectVariant.single}
|
||||||
aria-label={t("selectEncryptionType")}
|
|
||||||
isOpen={open}
|
isOpen={open}
|
||||||
>
|
>
|
||||||
{providers.map((option) => (
|
{providers.map((option) => (
|
||||||
<SelectOption
|
<SelectOption
|
||||||
selected={option === value}
|
selected={option === field.value}
|
||||||
key={option}
|
key={option}
|
||||||
value={option}
|
value={option}
|
||||||
data-testid={`option-${option}`}
|
data-testid={`option-${option}`}
|
||||||
|
@ -208,22 +196,21 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
|
||||||
fieldLabelId="client-scopes:displayOnConsentScreen"
|
fieldLabelId="client-scopes:displayOnConsentScreen"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
fieldId="kc-display.on.consent.screen"
|
fieldId="kc-display-on-consent-screen"
|
||||||
>
|
>
|
||||||
<Controller
|
<Controller
|
||||||
name={convertAttributeNameToForm(
|
name={convertAttributeNameToForm<ClientScopeDefaultOptionalType>(
|
||||||
"attributes.display.on.consent.screen"
|
"attributes.display.on.consent.screen"
|
||||||
)}
|
)}
|
||||||
control={control}
|
control={control}
|
||||||
defaultValue={displayOnConsentScreen}
|
defaultValue={displayOnConsentScreen}
|
||||||
render={({ onChange, value }) => (
|
render={({ field }) => (
|
||||||
<Switch
|
<Switch
|
||||||
id="kc-display.on.consent.screen-switch"
|
id="kc-display-on-consent-screen"
|
||||||
label={t("common:on")}
|
label={t("common:on")}
|
||||||
labelOff={t("common:off")}
|
labelOff={t("common:off")}
|
||||||
isChecked={value === "true"}
|
isChecked={field.value === "true"}
|
||||||
onChange={(value) => onChange("" + value)}
|
onChange={(value) => field.onChange(value.toString())}
|
||||||
aria-label={t("displayOnConsentScreen")}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
@ -240,10 +227,12 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
|
||||||
fieldId="kc-consent-screen-text"
|
fieldId="kc-consent-screen-text"
|
||||||
>
|
>
|
||||||
<KeycloakTextArea
|
<KeycloakTextArea
|
||||||
ref={register}
|
|
||||||
type="text"
|
|
||||||
id="kc-consent-screen-text"
|
id="kc-consent-screen-text"
|
||||||
name={convertAttributeNameToForm("attributes.consent.screen.text")}
|
{...register(
|
||||||
|
convertAttributeNameToForm<ClientScopeDefaultOptionalType>(
|
||||||
|
"attributes.consent.screen.text"
|
||||||
|
)
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
|
@ -256,20 +245,21 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
|
||||||
fieldLabelId="client-scopes:includeInTokenScope"
|
fieldLabelId="client-scopes:includeInTokenScope"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
fieldId="includeInTokenScope"
|
fieldId="kc-include-in-token-scope"
|
||||||
>
|
>
|
||||||
<Controller
|
<Controller
|
||||||
name={convertAttributeNameToForm("attributes.include.in.token.scope")}
|
name={convertAttributeNameToForm<ClientScopeDefaultOptionalType>(
|
||||||
|
"attributes.include.in.token.scope"
|
||||||
|
)}
|
||||||
control={control}
|
control={control}
|
||||||
defaultValue="true"
|
defaultValue="true"
|
||||||
render={({ onChange, value }) => (
|
render={({ field }) => (
|
||||||
<Switch
|
<Switch
|
||||||
id="includeInTokenScope-switch"
|
id="kc-include-in-token-scope"
|
||||||
label={t("common:on")}
|
label={t("common:on")}
|
||||||
labelOff={t("common:off")}
|
labelOff={t("common:off")}
|
||||||
isChecked={value === "true"}
|
isChecked={field.value === "true"}
|
||||||
onChange={(value) => onChange("" + value)}
|
onChange={(value) => field.onChange(value.toString())}
|
||||||
aria-label={t("includeInTokenScope")}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
@ -285,23 +275,28 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
|
||||||
fieldId="kc-gui-order"
|
fieldId="kc-gui-order"
|
||||||
>
|
>
|
||||||
<Controller
|
<Controller
|
||||||
name={convertAttributeNameToForm("attributes.gui.order")}
|
name={convertAttributeNameToForm<ClientScopeDefaultOptionalType>(
|
||||||
|
"attributes.gui.order"
|
||||||
|
)}
|
||||||
defaultValue=""
|
defaultValue=""
|
||||||
control={control}
|
control={control}
|
||||||
render={({ onChange, value }) => (
|
render={({ field }) => (
|
||||||
<KeycloakTextInput
|
<KeycloakTextInput
|
||||||
id="kc-gui-order"
|
id="kc-gui-order"
|
||||||
type="number"
|
type="number"
|
||||||
value={value}
|
value={field.value}
|
||||||
data-testid="displayOrder"
|
|
||||||
min={0}
|
min={0}
|
||||||
onChange={onChange}
|
onChange={field.onChange}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<ActionGroup>
|
<ActionGroup>
|
||||||
<Button variant="primary" type="submit">
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
type="submit"
|
||||||
|
isDisabled={!isDirty || !isValid}
|
||||||
|
>
|
||||||
{t("common:save")}
|
{t("common:save")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -242,7 +242,7 @@ export default function ClientScopeForm() {
|
||||||
<PageSection variant="light" className="pf-u-p-0">
|
<PageSection variant="light" className="pf-u-p-0">
|
||||||
{!id && (
|
{!id && (
|
||||||
<PageSection variant="light">
|
<PageSection variant="light">
|
||||||
<ScopeForm save={save} clientScope={{}} />
|
<ScopeForm save={save} />
|
||||||
</PageSection>
|
</PageSection>
|
||||||
)}
|
)}
|
||||||
{id && clientScope && (
|
{id && clientScope && (
|
||||||
|
|
|
@ -29,7 +29,7 @@ const clientScopeTypes = Object.keys(ClientScope);
|
||||||
export const allClientScopeTypes = Object.keys({
|
export const allClientScopeTypes = Object.keys({
|
||||||
...AllClientScopes,
|
...AllClientScopes,
|
||||||
...ClientScope,
|
...ClientScope,
|
||||||
});
|
}) as AllClientScopeType[];
|
||||||
|
|
||||||
export const clientScopeTypesSelectOptions = (
|
export const clientScopeTypesSelectOptions = (
|
||||||
t: TFunction,
|
t: TFunction,
|
||||||
|
|
Loading…
Reference in a new issue