diff --git a/cypress/integration/users_test.spec.ts b/cypress/integration/users_test.spec.ts index 5f89dc7201..672e964d49 100644 --- a/cypress/integration/users_test.spec.ts +++ b/cypress/integration/users_test.spec.ts @@ -10,6 +10,7 @@ import GroupModal from "../support/pages/admin_console/manage/groups/GroupModal" import UserGroupsPage from "../support/pages/admin_console/manage/users/UserGroupsPage"; let groupName = "group"; +let groupsList: string[] = []; describe("Group creation", () => { const loginPage = new LoginPage(); @@ -24,7 +25,7 @@ describe("Group creation", () => { sidebarPage.goToGroups(); }); - it("Add group to be joined", () => { + it("Add groups to be joined", () => { groupName += "_" + (Math.random() + 1).toString(36).substring(7); groupModal @@ -32,6 +33,21 @@ describe("Group creation", () => { .fillGroupForm(groupName) .clickCreate(); + groupsList = [...groupsList, groupName]; + masthead.checkNotificationMessage("Group created"); + + sidebarPage.goToGroups(); + listingPage.searchItem(groupName, false).itemExist(groupName); + + groupName = "group"; + groupName += "_" + (Math.random() + 1).toString(36).substring(7); + + groupModal + .open("openCreateGroupModal") + .fillGroupForm(groupName) + .clickCreate(); + + groupsList = [...groupsList, groupName]; masthead.checkNotificationMessage("Group created"); sidebarPage.goToGroups(); @@ -101,7 +117,7 @@ describe("Users test", () => { listingPage.searchItem(itemId).itemExist(itemId); }); - it("Add user to group test", function () { + it("Add user to groups test", function () { // Go to user groups listingPage.searchItem(itemId).itemExist(itemId); @@ -109,7 +125,11 @@ describe("Users test", () => { userGroupsPage.goToGroupsTab(); userGroupsPage.toggleAddGroupModal(); - cy.getId(`${groupName}`).click(); + + groupsList.forEach((element) => { + cy.getId(`${element}-check`).click(); + }); + userGroupsPage.joinGroup(); cy.wait(1000); @@ -150,4 +170,4 @@ describe("Users test", () => { listingPage.itemExist(itemId, false); }); }); -}); \ No newline at end of file +}); diff --git a/src/user/JoinGroupDialog.tsx b/src/user/JoinGroupDialog.tsx index 0b65b80166..2802acd5bd 100644 --- a/src/user/JoinGroupDialog.tsx +++ b/src/user/JoinGroupDialog.tsx @@ -7,6 +7,7 @@ import { DataList, DataListAction, DataListCell, + DataListCheck, DataListItem, DataListItemCells, DataListItemRow, @@ -23,14 +24,19 @@ import { asyncStateFetch, useAdminClient } from "../context/auth/AdminClient"; import { AngleRightIcon, SearchIcon } from "@patternfly/react-icons"; import GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation"; import { useErrorHandler } from "react-error-boundary"; -import { useParams } from "react-router-dom"; import _ from "lodash"; +import { useParams } from "react-router-dom"; + export type JoinGroupDialogProps = { open: boolean; toggleDialog: () => void; onClose: () => void; - onConfirm: (newGroup: GroupRepresentation) => void; username: string; + onConfirm: (newGroups: Group[]) => void; +}; + +type Group = GroupRepresentation & { + checked?: boolean; }; export const JoinGroupDialog = ({ @@ -42,11 +48,12 @@ export const JoinGroupDialog = ({ }: JoinGroupDialogProps) => { const { t } = useTranslation("roles"); const adminClient = useAdminClient(); + const [selectedRows, setSelectedRows] = useState([]); const errorHandler = useErrorHandler(); - const [navigation, setNavigation] = useState([]); - const [groups, setGroups] = useState([]); + const [navigation, setNavigation] = useState([]); + const [groups, setGroups] = useState([]); const [filtered, setFiltered] = useState(); const [filter, setFilter] = useState(""); @@ -75,6 +82,9 @@ export const JoinGroupDialog = ({ setNavigation([...navigation, selectedGroup]); } + groups.forEach((group: Group) => { + group.checked = !!selectedRows.find((r) => r.id === group.id); + }); setGroups(groups); }, errorHandler @@ -96,8 +106,9 @@ export const JoinGroupDialog = ({ form="group-form" onClick={() => { toggleDialog(); - onConfirm(navigation[navigation.length - 1]); + onConfirm(selectedRows); }} + isDisabled={selectedRows.length === 0} > {t("users:Join")} , @@ -166,17 +177,43 @@ export const JoinGroupDialog = ({ setGroupId(value)} + onSelectDataListItem={(value) => { + setGroupId(value); + }} aria-label={t("groups")} isCompact > - {(filtered || groups).map((group) => ( + {(filtered || groups).map((group: Group) => ( { + if ((e.target as HTMLInputElement).type !== "checkbox") { + setGroupId(group.id); + } + }} > + { + group.checked = (e.target as HTMLInputElement).checked; + let newSelectedRows: Group[]; + if (!group.checked) { + newSelectedRows = selectedRows.filter( + (r) => r.id !== group.id + ); + } else if (group.checked) { + newSelectedRows = [...selectedRows, group]; + } + + setSelectedRows(newSelectedRows!); + }} + aria-labelledby="data-list-check" + /> + diff --git a/src/user/UserGroups.tsx b/src/user/UserGroups.tsx index b8434ea858..ce353e47d5 100644 --- a/src/user/UserGroups.tsx +++ b/src/user/UserGroups.tsx @@ -265,23 +265,25 @@ export const UserGroups = () => { } }; - const addGroup = async (group: GroupRepresentation): Promise => { - const newGroup = group; + const addGroups = async (groups: GroupRepresentation[]): Promise => { + const newGroups = groups; - try { - await adminClient.users.addToGroup({ - id: id, - groupId: newGroup.id!, - }); - setList(true); - refresh(); - addAlert(t("users:addedGroupMembership"), AlertVariant.success); - } catch (error) { - addAlert( - t("users:addedGroupMembershipError", { error }), - AlertVariant.danger - ); - } + newGroups.forEach(async (group) => { + try { + await adminClient.users.addToGroup({ + id: id, + groupId: group.id!, + }); + setList(true); + refresh(); + addAlert(t("users:addedGroupMembership"), AlertVariant.success); + } catch (error) { + addAlert( + t("users:addedGroupMembershipError", { error }), + AlertVariant.danger + ); + } + }); }; return ( @@ -292,7 +294,7 @@ export const UserGroups = () => { setOpen(!open)} - onConfirm={addGroup} + onConfirm={addGroups} toggleDialog={() => toggleModal()} username={username} />