Use react-hook-form v7 for client scope form (#3885)

This commit is contained in:
Jon Koops 2022-12-06 14:38:28 +01:00 committed by GitHub
parent 49ed77b85c
commit c2924ceb9c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 80 additions and 96 deletions

View file

@ -272,12 +272,7 @@ describe("Client Scopes test", () => {
sidebarPage.waitForPageLoad();
listingPage.goToCreateItem();
createClientScopePage.save().checkClientNameRequiredMessage();
createClientScopePage
.fillClientScopeData("address")
.save()
.checkClientNameRequiredMessage(false);
createClientScopePage.fillClientScopeData("address").save();
// The error should inform about duplicated name/id
masthead.checkNotificationMessage(

View file

@ -26,7 +26,7 @@ export default class CreateClientScopePage extends CommonPage {
this.clientScopeDescriptionInput = "#kc-description";
this.clientScopeTypeDrpDwn = "#kc-protocol";
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.displayOnConsentInput + " + .pf-c-switch__toggle";
this.consentScreenTextInput = "#kc-consent-screen-text";
@ -73,12 +73,6 @@ export default class CreateClientScopePage extends CommonPage {
return this;
}
checkClientNameRequiredMessage(exist = true) {
cy.get(this.clientScopeNameError).should((!exist ? "not." : "") + "exist");
return this;
}
getSwitchDisplayOnConsentScreenInput() {
return cy.get(this.displayOnConsentInput);
}

View file

@ -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 {
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,
ClientScopeDefaultOptionalType,
clientScopeTypesSelectOptions,
} from "../../components/client-scope/ClientScopeTypes";
import { FormAccess } from "../../components/form-access/FormAccess";
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 { convertAttributeNameToForm, convertToFormValues } from "../../util";
import { useRealm } from "../../context/realm-context/RealmContext";
import { getProtocolName } from "../../clients/utils";
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 = {
clientScope: ClientScopeRepresentation;
clientScope?: ClientScopeRepresentation;
save: (clientScope: ClientScopeDefaultOptionalType) => void;
};
@ -43,32 +42,30 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
control,
handleSubmit,
setValue,
formState: { errors },
} = useForm<ClientScopeRepresentation>();
formState: { errors, isDirty, isValid },
} = useForm<ClientScopeDefaultOptionalType>({ mode: "onChange" });
const { realm } = useRealm();
const providers = useLoginProviders();
const [open, isOpen] = useState(false);
const [openType, setOpenType] = useState(false);
const { id } = useParams<{ id: string }>();
const displayOnConsentScreen = useWatch<string>({
const displayOnConsentScreen: string = useWatch({
control,
name: convertAttributeNameToForm("attributes.display.on.consent.screen"),
defaultValue:
clientScope.attributes?.["display.on.consent.screen"] ??
(id ? "false" : "true"),
clientScope?.attributes?.["display.on.consent.screen"] ?? "true",
});
useEffect(() => {
convertToFormValues(clientScope, setValue);
convertToFormValues(clientScope ?? {}, setValue);
}, [clientScope]);
return (
<FormAccess
isHorizontal
onSubmit={handleSubmit(save)}
role="manage-clients"
onSubmit={handleSubmit(save)}
isHorizontal
>
<FormGroup
label={t("common:name")}
@ -76,24 +73,19 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
<HelpItem helpText="client-scopes-help:name" fieldLabelId="name" />
}
fieldId="kc-name"
isRequired
validated={
errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
helperTextInvalid={t("common:required")}
isRequired
>
<KeycloakTextInput
ref={register({
required: true,
validate: (value: string) =>
!!value.trim() || t("common:required").toString(),
})}
type="text"
id="kc-name"
name="name"
validated={
errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
isRequired
{...register("name", { required: true })}
/>
</FormGroup>
<FormGroup
@ -111,17 +103,15 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
helperTextInvalid={t("common:maxLength", { length: 255 })}
>
<KeycloakTextInput
ref={register({
maxLength: 255,
})}
id="kc-description"
validated={
errors.description
? ValidatedOptions.error
: ValidatedOptions.default
}
type="text"
id="kc-description"
name="description"
{...register("description", {
maxLength: 255,
})}
/>
</FormGroup>
<FormGroup
@ -132,21 +122,21 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
fieldLabelId="client-scopes:type"
/>
}
fieldId="type"
fieldId="kc-type"
>
<Controller
name="type"
defaultValue={allClientScopeTypes[0]}
control={control}
render={({ onChange, value }) => (
render={({ field }) => (
<Select
id="type"
toggleId="kc-type"
variant={SelectVariant.single}
isOpen={openType}
selections={value}
selections={field.value}
onToggle={setOpenType}
onSelect={(_, value) => {
onChange(value);
field.onChange(value);
setOpenType(false);
}}
>
@ -155,7 +145,7 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
)}
/>
</FormGroup>
{!id && (
{!clientScope && (
<FormGroup
label={t("protocol")}
labelIcon={
@ -170,23 +160,21 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
name="protocol"
defaultValue={providers[0]}
control={control}
render={({ onChange, value }) => (
render={({ field }) => (
<Select
toggleId="kc-protocol"
required
onToggle={isOpen}
onSelect={(_, value) => {
onChange(value as string);
field.onChange(value);
isOpen(false);
}}
selections={value}
selections={field.value}
variant={SelectVariant.single}
aria-label={t("selectEncryptionType")}
isOpen={open}
>
{providers.map((option) => (
<SelectOption
selected={option === value}
selected={option === field.value}
key={option}
value={option}
data-testid={`option-${option}`}
@ -208,22 +196,21 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
fieldLabelId="client-scopes:displayOnConsentScreen"
/>
}
fieldId="kc-display.on.consent.screen"
fieldId="kc-display-on-consent-screen"
>
<Controller
name={convertAttributeNameToForm(
name={convertAttributeNameToForm<ClientScopeDefaultOptionalType>(
"attributes.display.on.consent.screen"
)}
control={control}
defaultValue={displayOnConsentScreen}
render={({ onChange, value }) => (
render={({ field }) => (
<Switch
id="kc-display.on.consent.screen-switch"
id="kc-display-on-consent-screen"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value === "true"}
onChange={(value) => onChange("" + value)}
aria-label={t("displayOnConsentScreen")}
isChecked={field.value === "true"}
onChange={(value) => field.onChange(value.toString())}
/>
)}
/>
@ -240,10 +227,12 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
fieldId="kc-consent-screen-text"
>
<KeycloakTextArea
ref={register}
type="text"
id="kc-consent-screen-text"
name={convertAttributeNameToForm("attributes.consent.screen.text")}
{...register(
convertAttributeNameToForm<ClientScopeDefaultOptionalType>(
"attributes.consent.screen.text"
)
)}
/>
</FormGroup>
)}
@ -256,20 +245,21 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
fieldLabelId="client-scopes:includeInTokenScope"
/>
}
fieldId="includeInTokenScope"
fieldId="kc-include-in-token-scope"
>
<Controller
name={convertAttributeNameToForm("attributes.include.in.token.scope")}
name={convertAttributeNameToForm<ClientScopeDefaultOptionalType>(
"attributes.include.in.token.scope"
)}
control={control}
defaultValue="true"
render={({ onChange, value }) => (
render={({ field }) => (
<Switch
id="includeInTokenScope-switch"
id="kc-include-in-token-scope"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value === "true"}
onChange={(value) => onChange("" + value)}
aria-label={t("includeInTokenScope")}
isChecked={field.value === "true"}
onChange={(value) => field.onChange(value.toString())}
/>
)}
/>
@ -285,23 +275,28 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
fieldId="kc-gui-order"
>
<Controller
name={convertAttributeNameToForm("attributes.gui.order")}
name={convertAttributeNameToForm<ClientScopeDefaultOptionalType>(
"attributes.gui.order"
)}
defaultValue=""
control={control}
render={({ onChange, value }) => (
render={({ field }) => (
<KeycloakTextInput
id="kc-gui-order"
type="number"
value={value}
data-testid="displayOrder"
value={field.value}
min={0}
onChange={onChange}
onChange={field.onChange}
/>
)}
/>
</FormGroup>
<ActionGroup>
<Button variant="primary" type="submit">
<Button
variant="primary"
type="submit"
isDisabled={!isDirty || !isValid}
>
{t("common:save")}
</Button>
<Button

View file

@ -242,7 +242,7 @@ export default function ClientScopeForm() {
<PageSection variant="light" className="pf-u-p-0">
{!id && (
<PageSection variant="light">
<ScopeForm save={save} clientScope={{}} />
<ScopeForm save={save} />
</PageSection>
)}
{id && clientScope && (

View file

@ -29,7 +29,7 @@ const clientScopeTypes = Object.keys(ClientScope);
export const allClientScopeTypes = Object.keys({
...AllClientScopes,
...ClientScope,
});
}) as AllClientScopeType[];
export const clientScopeTypesSelectOptions = (
t: TFunction,