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 1fabe634f94..8b084cdb677 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 @@ -505,7 +505,7 @@ eventTypes.CLIENT_LOGIN.name=Client login mapper.nameid.format.tooltip=This mapper is applied only if the NameID format of the incoming AuthnRequest is equal to this value. hideOnLoginPageHelp=If hidden, login with this provider is possible only if requested explicitly, for example using the 'kc_idp_hint' parameter. eventTypes.UPDATE_PROFILE.description=Update profile -assignRolesTo=Assign roles to {{client}} +assignRolesTo=Assign {{type}} roles to {{client}} orderChangeError=Could not change display order of identity providers {{error}} policyProvider.client-scope=Define conditions for your permissions where a set of one or more client scopes is permitted to access an object. secretExpiresOn=Secret expires on {{time}} diff --git a/js/apps/admin-ui/src/clients/authorization/policy/Role.tsx b/js/apps/admin-ui/src/clients/authorization/policy/Role.tsx index 5b6c7c6c157..d5afa79a6fb 100644 --- a/js/apps/admin-ui/src/clients/authorization/policy/Role.tsx +++ b/js/apps/admin-ui/src/clients/authorization/policy/Role.tsx @@ -7,7 +7,11 @@ import { Controller, useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useAdminClient } from "../../../admin-client"; import { DefaultSwitchControl } from "../../../components/SwitchControl"; -import { AddRoleMappingModal } from "../../../components/role-mapping/AddRoleMappingModal"; +import { + AddRoleButton, + AddRoleMappingModal, + FilterType, +} from "../../../components/role-mapping/AddRoleMappingModal"; import { Row, ServiceRole } from "../../../components/role-mapping/RoleMapping"; import type { RequiredIdValue } from "./ClientScope"; @@ -22,6 +26,8 @@ export const Role = () => { const values = getValues("roles"); const [open, setOpen] = useState(false); + const [filterType, setFilterType] = useState("clients"); + const [selectedRoles, setSelectedRoles] = useState([]); useFetch( @@ -67,6 +73,7 @@ export const Role = () => { id="role" type="roles" title={t("assignRole")} + filterType={filterType} onAssign={(rows) => { field.onChange([ ...(field.value || []), @@ -80,15 +87,14 @@ export const Role = () => { }} /> )} - + /> )} /> diff --git a/js/apps/admin-ui/src/components/dynamic/RoleComponent.tsx b/js/apps/admin-ui/src/components/dynamic/RoleComponent.tsx index 39a8a42f2ab..51cd17c2f2a 100644 --- a/js/apps/admin-ui/src/components/dynamic/RoleComponent.tsx +++ b/js/apps/admin-ui/src/components/dynamic/RoleComponent.tsx @@ -1,15 +1,14 @@ import { FormErrorText, HelpItem } from "@keycloak/keycloak-ui-shared"; -import { - Button, - Chip, - FormGroup, - Split, - SplitItem, -} from "@patternfly/react-core"; +import { Chip, FormGroup, Split, SplitItem } from "@patternfly/react-core"; +import { useState } from "react"; import { Controller, useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; import useToggle from "../../utils/useToggle"; -import { AddRoleMappingModal } from "../role-mapping/AddRoleMappingModal"; +import { + AddRoleButton, + AddRoleMappingModal, + FilterType, +} from "../role-mapping/AddRoleMappingModal"; import { Row, ServiceRole } from "../role-mapping/RoleMapping"; import type { ComponentProps } from "./components"; @@ -33,6 +32,8 @@ export const RoleComponent = ({ const { t } = useTranslation(); const [openModal, toggleModal] = useToggle(); + const [filterType, setFilterType] = useState("clients"); + const { control, formState: { errors }, @@ -57,6 +58,7 @@ export const RoleComponent = ({ field.onChange(parseRow(rows[0]))} onClose={toggleModal} @@ -75,14 +77,16 @@ export const RoleComponent = ({ )} - + isDisabled={isDisabled} + /> )} diff --git a/js/apps/admin-ui/src/components/role-mapping/AddRoleMappingModal.tsx b/js/apps/admin-ui/src/components/role-mapping/AddRoleMappingModal.tsx index 26b8fed32af..daf0680363e 100644 --- a/js/apps/admin-ui/src/components/role-mapping/AddRoleMappingModal.tsx +++ b/js/apps/admin-ui/src/components/role-mapping/AddRoleMappingModal.tsx @@ -8,12 +8,11 @@ import { Dropdown, DropdownItem, DropdownList, + DropdownProps, MenuToggle, Modal, ModalVariant, - ToolbarItem, } from "@patternfly/react-core"; -import { FilterIcon } from "@patternfly/react-icons"; import { cellWidth, TableText } from "@patternfly/react-table"; import { useState } from "react"; import { useTranslation } from "react-i18next"; @@ -21,13 +20,15 @@ import { useAdminClient } from "../../admin-client"; import { useAccess } from "../../context/access/Access"; import { translationFormatter } from "../../utils/translationFormatter"; import useLocaleSort from "../../utils/useLocaleSort"; -import { ResourcesKey, Row, ServiceRole } from "./RoleMapping"; +import useToggle from "../../utils/useToggle"; +import { ResourcesKey, Row } from "./RoleMapping"; import { getAvailableRoles } from "./queries"; import { getAvailableClientRoles } from "./resource"; type AddRoleMappingModalProps = { id: string; type: ResourcesKey; + filterType: FilterType; name?: string; isRadio?: boolean; onAssign: (rows: Row[]) => void; @@ -36,7 +37,7 @@ type AddRoleMappingModalProps = { actionLabel?: string; }; -type FilterType = "roles" | "clients"; +export type FilterType = "roles" | "clients"; const RoleDescription = ({ role }: { role: RoleRepresentation }) => { const { t } = useTranslation(); @@ -47,11 +48,78 @@ const RoleDescription = ({ role }: { role: RoleRepresentation }) => { ); }; +type AddRoleButtonProps = Omit< + DropdownProps, + "children" | "toggle" | "isOpen" | "onOpenChange" +> & { + label?: string; + variant?: "default" | "plain" | "primary" | "plainText" | "secondary"; + isDisabled?: boolean; + onFilerTypeChange: (type: FilterType) => void; +}; + +export const AddRoleButton = ({ + label, + variant, + isDisabled, + onFilerTypeChange, + ...rest +}: AddRoleButtonProps) => { + const { t } = useTranslation(); + const [open, toggle] = useToggle(); + + const { hasAccess } = useAccess(); + const canViewRealmRoles = hasAccess("view-realm") || hasAccess("query-users"); + + return ( + ( + + {t(label || "assignRole")} + + )} + isOpen={open} + {...rest} + > + + { + onFilerTypeChange("clients"); + }} + > + {t("clientRoles")} + + {canViewRealmRoles && ( + { + onFilerTypeChange("roles"); + }} + > + {t("realmRoles")} + + )} + + + ); +}; + export const AddRoleMappingModal = ({ id, name, type, isRadio, + filterType, onAssign, onClose, title, @@ -60,15 +128,7 @@ export const AddRoleMappingModal = ({ const { adminClient } = useAdminClient(); const { t } = useTranslation(); - const { hasAccess } = useAccess(); - const canViewRealmRoles = hasAccess("view-realm") || hasAccess("query-users"); - - const [searchToggle, setSearchToggle] = useState(false); - - const [filterType, setFilterType] = useState("clients"); const [selectedRows, setSelectedRows] = useState([]); - const [key, setKey] = useState(0); - const refresh = () => setKey(key + 1); const localeSort = useLocaleSort(); const compareRow = ({ role: { name } }: Row) => name?.toUpperCase(); @@ -120,10 +180,37 @@ export const AddRoleMappingModal = ({ ); }; + const columns = [ + { + name: "role.name", + displayKey: "name", + transforms: [cellWidth(30)], + }, + { + name: "client.clientId", + displayKey: "clientId", + }, + { + name: "role.description", + displayKey: "description", + cellRenderer: RoleDescription, + }, + ]; + + if (filterType === "roles") { + columns.splice(1, 1); + } + return ( setSelectedRows([...rows])} - searchPlaceholderKey="searchByRoleName" - isPaginated={!(filterType === "roles" && type !== "roles")} - searchTypeComponent={ - canViewRealmRoles && ( - - setSearchToggle(isOpen)} - onSelect={() => { - setFilterType(filterType === "roles" ? "clients" : "roles"); - setSearchToggle(false); - refresh(); - }} - toggle={(ref) => ( - setSearchToggle(!searchToggle)} - icon={} - > - {filterType === "roles" - ? t("filterByRoles") - : t("filterByClients")} - - )} - isOpen={searchToggle} - > - - - {filterType === "roles" - ? t("filterByClients") - : t("filterByRoles")} - - - - - ) + searchPlaceholderKey={ + filterType === "roles" ? "searchByRoleName" : "search" } + isPaginated={!(filterType === "roles" && type !== "roles")} canSelectAll isRadio={isRadio} loader={filterType === "roles" ? loader : clientRolesLoader} ariaLabelKey="associatedRolesText" - columns={[ - { - name: "name", - cellRenderer: ServiceRole, - transforms: [cellWidth(30)], - }, - { - name: "role.description", - displayKey: "description", - cellRenderer: RoleDescription, - }, - ]} + columns={columns} emptyState={ { - setFilterType("roles"); - refresh(); - }, - }, - ]} /> } /> diff --git a/js/apps/admin-ui/src/components/role-mapping/RoleMapping.tsx b/js/apps/admin-ui/src/components/role-mapping/RoleMapping.tsx index 16711aa5561..49b5c25e51a 100644 --- a/js/apps/admin-ui/src/components/role-mapping/RoleMapping.tsx +++ b/js/apps/admin-ui/src/components/role-mapping/RoleMapping.tsx @@ -19,7 +19,11 @@ import { translationFormatter } from "../../utils/translationFormatter"; import { useConfirmDialog } from "../confirm-dialog/ConfirmDialog"; import { ListEmptyState } from "@keycloak/keycloak-ui-shared"; import { Action, KeycloakDataTable } from "@keycloak/keycloak-ui-shared"; -import { AddRoleMappingModal } from "./AddRoleMappingModal"; +import { + AddRoleButton, + AddRoleMappingModal, + FilterType, +} from "./AddRoleMappingModal"; import { deleteMapping, getEffectiveRoles, getMapping } from "./queries"; import { getEffectiveClientRoles } from "./resource"; @@ -99,6 +103,7 @@ export const RoleMapping = ({ const [hide, setHide] = useState(true); const [showAssign, setShowAssign] = useState(false); + const [filterType, setFilterType] = useState("clients"); const [selected, setSelected] = useState([]); const assignRoles = async (rows: Row[]) => { @@ -179,6 +184,7 @@ export const RoleMapping = ({ setShowAssign(false)} @@ -213,12 +219,12 @@ export const RoleMapping = ({ {isManager && ( <> - + { + setFilterType(type); + setShowAssign(true); + }} + /> + onFilerTypeChange={(type) => { + setFilterType(type); + setIsModalOpen(true); + }} + /> {selectedRoles.length > 0 && ( diff --git a/js/apps/admin-ui/src/realm-roles/RealmRoleTabs.tsx b/js/apps/admin-ui/src/realm-roles/RealmRoleTabs.tsx index 85a76b50531..d9ad10da970 100644 --- a/js/apps/admin-ui/src/realm-roles/RealmRoleTabs.tsx +++ b/js/apps/admin-ui/src/realm-roles/RealmRoleTabs.tsx @@ -1,5 +1,9 @@ import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation"; -import { useAlerts, useFetch } from "@keycloak/keycloak-ui-shared"; +import { + KeycloakSpinner, + useAlerts, + useFetch, +} from "@keycloak/keycloak-ui-shared"; import { AlertVariant, ButtonVariant, @@ -35,10 +39,8 @@ import { arrayToKeyValue, keyValueToArray, } from "../components/key-value-form/key-value-convert"; -import { KeycloakSpinner } from "@keycloak/keycloak-ui-shared"; import { PermissionsTab } from "../components/permission-tab/PermissionTab"; import { RoleForm } from "../components/role-form/RoleForm"; -import { AddRoleMappingModal } from "../components/role-mapping/AddRoleMappingModal"; import { RoleMapping } from "../components/role-mapping/RoleMapping"; import { RoutableTabs, @@ -47,12 +49,12 @@ import { import { ViewHeader } from "../components/view-header/ViewHeader"; import { useAccess } from "../context/access/Access"; import { useRealm } from "../context/realm-context/RealmContext"; +import { AdminEvents } from "../events/AdminEvents"; import useIsFeatureEnabled, { Feature } from "../utils/useIsFeatureEnabled"; import { useParams } from "../utils/useParams"; import { UsersInRoleTab } from "./UsersInRoleTab"; import { RealmRoleRoute, RealmRoleTab, toRealmRole } from "./routes/RealmRole"; import { toRealmRoles } from "./routes/RealmRoles"; -import { AdminEvents } from "../events/AdminEvents"; export default function RealmRoleTabs() { const { adminClient } = useAdminClient(); @@ -82,7 +84,6 @@ export default function RealmRoleTabs() { const [canManageClientRole, setCanManageClientRole] = useState(false); - const [open, setOpen] = useState(false); const convert = (role: RoleRepresentation) => { const { attributes, ...rest } = role; return { @@ -246,15 +247,6 @@ export default function RealmRoleTabs() { return ( <> - {open && ( - addComposites(rows.map((r) => r.role))} - onClose={() => setOpen(false)} - /> - )} { const scopeName = `client-scope-mapper-${uuidv4()}`; @@ -47,8 +47,7 @@ test.describe("Scope tab test", () => { test("Assign and unassign role", async ({ page }) => { const role = "admin"; - await assignRole(page); - await changeRoleTypeFilter(page, "roles"); + await pickRoleType(page, "roles"); await pickRole(page, role, true); await confirmModalAssign(page); diff --git a/js/apps/admin-ui/test/client-scope/scope.ts b/js/apps/admin-ui/test/client-scope/scope.ts index 1bc56bb6a3c..302f6a758fa 100644 --- a/js/apps/admin-ui/test/client-scope/scope.ts +++ b/js/apps/admin-ui/test/client-scope/scope.ts @@ -3,7 +3,3 @@ import { Page } from "@playwright/test"; export async function goToScopeTab(page: Page) { await page.getByTestId("scopeTab").click(); } - -export async function assignRole(page: Page) { - await page.getByTestId("no-roles-for-this-client-scope-empty-action").click(); -} diff --git a/js/apps/admin-ui/test/clients/role.spec.ts b/js/apps/admin-ui/test/clients/role.spec.ts index 80f07c50cd8..287697b3c65 100644 --- a/js/apps/admin-ui/test/clients/role.spec.ts +++ b/js/apps/admin-ui/test/clients/role.spec.ts @@ -170,7 +170,6 @@ test.describe("Roles tab test", () => { await goToAssociatedRolesTab(page); // Add associated realm role - await page.getByTestId("no-roles-in-this-realm-empty-action").click(); await addAssociatedRoles(page, createRealmRoleName); await assertNotificationMessage(page, "Associated roles have been added"); @@ -188,8 +187,7 @@ test.describe("Roles tab test", () => { await goToAssociatedRolesTab(page); // Add associated client roles - await page.getByTestId("no-roles-in-this-realm-empty-action").click(); - await addAssociatedRoles(page, "accountmanage-account", "client"); + await addAssociatedRoles(page, "manage-account", "client"); await assertNotificationMessage(page, "Associated roles have been added"); }); diff --git a/js/apps/admin-ui/test/clients/role.ts b/js/apps/admin-ui/test/clients/role.ts index e162aa11807..f81e87b9a94 100644 --- a/js/apps/admin-ui/test/clients/role.ts +++ b/js/apps/admin-ui/test/clients/role.ts @@ -1,6 +1,6 @@ import { expect, Page } from "@playwright/test"; import { - changeRoleTypeFilter, + pickRoleType, confirmModalAssign, pickRole, RoleType, @@ -45,7 +45,7 @@ export async function addAssociatedRoles( roleName: string, roleType: RoleType = "roles", ) { - await changeRoleTypeFilter(page, roleType); + await pickRoleType(page, roleType); await pickRole(page, roleName, true); await confirmModalAssign(page); } diff --git a/js/apps/admin-ui/test/groups/role.spec.ts b/js/apps/admin-ui/test/groups/role.spec.ts index 62342ae062c..1151d82c5de 100644 --- a/js/apps/admin-ui/test/groups/role.spec.ts +++ b/js/apps/admin-ui/test/groups/role.spec.ts @@ -3,8 +3,9 @@ import { v4 as uuid } from "uuid"; import adminClient from "../utils/AdminClient"; import { login } from "../utils/login"; import { assertNotificationMessage } from "../utils/masthead"; +import { confirmModal } from "../utils/modal"; import { - changeRoleTypeFilter, + pickRoleType, clickHideInheritedRoles, clickUnassign, confirmModalAssign, @@ -16,8 +17,7 @@ import { assertRowExists, clickTableRowItem, } from "../utils/table"; -import { assignRole, goToRoleMappingTab } from "./role"; -import { confirmModal } from "../utils/modal"; +import { goToRoleMappingTab } from "./role"; test.describe("Role mappings", () => { const predefinedGroup = "group1"; @@ -53,8 +53,7 @@ test.describe("Role mappings", () => { }); test("Assign roles from empty state", async ({ page }) => { - await assignRole(page); - await changeRoleTypeFilter(page, "roles"); + await pickRoleType(page, "roles"); await pickRole(page, "default-roles-master", true); await confirmModalAssign(page); diff --git a/js/apps/admin-ui/test/groups/role.ts b/js/apps/admin-ui/test/groups/role.ts index 0082922f10e..b2422c033ee 100644 --- a/js/apps/admin-ui/test/groups/role.ts +++ b/js/apps/admin-ui/test/groups/role.ts @@ -3,7 +3,3 @@ import { Page } from "@playwright/test"; export async function goToRoleMappingTab(page: Page) { await page.getByTestId("role-mapping-tab").click(); } - -export async function assignRole(page: Page) { - await page.getByTestId("no-roles-for-this-group-empty-action").click(); -} diff --git a/js/apps/admin-ui/test/realm-roles/main.spec.ts b/js/apps/admin-ui/test/realm-roles/main.spec.ts index e8932e6ac2e..6f395598fce 100644 --- a/js/apps/admin-ui/test/realm-roles/main.spec.ts +++ b/js/apps/admin-ui/test/realm-roles/main.spec.ts @@ -19,7 +19,7 @@ import { login } from "../utils/login"; import { assertNotificationMessage } from "../utils/masthead"; import { confirmModal } from "../utils/modal"; import { - changeRoleTypeFilter, + pickRoleType, clickUnassign, confirmModalAssign, pickRole, @@ -35,8 +35,6 @@ import { } from "../utils/table"; import { assertUnassignDisabled, - assignRole, - clickAddRoleButton, clickCreateRoleButton, goToAssociatedRolesTab, } from "./main"; @@ -132,25 +130,22 @@ test.describe("Realm roles test", () => { await goToAssociatedRolesTab(page); // Add associated realm role from search bar - await clickAddRoleButton(page, true); - await changeRoleTypeFilter(page, "roles"); + await pickRoleType(page, "roles"); await pickRole(page, "offline_access", true); await confirmModalAssign(page); await assertNotificationMessage(page, "Associated roles have been added"); // Add associated client role from search bar - await clickAddRoleButton(page); - await pickRole(page, "accountmanage-account", true); + await pickRoleType(page, "client"); + await pickRole(page, "manage-account", true); await confirmModalAssign(page); await assertNotificationMessage(page, "Associated roles have been added"); // Add associated client role - await clickAddRoleButton(page); - await pickRole(page, "accountmanage-consent", true); + await pickRoleType(page, "client"); + await pickRole(page, "manage-consent", true); await confirmModalAssign(page); await assertNotificationMessage(page, "Associated roles have been added"); - - await clickAddRoleButton(page); }); test("should search existing associated role by name and go to it", async ({ @@ -207,8 +202,8 @@ test.describe("Realm roles test", () => { await clickTableRowItem(page, itemId); await goToAssociatedRolesTab(page); - await assignRole(page); - await pickRole(page, "accountview-profile", true); + await pickRoleType(page, "client"); + await pickRole(page, "view-profile", true); await confirmModalAssign(page); await assertUnassignDisabled(page); @@ -222,8 +217,8 @@ test.describe("Realm roles test", () => { await clickTableRowItem(page, itemId); await goToAssociatedRolesTab(page); - await assignRole(page); - await pickRole(page, "accountview-profile", true); + await pickRoleType(page, "client"); + await pickRole(page, "view-profile", true); await confirmModalAssign(page); await clickRowKebabItem(page, "account view-profile", "Unassign"); @@ -239,8 +234,8 @@ test.describe("Realm roles test", () => { await clickTableRowItem(page, itemId); await goToAssociatedRolesTab(page); - await assignRole(page); - await pickRole(page, "accountview-profile", true); + await pickRoleType(page, "client"); + await pickRole(page, "view-profile", true); await confirmModalAssign(page); await page.locator('input[name="check-all"]').check(); diff --git a/js/apps/admin-ui/test/realm-roles/main.ts b/js/apps/admin-ui/test/realm-roles/main.ts index 20370afbacb..4f30bc40e1c 100644 --- a/js/apps/admin-ui/test/realm-roles/main.ts +++ b/js/apps/admin-ui/test/realm-roles/main.ts @@ -8,15 +8,6 @@ export async function goToAssociatedRolesTab(page: Page) { await page.getByTestId("associatedRolesTab").click(); } -export async function clickAddRoleButton(page: Page, empty: boolean = false) { - const id = empty ? "no-roles-in-this-realm-empty-action" : "assignRole"; - await page.getByTestId(id).click(); -} - -export async function assignRole(page: Page) { - await page.getByTestId("no-roles-in-this-realm-empty-action").click(); -} - export async function assertUnassignDisabled(page: Page) { await expect(page.getByTestId("unAssignRole")).toBeDisabled(); } diff --git a/js/apps/admin-ui/test/realm-settings/accessibility.spec.ts b/js/apps/admin-ui/test/realm-settings/accessibility.spec.ts index 76b4d82835d..4c31ae5d2df 100644 --- a/js/apps/admin-ui/test/realm-settings/accessibility.spec.ts +++ b/js/apps/admin-ui/test/realm-settings/accessibility.spec.ts @@ -3,15 +3,16 @@ import { v4 as uuid } from "uuid"; import adminClient from "../utils/AdminClient"; import { login } from "../utils/login"; import { assertAxeViolations } from "../utils/masthead"; +import { pickRoleType } from "../utils/roles"; import { goToRealm, goToRealmSettings } from "../utils/sidebar"; -import { goToRealmEventsTab } from "./events"; -import { goToAddProviders, goToKeys } from "./keys"; -import { goToLoginTab } from "./login"; -import { goToLocalizationTab, goToRealmOverridesSubTab } from "./localization"; import { goToClientPoliciesList, goToClientPoliciesTab, } from "./client-policies"; +import { goToRealmEventsTab } from "./events"; +import { goToAddProviders, goToKeys } from "./keys"; +import { goToLocalizationTab, goToRealmOverridesSubTab } from "./localization"; +import { goToLoginTab } from "./login"; test.describe("Accessibility tests for realm settings", () => { const realmName = `realm-settings-accessibility-${uuid()}`; @@ -186,7 +187,7 @@ test.describe("Accessibility tests for realm settings", () => { page, }) => { await page.getByTestId("rs-userRegistration-tab").click(); - await page.getByTestId("assignRole").click(); + await pickRoleType(page, "client"); await assertAxeViolations(page); }); }); diff --git a/js/apps/admin-ui/test/realm-settings/user-registration.spec.ts b/js/apps/admin-ui/test/realm-settings/user-registration.spec.ts index 33748e4fab7..874c6443609 100644 --- a/js/apps/admin-ui/test/realm-settings/user-registration.spec.ts +++ b/js/apps/admin-ui/test/realm-settings/user-registration.spec.ts @@ -4,15 +4,10 @@ import adminClient from "../utils/AdminClient"; import { login } from "../utils/login"; import { assertNotificationMessage } from "../utils/masthead"; import { confirmModal } from "../utils/modal"; -import { - changeRoleTypeFilter, - confirmModalAssign, - pickRole, -} from "../utils/roles"; +import { pickRoleType, confirmModalAssign, pickRole } from "../utils/roles"; import { goToRealm, goToRealmSettings } from "../utils/sidebar"; import { assertRowExists, clickRowKebabItem, searchItem } from "../utils/table"; import { - clickAssignRole, goToDefaultGroupTab, goToUserRegistrationTab, } from "./user-registration"; @@ -41,8 +36,7 @@ test.describe("Realm settings - User registration tab", () => { test(`Add / remove ${roleName} role`, async ({ page }) => { const roleType = "roles"; - await clickAssignRole(page); - await changeRoleTypeFilter(page, roleType); + await pickRoleType(page, roleType); await pickRole(page, roleName, true); await confirmModalAssign(page); await assertNotificationMessage(page, "Associated roles have been added"); diff --git a/js/apps/admin-ui/test/realm-settings/user-registration.ts b/js/apps/admin-ui/test/realm-settings/user-registration.ts index 3f63901727c..a5b9daeaadc 100644 --- a/js/apps/admin-ui/test/realm-settings/user-registration.ts +++ b/js/apps/admin-ui/test/realm-settings/user-registration.ts @@ -4,10 +4,6 @@ export async function goToUserRegistrationTab(page: Page) { await page.getByTestId("rs-userRegistration-tab").click(); } -export async function clickAssignRole(page: Page) { - await page.getByTestId("assignRole").click(); -} - export async function goToDefaultGroupTab(page: Page) { await page.getByTestId("default-groups-tab").click(); } diff --git a/js/apps/admin-ui/test/user-federation/ldap-mapper.ts b/js/apps/admin-ui/test/user-federation/ldap-mapper.ts index a611e2be70b..04dee99ae76 100644 --- a/js/apps/admin-ui/test/user-federation/ldap-mapper.ts +++ b/js/apps/admin-ui/test/user-federation/ldap-mapper.ts @@ -1,10 +1,6 @@ import ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation"; import { Page } from "@playwright/test"; -import { - changeRoleTypeFilter, - confirmModalAssign, - pickRole, -} from "../utils/roles"; +import { pickRoleType, confirmModalAssign, pickRole } from "../utils/roles"; export async function goToMapperTab(page: Page) { await page.getByTestId("ldap-mappers-tab").click(); @@ -43,8 +39,7 @@ export async function fillHardwareAttributeMapper( .fill(data.config?.["ldap.attribute.value"][0] || ""); if (data.config?.role) { - await page.getByTestId("add-roles").click(); - await changeRoleTypeFilter(page, "roles"); + await pickRoleType(page, "roles"); await pickRole(page, data.config.role[0], true); await confirmModalAssign(page); } diff --git a/js/apps/admin-ui/test/utils/roles.ts b/js/apps/admin-ui/test/utils/roles.ts index 9cc059261c4..9ec266ac7cb 100644 --- a/js/apps/admin-ui/test/utils/roles.ts +++ b/js/apps/admin-ui/test/utils/roles.ts @@ -4,22 +4,16 @@ import { clickSelectRow } from "./table"; export type RoleType = "client" | "roles"; const rolePickTableName = "Role list"; -export async function changeRoleTypeFilter(page: Page, roleType: RoleType) { - const currentFilter = await page - .getByTestId("filter-type-dropdown") - .innerText(); - if (currentFilter.includes(roleType)) { - return; - } +export async function pickRoleType(page: Page, roleType: RoleType) { + await page.getByTestId("add-role-mapping-button").click(); let filter; if (roleType === "client") { - filter = "Filter by clients"; + filter = "Client roles"; } else { - filter = "Filter by realm roles"; + filter = "Realm roles"; } - await page.getByTestId("filter-type-dropdown").click(); await page.getByRole("menuitem", { name: filter, exact: true }).click(); } diff --git a/js/libs/ui-shared/src/controls/table/ListEmptyState.tsx b/js/libs/ui-shared/src/controls/table/ListEmptyState.tsx index 7319bd62529..1074980ba36 100644 --- a/js/libs/ui-shared/src/controls/table/ListEmptyState.tsx +++ b/js/libs/ui-shared/src/controls/table/ListEmptyState.tsx @@ -1,4 +1,9 @@ -import { ComponentClass, MouseEventHandler, ReactNode } from "react"; +import { + ComponentClass, + MouseEventHandler, + PropsWithChildren, + ReactNode, +} from "react"; import { EmptyState, EmptyStateIcon, @@ -40,7 +45,8 @@ export const ListEmptyState = ({ secondaryActions, icon, isDisabled = false, -}: ListEmptyStateProps) => { + children, +}: PropsWithChildren) => { return ( {hasIcon && isSearchVariant ? ( @@ -63,6 +69,7 @@ export const ListEmptyState = ({ {primaryActionText} )} + {children} {secondaryActions && ( {secondaryActions.map((action) => (