diff --git a/js/apps/admin-ui/cypress/e2e/realm_settings_tabs_test.spec.ts b/js/apps/admin-ui/cypress/e2e/realm_settings_tabs_test.spec.ts index 084bd61dc1..2b7a289303 100644 --- a/js/apps/admin-ui/cypress/e2e/realm_settings_tabs_test.spec.ts +++ b/js/apps/admin-ui/cypress/e2e/realm_settings_tabs_test.spec.ts @@ -101,13 +101,15 @@ describe("Realm settings tabs tests", () => { realmSettingsPage.fillReplyToEmail("replyTo@email.com"); realmSettingsPage.fillPort("10"); cy.findByTestId("email-tab-save").click(); - cy.get("#kc-display-name-helper").contains("You must enter a valid email."); - cy.get("#kc-host-helper").contains("Required field"); + cy.get("#smtpServer\\.from-helper").contains( + "You must enter a valid email.", + ); + cy.get("#smtpServer\\.host-helper").contains("Required field"); cy.findByTestId("email-tab-revert").click(); - cy.findByTestId("sender-email-address").should("be.empty"); - cy.findByTestId("from-display-name").should("be.empty"); - cy.get("#kc-port").should("be.empty"); + cy.findByTestId("smtpServer.from").should("be.empty"); + cy.findByTestId("smtpServer.fromDisplayName").should("be.empty"); + cy.findByTestId("smtpServer.port").should("be.empty"); realmSettingsPage.addSenderEmail("example@example.com"); realmSettingsPage.toggleCheck(realmSettingsPage.enableSslCheck); diff --git a/js/apps/admin-ui/cypress/support/pages/admin-ui/manage/realm_settings/RealmSettingsPage.ts b/js/apps/admin-ui/cypress/support/pages/admin-ui/manage/realm_settings/RealmSettingsPage.ts index acc7f9d67d..560536a192 100644 --- a/js/apps/admin-ui/cypress/support/pages/admin-ui/manage/realm_settings/RealmSettingsPage.ts +++ b/js/apps/admin-ui/cypress/support/pages/admin-ui/manage/realm_settings/RealmSettingsPage.ts @@ -27,7 +27,7 @@ export default class RealmSettingsPage extends CommonPage { adminThemeList = "#kc-admin-ui-theme + ul"; selectEmailTheme = "#kc-email-theme"; emailThemeList = "#kc-email-theme + ul"; - hostInput = "#kc-host"; + hostInput = "smtpServer.host"; ssoSessionIdleSelectMenu = "#kc-sso-session-idle-select-menu"; ssoSessionIdleSelectMenuList = "#kc-sso-session-idle-select-menu > div > ul"; ssoSessionMaxSelectMenu = "#kc-sso-session-max-select-menu"; @@ -76,7 +76,7 @@ export default class RealmSettingsPage extends CommonPage { duplicateEmailsSwitch = "duplicate-emails-switch"; verifyEmailSwitch = "verify-email-switch"; authSwitch = "email-authentication-switch"; - fromInput = "sender-email-address"; + fromInput = "smtpServer.from"; enableSslCheck = "enable-ssl"; enableStartTlsCheck = "enable-start-tls"; addProviderDropdown = "addProviderDropdown"; @@ -98,8 +98,8 @@ export default class RealmSettingsPage extends CommonPage { emailAddressInput = "email-address-input"; addBundleButton = "add-translationBtn"; confirmAddTranslation = "add-translation-confirm-button"; - keyInput = "key-input"; - valueInput = "value-input"; + keyInput = "key"; + valueInput = "value"; deleteAction = "delete-action"; modalConfirm = "confirm"; ssoSessionIdleInput = "sso-session-idle-input"; @@ -173,8 +173,8 @@ export default class RealmSettingsPage extends CommonPage { #jsonEditorSelect = "jsonEditor-profilesView"; #formViewSelectPolicies = "formView-policiesView"; #jsonEditorSelectPolicies = "jsonEditor-policiesView"; - #newClientProfileNameInput = "client-profile-name"; - #newClientProfileDescriptionInput = "client-profile-description"; + #newClientProfileNameInput = "name"; + #newClientProfileDescriptionInput = "description"; #saveNewClientProfileBtn = "saveCreateProfile"; #cancelNewClientProfile = "cancelCreateProfile"; #createPolicyEmptyStateBtn = "no-client-policies-empty-action"; @@ -234,9 +234,9 @@ export default class RealmSettingsPage extends CommonPage { #frontEndURL = "#kc-frontend-url"; #requireSSL = "#kc-require-ssl"; #unmanagedAttributes = "#kc-user-profile-unmanaged-attribute-policy"; - #fromDisplayName = "from-display-name"; - #replyToEmail = "#kc-reply-to"; - #port = "#kc-port"; + #fromDisplayName = "smtpServer.fromDisplayName"; + #replyToEmail = "smtpServer.replyTo"; + #port = "smtpServer.port"; #publicKeyBtn = ".kc-keys-list > tbody > tr > td > .button-wrapper > button"; #localizationLocalesSubTab = "rs-localization-locales-tab"; @@ -300,7 +300,8 @@ export default class RealmSettingsPage extends CommonPage { } fillHostField(host: string) { - cy.get(this.hostInput).clear().type(host); + cy.findByTestId(this.hostInput).clear(); + cy.findByTestId(this.hostInput).type(host); return this; } @@ -339,11 +340,13 @@ export default class RealmSettingsPage extends CommonPage { } fillReplyToEmail(email: string) { - cy.get(this.#replyToEmail).clear().type(email); + cy.findByTestId(this.#replyToEmail).clear(); + cy.findByTestId(this.#replyToEmail).type(email); } fillPort(port: string) { - cy.get(this.#port).clear().type(port); + cy.findByTestId(this.#port).clear(); + cy.findByTestId(this.#port).type(port); } fillFrontendURL(url: string) { diff --git a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties index 0d58bf7167..ec0d5bc748 100644 --- a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties +++ b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties @@ -3105,3 +3105,10 @@ emptyAdminEvents=No admin events emptyAdminEventsInstructions=There are no admin events in this realm. emptyUserEvents=No user events emptyUserEventsInstructions=There are no user events in this realm. +smtpFromPlaceholder=Sender email address +smtpFromDisplayPlaceholder=Display name for Sender email address +replyToEmailPlaceholder=Reply to email address +replyToDisplayPlaceholder=Display name for "reply to" email address +senderEnvelopePlaceholder=Sender envelope email address +smtpPortPlaceholder=SMTP port (defaults to 25) +loginUsernamePlaceholder=Login username diff --git a/js/apps/admin-ui/src/realm-settings/AddTranslationModal.tsx b/js/apps/admin-ui/src/realm-settings/AddTranslationModal.tsx index 1ed39d2aa3..602be4f3f4 100644 --- a/js/apps/admin-ui/src/realm-settings/AddTranslationModal.tsx +++ b/js/apps/admin-ui/src/realm-settings/AddTranslationModal.tsx @@ -2,16 +2,13 @@ import { Button, ButtonVariant, Form, - FormGroup, Modal, ModalVariant, - ValidatedOptions, } from "@patternfly/react-core"; -import { SubmitHandler, UseFormReturn } from "react-hook-form"; +import { FormProvider, SubmitHandler, UseFormReturn } from "react-hook-form"; import { useTranslation } from "react-i18next"; - +import { TextControl } from "ui-shared"; import type { KeyValueType } from "../components/key-value-form/key-value-convert"; -import { KeycloakTextInput } from "../components/keycloak-text-input/KeycloakTextInput"; type AddTranslationModalProps = { id?: string; @@ -29,11 +26,7 @@ export type TranslationForm = { export const AddTranslationModal = ({ handleModalToggle, save, - form: { - register, - handleSubmit, - formState: { errors }, - }, + form, }: AddTranslationModalProps) => { const { t } = useTranslation(); @@ -66,46 +59,28 @@ export const AddTranslationModal = ({ , ]} > -
- - + + - - - - +
); diff --git a/js/apps/admin-ui/src/realm-settings/ClientProfileForm.tsx b/js/apps/admin-ui/src/realm-settings/ClientProfileForm.tsx index 85f7cde5c3..c5d3bb25e1 100644 --- a/js/apps/admin-ui/src/realm-settings/ClientProfileForm.tsx +++ b/js/apps/admin-ui/src/realm-settings/ClientProfileForm.tsx @@ -14,27 +14,22 @@ import { DropdownItem, Flex, FlexItem, - FormGroup, Label, PageSection, Text, TextVariants, - ValidatedOptions, } from "@patternfly/react-core"; import { PlusCircleIcon, TrashIcon } from "@patternfly/react-icons"; import { Fragment, useMemo, useState } from "react"; -import { useFieldArray, useForm } from "react-hook-form"; +import { FormProvider, useFieldArray, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; -import { HelpItem } from "ui-shared"; - +import { HelpItem, TextAreaControl, TextControl } from "ui-shared"; import { adminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { FormAccess } from "../components/form/FormAccess"; import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"; -import { KeycloakTextArea } from "../components/keycloak-text-area/KeycloakTextArea"; -import { KeycloakTextInput } from "../components/keycloak-text-input/KeycloakTextInput"; import { ViewHeader } from "../components/view-header/ViewHeader"; import { useServerInfo } from "../context/server-info/ServerInfoProvider"; import { useFetch } from "../utils/useFetch"; @@ -57,17 +52,18 @@ const defaultValues: ClientProfileForm = { export default function ClientProfileForm() { const { t } = useTranslation(); const navigate = useNavigate(); + const form = useForm({ + defaultValues, + mode: "onChange", + }); + const { handleSubmit, setValue, getValues, - register, - formState: { isDirty, errors }, + formState: { isDirty }, control, - } = useForm({ - defaultValues, - mode: "onChange", - }); + } = form; const { fields: profileExecutors, remove } = useFieldArray({ name: "executors", @@ -220,216 +216,207 @@ export default function ClientProfileForm() { } /> - - - + + - - - - - - {!isGlobalProfile && ( - - )} - {editMode && !isGlobalProfile && ( - - )} - {!editMode && !isGlobalProfile && ( - - )} - - {editMode && ( - <> - - - - {t("executors")} - + {!isGlobalProfile && ( + + )} + {editMode && !isGlobalProfile && ( + + )} + {!editMode && !isGlobalProfile && ( + + )} + data-testid={"cancelCreateProfile"} + > + {t("cancel")} + + )} + + {editMode && ( + <> + + + + {t("executors")} + + - )} - - {profileExecutors.length > 0 && ( - <> - - {profileExecutors.map((executor, idx) => ( - + - ) : ( - - {executor.executor} - - )} - {executorTypes - ?.filter( - (type) => type.id === executor.executor, - ) - .map((type) => ( - - - {!isGlobalProfile && ( - + + )} + + {profileExecutors.length > 0 && ( + <> + + {profileExecutors.map((executor, idx) => ( + + + + {executor.configuration ? ( + + ) : ( + + {executor.executor} + + )} + {executorTypes + ?.filter( + (type) => type.id === executor.executor, + ) + .map((type) => ( + + + {!isGlobalProfile && ( + + )} + + )} + {profileExecutors.length === 0 && ( + <> + + - {t("back")} - - )} - - )} - {profileExecutors.length === 0 && ( - <> - - - {t("emptyExecutors")} - - - )} - - )} - + {t("emptyExecutors")} + + + )} + + )} + + ); diff --git a/js/apps/admin-ui/src/realm-settings/EmailTab.tsx b/js/apps/admin-ui/src/realm-settings/EmailTab.tsx index 7e2cc01893..af8a7622ee 100644 --- a/js/apps/admin-ui/src/realm-settings/EmailTab.tsx +++ b/js/apps/admin-ui/src/realm-settings/EmailTab.tsx @@ -9,16 +9,14 @@ import { Checkbox, FormGroup, PageSection, - Switch, } from "@patternfly/react-core"; -import { Controller, useForm, useWatch } from "react-hook-form"; +import { Controller, FormProvider, useForm, useWatch } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; -import { FormPanel, HelpItem } from "ui-shared"; +import { FormPanel, HelpItem, SwitchControl, TextControl } from "ui-shared"; import { adminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { FormAccess } from "../components/form/FormAccess"; -import { KeycloakTextInput } from "../components/keycloak-text-input/KeycloakTextInput"; import { PasswordInput } from "../components/password-input/PasswordInput"; import { useRealm } from "../context/realm-context/RealmContext"; import { toUser } from "../user/routes/User"; @@ -43,6 +41,7 @@ export const RealmSettingsEmailTab = ({ const { addAlert, addError } = useAlerts(); const currentUser = useCurrentUser(); + const form = useForm({ defaultValues: realm }); const { register, control, @@ -51,7 +50,7 @@ export const RealmSettingsEmailTab = ({ reset: resetForm, getValues, formState: { errors }, - } = useForm({ defaultValues: realm }); + } = form; const reset = () => resetForm(realm); const watchFromValue = watch("smtpServer.from", ""); @@ -98,301 +97,232 @@ export const RealmSettingsEmailTab = ({ return ( - - - + + - - - - } - > - - - - - - - } - > - - - - } - > - - - - - - + + - - - - - - - - ( - field.onChange("" + value)} - /> - )} - /> - ( - field.onChange("" + value)} - /> - )} - /> - - - ( - { - field.onChange("" + value); - }} - aria-label={t("authentication")} - /> - )} - /> - - {authenticationEnabled === "true" && ( - <> - - - - + ( + field.onChange("" + value)} /> - } - > - - - - )} - {currentUser && ( - - {currentUser.email ? ( - - ) : ( - ( - - )} - > - {t("testConnectionHint.withoutEmailAction")} - - } - /> - )} + )} + /> + ( + field.onChange("" + value)} + /> + )} + /> - )} - - - - - - - - - - - - - + + {authenticationEnabled === "true" && ( + <> + + + } + > + + + + )} + {currentUser && ( + + {currentUser.email ? ( + + ) : ( + ( + + )} + > + {t("testConnectionHint.withoutEmailAction")} + + } + /> + )} + + )} + + + + + + + + + + + + + + ); }; diff --git a/js/libs/ui-shared/src/controls/TextAreaControl.tsx b/js/libs/ui-shared/src/controls/TextAreaControl.tsx index c42bf24ba2..b0c677be1d 100644 --- a/js/libs/ui-shared/src/controls/TextAreaControl.tsx +++ b/js/libs/ui-shared/src/controls/TextAreaControl.tsx @@ -7,7 +7,6 @@ import { UseControllerProps, } from "react-hook-form"; import { FormLabel } from "./FormLabel"; - import { KeycloakTextArea } from "./keycloak-text-area/KeycloakTextArea"; export type TextAreaControlProps<