From 721ed1bcaf6bb9ec7986ce1031d3d8321ce7393a Mon Sep 17 00:00:00 2001 From: Eugenia <32821331+jenny-s51@users.noreply.github.com> Date: Thu, 29 Apr 2021 13:39:19 -0400 Subject: [PATCH] Realm settings(themes): Add themes tab + tests (#544) * wip add realm themes * get switch value from server * add tests * fix admin theme help text * remove requireSsl forlabel * update dropdown options * wip fix for locales * format * save/update locales done * expose all themes in dropdown * remove comments * remove theme types --- .../integration/realm_settings_test.spec.ts | 15 +- .../realm_settings/RealmSettingsPage.ts | 113 +++-- src/realm-settings/RealmSettingsSection.tsx | 8 + src/realm-settings/ThemesTab.tsx | 419 ++++++++++++++++++ src/realm-settings/help.json | 6 +- src/realm-settings/messages.json | 33 +- 6 files changed, 553 insertions(+), 41 deletions(-) create mode 100644 src/realm-settings/ThemesTab.tsx diff --git a/cypress/integration/realm_settings_test.spec.ts b/cypress/integration/realm_settings_test.spec.ts index ee8aa79e58..4a610acb4c 100644 --- a/cypress/integration/realm_settings_test.spec.ts +++ b/cypress/integration/realm_settings_test.spec.ts @@ -23,9 +23,9 @@ describe("Realm settings test", () => { it("Go to general tab", function () { sidebarPage.goToRealmSettings(); realmSettingsPage.toggleSwitch(managedAccessSwitch); - realmSettingsPage.save(); + realmSettingsPage.saveGeneral(); realmSettingsPage.toggleSwitch(managedAccessSwitch); - realmSettingsPage.save(); + realmSettingsPage.saveGeneral(); }); it("Go to login tab", function () { @@ -36,5 +36,16 @@ describe("Realm settings test", () => { realmSettingsPage.toggleSwitch(rememberMeSwitch); realmSettingsPage.toggleSwitch(verifyEmailSwitch); }); + + it("Go to themes tab", function () { + sidebarPage.goToRealmSettings(); + cy.getId("rs-themes-tab").click(); + realmSettingsPage.selectLoginThemeType("keycloak"); + realmSettingsPage.selectAccountThemeType("keycloak"); + realmSettingsPage.selectAdminThemeType("keycloak.v2"); + realmSettingsPage.selectEmailThemeType("base"); + + realmSettingsPage.saveThemes(); + }); }); }); diff --git a/cypress/support/pages/admin_console/manage/realm_settings/RealmSettingsPage.ts b/cypress/support/pages/admin_console/manage/realm_settings/RealmSettingsPage.ts index 2f91179c8d..291a95e16c 100644 --- a/cypress/support/pages/admin_console/manage/realm_settings/RealmSettingsPage.ts +++ b/cypress/support/pages/admin_console/manage/realm_settings/RealmSettingsPage.ts @@ -1,40 +1,79 @@ export default class RealmSettingsPage { - saveBtn: string; - loginTab: string; - managedAccessSwitch: string; - userRegSwitch: string; - forgotPwdSwitch: string; - rememberMeSwitch: string; - emailAsUsernameSwitch: string; - loginWithEmailSwitch: string; - duplicateEmailsSwitch: string; - verifyEmailSwitch: string; + saveBtnGeneral: string; + saveBtnThemes: string; + loginTab: string; + selectLoginTheme: string; + loginThemeList: string; + selectAccountTheme: string; + accountThemeList: string; + selectAdminTheme: string; + adminThemeList: string; + selectEmailTheme: string; + emailThemeList: string; + selectDefaultLocale: string; + defaultLocaleList: string; - - constructor() { - this.saveBtn = "general-tab-save"; - this.loginTab = "rs-login-tab"; - this.managedAccessSwitch = "user-managed-access-switch"; - this.userRegSwitch = "user-reg-switch" - this.forgotPwdSwitch = "forgot-password-switch" - this.rememberMeSwitch = "remember-me-switch" - this.emailAsUsernameSwitch = "email-as-username-switch" - this.loginWithEmailSwitch = "login-with-email-switch" - this.duplicateEmailsSwitch = "duplicate-emails-switch" - this.verifyEmailSwitch = "verify-email-switch" - - } - - toggleSwitch(switchName: string) { - cy.getId(switchName).next().click(); - - return this; - } - - save() { - cy.getId(this.saveBtn).click(); - - return this; - } + constructor() { + this.saveBtnGeneral = "general-tab-save"; + this.saveBtnThemes = "themes-tab-save"; + this.loginTab = "rs-login-tab"; + this.selectLoginTheme = "#kc-login-theme"; + this.loginThemeList = "#kc-login-theme + ul"; + this.selectAccountTheme = "#kc-account-theme"; + this.accountThemeList = "#kc-account-theme + ul"; + this.selectAdminTheme = "#kc-admin-console-theme"; + this.adminThemeList = "#kc-admin-console-theme + ul"; + this.selectEmailTheme = "#kc-email-theme"; + this.emailThemeList = "#kc-email-theme + ul"; + this.selectDefaultLocale = "select-default-locale"; + this.defaultLocaleList = "select-default-locale + ul"; } - \ No newline at end of file + + selectLoginThemeType(themeType: string) { + cy.get(this.selectLoginTheme).click(); + cy.get(this.loginThemeList).contains(themeType).click(); + return this; + } + + selectAccountThemeType(themeType: string) { + cy.get(this.selectAccountTheme).click(); + cy.get(this.accountThemeList).contains(themeType).click(); + return this; + } + + selectAdminThemeType(themeType: string) { + cy.get(this.selectAdminTheme).click(); + cy.get(this.adminThemeList).contains(themeType).click(); + return this; + } + + selectEmailThemeType(themeType: string) { + cy.get(this.selectEmailTheme).click(); + cy.get(this.emailThemeList).contains(themeType).click(); + return this; + } + + setDefaultLocale(locale: string) { + cy.get(this.selectDefaultLocale).click(); + cy.get(this.defaultLocaleList).contains(locale).click(); + return this; + } + + toggleSwitch(switchName: string) { + cy.getId(switchName).next().click(); + + return this; + } + + saveGeneral() { + cy.getId(this.saveBtnGeneral).click(); + + return this; + } + + saveThemes() { + cy.getId(this.saveBtnThemes).click(); + + return this; + } +} diff --git a/src/realm-settings/RealmSettingsSection.tsx b/src/realm-settings/RealmSettingsSection.tsx index 4addbd4036..e45d4115c0 100644 --- a/src/realm-settings/RealmSettingsSection.tsx +++ b/src/realm-settings/RealmSettingsSection.tsx @@ -24,6 +24,7 @@ import { KeycloakTabs } from "../components/keycloak-tabs/KeycloakTabs"; import { RealmSettingsLoginTab } from "./LoginTab"; import { RealmSettingsGeneralTab } from "./GeneralTab"; import { PartialImportDialog } from "./PartialImport"; +import { RealmSettingsThemesTab } from "./ThemesTab"; type RealmSettingsHeaderProps = { onChange: (value: boolean) => void; @@ -177,6 +178,13 @@ export const RealmSettingsSection = () => { > + {t("realm-settings:themes")}} + data-testid="rs-themes-tab" + > + + diff --git a/src/realm-settings/ThemesTab.tsx b/src/realm-settings/ThemesTab.tsx new file mode 100644 index 0000000000..d2ad16def0 --- /dev/null +++ b/src/realm-settings/ThemesTab.tsx @@ -0,0 +1,419 @@ +import React, { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { Controller, useForm } from "react-hook-form"; +import { useErrorHandler } from "react-error-boundary"; +import { + ActionGroup, + AlertVariant, + Button, + FormGroup, + PageSection, + Select, + SelectOption, + SelectVariant, + Switch, +} from "@patternfly/react-core"; + +import RealmRepresentation from "keycloak-admin/lib/defs/realmRepresentation"; +import { useAdminClient, asyncStateFetch } from "../context/auth/AdminClient"; +import { useRealm } from "../context/realm-context/RealmContext"; +import { useAlerts } from "../components/alert/Alerts"; +import { FormAccess } from "../components/form-access/FormAccess"; +import { HelpItem } from "../components/help-enabler/HelpItem"; +import { useServerInfo } from "../context/server-info/ServerInfoProvider"; + +export const RealmSettingsThemesTab = () => { + const { t } = useTranslation("realm-settings"); + const adminClient = useAdminClient(); + const handleError = useErrorHandler(); + const { realm: realmName } = useRealm(); + const { addAlert } = useAlerts(); + const { control, setValue, handleSubmit } = useForm(); + const [realm, setRealm] = useState(); + + const [loginThemeOpen, setLoginThemeOpen] = useState(false); + const [accountThemeOpen, setAccountThemeOpen] = useState(false); + const [adminConsoleThemeOpen, setAdminConsoleThemeOpen] = useState(false); + const [emailThemeOpen, setEmailThemeOpen] = useState(false); + + const [supportedLocalesOpen, setSupportedLocalesOpen] = useState(false); + const [defaultLocaleOpen, setDefaultLocaleOpen] = useState(false); + const [selections, setSelections] = useState([]); + + const [ + internationalizationEnabled, + setInternationalizationEnabled, + ] = useState(false); + + const form = useForm(); + + const themeTypes = useServerInfo().themes!; + + const watchSupportedLocales = form.watch( + "supportedLocales", + themeTypes?.account![0].locales + ); + + useEffect(() => { + return asyncStateFetch( + () => adminClient.realms.findOne({ realm: realmName }), + (realm) => { + setRealm(realm); + setupForm(realm); + setInternationalizationEnabled(realm.internationalizationEnabled!); + }, + handleError + ); + }, []); + + useEffect(() => { + setValue("supportedLocales", realm?.supportedLocales); + setValue("defaultLocale", realm?.defaultLocale); + }, [internationalizationEnabled]); + + const setupForm = (realm: RealmRepresentation) => { + const { ...formValues } = realm; + + form.reset(formValues); + Object.entries(realm).map((entry) => { + if (entry[0] === "internationalizationEnabled") { + setInternationalizationEnabled(realm!.internationalizationEnabled!); + } + setValue(entry[0], entry[1]); + }); + }; + + const save = async (realm: RealmRepresentation) => { + try { + await adminClient.realms.update({ realm: realmName }, realm); + setRealm({ supportedLocales: selections, ...realm }); + addAlert(t("saveSuccess"), AlertVariant.success); + } catch (error) { + addAlert(t("saveError", { error }), AlertVariant.danger); + } + }; + + return ( + <> + + + + } + > + ( + + )} + /> + + + } + > + ( + + )} + /> + + + } + > + ( + + )} + /> + + + } + > + ( + + )} + /> + + + ( + { + onChange(value); + if (value) { + setValue("internationalizationEnabled", true); + setInternationalizationEnabled(value); + } + setInternationalizationEnabled(value); + }} + /> + )} + /> + + {internationalizationEnabled && ( + <> + + ( + + )} + /> + + + ( + + )} + /> + + + )} + + + + + + + + ); +}; diff --git a/src/realm-settings/help.json b/src/realm-settings/help.json index 3c9bc26a1e..fce3d33d47 100644 --- a/src/realm-settings/help.json +++ b/src/realm-settings/help.json @@ -3,6 +3,10 @@ "frontendUrl": "Set the frontend URL for the realm. Use in combination with the default hostname provider to override the base URL for frontend requests for a specific realm.", "requireSsl": "Is HTTPS required? 'None' means HTTPS is not required for any client IP address. 'External requests' means localhost and private IP addresses can access without HTTPS. 'All requests' means HTTPS is required for all IP addresses.", "userManagedAccess": "If enabled, users are allowed to manage their resources and permissions using the Account Management Console.", - "endpoints": "Shows the configuration of the protocol endpoints" + "endpoints": "Shows the configuration of the protocol endpoints", + "loginTheme": "Select theme for login, OTP, grant, registration and forgot password pages.", + "accountTheme": "Select theme for user account management pages.", + "adminConsoleTheme": "Select theme for admin console.", + "emailTheme": "Select theme for emails that are sent by the server." } } \ No newline at end of file diff --git a/src/realm-settings/messages.json b/src/realm-settings/messages.json index 87a6dff61c..a10dbb24ad 100644 --- a/src/realm-settings/messages.json +++ b/src/realm-settings/messages.json @@ -13,6 +13,7 @@ "saveError": "Realm could not be updated: {error}", "general": "General", "login": "Login", + "themes": "Themes", "userRegistration": "User registration", "userRegistrationHelpText": "Enable/disable the registration page. A link for registration will show on login page too.", "forgotPassword": "Forgot password", @@ -37,10 +38,40 @@ "external": "External requests", "none": "None" }, + "selectATheme": "Select a theme", + "allSupportedLocales": { + "ca": "Català", + "cs": "Čeština", + "da": "Dansk", + "de": "Deutsch", + "en": "English", + "es": "Español", + "fr": "Français", + "hu": "Magyar", + "it": "Italiano", + "ja": "日本語", + "lt": "Lietuvių kalba", + "nl": "Nederlands", + "no": "Norsk", + "pl": "Polski", + "pt-BR": "Português (Brasil)", + "ru": "Русский", + "sk": "Slovenčina", + "sv": "Svenska", + "tr": "Türkçe", + "zh-CN": "中文" + }, "userManagedAccess": "User-managed access", "endpoints": "Endpoints", "openEndpointConfiguration": "Open Endpoint Configuration", - "samlIdentityProviderMetadata": "SAML 2.0 Identity Provider Metadata" + "samlIdentityProviderMetadata": "SAML 2.0 Identity Provider Metadata", + "loginTheme": "Login theme", + "accountTheme": "Account theme", + "adminTheme": "Admin console theme", + "emailTheme": "Email theme", + "internationalization": "Internationalization", + "supportedLocales": "Supported locales", + "defaultLocale": "Default locale" }, "partial-import": { "partialImportHeaderText": "Partial import allows you to import users, clients, and resources from a previously exported json file.",