From c2924ceb9c144ffe68779d217063b204ef6e8755 Mon Sep 17 00:00:00 2001 From: Jon Koops Date: Tue, 6 Dec 2022 14:38:28 +0100 Subject: [PATCH] Use `react-hook-form` v7 for client scope form (#3885) --- .../cypress/e2e/client_scopes_test.spec.ts | 7 +- .../client_scopes/CreateClientScopePage.ts | 8 +- .../src/client-scopes/details/ScopeForm.tsx | 157 +++++++++--------- .../client-scopes/form/ClientScopeForm.tsx | 2 +- .../client-scope/ClientScopeTypes.tsx | 2 +- 5 files changed, 80 insertions(+), 96 deletions(-) diff --git a/apps/admin-ui/cypress/e2e/client_scopes_test.spec.ts b/apps/admin-ui/cypress/e2e/client_scopes_test.spec.ts index 2c848c84fc..57570e58fb 100644 --- a/apps/admin-ui/cypress/e2e/client_scopes_test.spec.ts +++ b/apps/admin-ui/cypress/e2e/client_scopes_test.spec.ts @@ -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( diff --git a/apps/admin-ui/cypress/support/pages/admin_console/manage/client_scopes/CreateClientScopePage.ts b/apps/admin-ui/cypress/support/pages/admin_console/manage/client_scopes/CreateClientScopePage.ts index b82dde3e61..2a6bfaad1f 100644 --- a/apps/admin-ui/cypress/support/pages/admin_console/manage/client_scopes/CreateClientScopePage.ts +++ b/apps/admin-ui/cypress/support/pages/admin_console/manage/client_scopes/CreateClientScopePage.ts @@ -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); } diff --git a/apps/admin-ui/src/client-scopes/details/ScopeForm.tsx b/apps/admin-ui/src/client-scopes/details/ScopeForm.tsx index 1496c4fb11..d4d6d50aa8 100644 --- a/apps/admin-ui/src/client-scopes/details/ScopeForm.tsx +++ b/apps/admin-ui/src/client-scopes/details/ScopeForm.tsx @@ -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(); + formState: { errors, isDirty, isValid }, + } = useForm({ 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({ + 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 ( { } fieldId="kc-name" - isRequired validated={ errors.name ? ValidatedOptions.error : ValidatedOptions.default } helperTextInvalid={t("common:required")} + isRequired > - !!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 })} /> { helperTextInvalid={t("common:maxLength", { length: 255 })} > { fieldLabelId="client-scopes:type" /> } - fieldId="type" + fieldId="kc-type" > ( + render={({ field }) => ( { - 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) => ( { fieldLabelId="client-scopes:displayOnConsentScreen" /> } - fieldId="kc-display.on.consent.screen" + fieldId="kc-display-on-consent-screen" > ( "attributes.display.on.consent.screen" )} control={control} defaultValue={displayOnConsentScreen} - render={({ onChange, value }) => ( + render={({ field }) => ( 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" > ( + "attributes.consent.screen.text" + ) + )} /> )} @@ -256,20 +245,21 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => { fieldLabelId="client-scopes:includeInTokenScope" /> } - fieldId="includeInTokenScope" + fieldId="kc-include-in-token-scope" > ( + "attributes.include.in.token.scope" + )} control={control} defaultValue="true" - render={({ onChange, value }) => ( + render={({ field }) => ( 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" > ( + "attributes.gui.order" + )} defaultValue="" control={control} - render={({ onChange, value }) => ( + render={({ field }) => ( )} /> -