Users(groups): functionality for adding multiple users to groups (#534)

* add user to groups modal wip

* add logic for selecting multiple groups, API does not support this yet

* cfixcheckbox select bug

* add functionality to select and add multiple groups

* function name

* update cypress test
This commit is contained in:
Eugenia 2021-04-19 15:53:28 -04:00 committed by GitHub
parent ba4e576a2d
commit 820af6abca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 87 additions and 28 deletions

View file

@ -10,6 +10,7 @@ import GroupModal from "../support/pages/admin_console/manage/groups/GroupModal"
import UserGroupsPage from "../support/pages/admin_console/manage/users/UserGroupsPage"; import UserGroupsPage from "../support/pages/admin_console/manage/users/UserGroupsPage";
let groupName = "group"; let groupName = "group";
let groupsList: string[] = [];
describe("Group creation", () => { describe("Group creation", () => {
const loginPage = new LoginPage(); const loginPage = new LoginPage();
@ -24,7 +25,7 @@ describe("Group creation", () => {
sidebarPage.goToGroups(); sidebarPage.goToGroups();
}); });
it("Add group to be joined", () => { it("Add groups to be joined", () => {
groupName += "_" + (Math.random() + 1).toString(36).substring(7); groupName += "_" + (Math.random() + 1).toString(36).substring(7);
groupModal groupModal
@ -32,6 +33,21 @@ describe("Group creation", () => {
.fillGroupForm(groupName) .fillGroupForm(groupName)
.clickCreate(); .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"); masthead.checkNotificationMessage("Group created");
sidebarPage.goToGroups(); sidebarPage.goToGroups();
@ -101,7 +117,7 @@ describe("Users test", () => {
listingPage.searchItem(itemId).itemExist(itemId); listingPage.searchItem(itemId).itemExist(itemId);
}); });
it("Add user to group test", function () { it("Add user to groups test", function () {
// Go to user groups // Go to user groups
listingPage.searchItem(itemId).itemExist(itemId); listingPage.searchItem(itemId).itemExist(itemId);
@ -109,7 +125,11 @@ describe("Users test", () => {
userGroupsPage.goToGroupsTab(); userGroupsPage.goToGroupsTab();
userGroupsPage.toggleAddGroupModal(); userGroupsPage.toggleAddGroupModal();
cy.getId(`${groupName}`).click();
groupsList.forEach((element) => {
cy.getId(`${element}-check`).click();
});
userGroupsPage.joinGroup(); userGroupsPage.joinGroup();
cy.wait(1000); cy.wait(1000);
@ -150,4 +170,4 @@ describe("Users test", () => {
listingPage.itemExist(itemId, false); listingPage.itemExist(itemId, false);
}); });
}); });
}); });

View file

@ -7,6 +7,7 @@ import {
DataList, DataList,
DataListAction, DataListAction,
DataListCell, DataListCell,
DataListCheck,
DataListItem, DataListItem,
DataListItemCells, DataListItemCells,
DataListItemRow, DataListItemRow,
@ -23,14 +24,19 @@ import { asyncStateFetch, useAdminClient } from "../context/auth/AdminClient";
import { AngleRightIcon, SearchIcon } from "@patternfly/react-icons"; import { AngleRightIcon, SearchIcon } from "@patternfly/react-icons";
import GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation"; import GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation";
import { useErrorHandler } from "react-error-boundary"; import { useErrorHandler } from "react-error-boundary";
import { useParams } from "react-router-dom";
import _ from "lodash"; import _ from "lodash";
import { useParams } from "react-router-dom";
export type JoinGroupDialogProps = { export type JoinGroupDialogProps = {
open: boolean; open: boolean;
toggleDialog: () => void; toggleDialog: () => void;
onClose: () => void; onClose: () => void;
onConfirm: (newGroup: GroupRepresentation) => void;
username: string; username: string;
onConfirm: (newGroups: Group[]) => void;
};
type Group = GroupRepresentation & {
checked?: boolean;
}; };
export const JoinGroupDialog = ({ export const JoinGroupDialog = ({
@ -42,11 +48,12 @@ export const JoinGroupDialog = ({
}: JoinGroupDialogProps) => { }: JoinGroupDialogProps) => {
const { t } = useTranslation("roles"); const { t } = useTranslation("roles");
const adminClient = useAdminClient(); const adminClient = useAdminClient();
const [selectedRows, setSelectedRows] = useState<Group[]>([]);
const errorHandler = useErrorHandler(); const errorHandler = useErrorHandler();
const [navigation, setNavigation] = useState<GroupRepresentation[]>([]); const [navigation, setNavigation] = useState<Group[]>([]);
const [groups, setGroups] = useState<GroupRepresentation[]>([]); const [groups, setGroups] = useState<Group[]>([]);
const [filtered, setFiltered] = useState<GroupRepresentation[]>(); const [filtered, setFiltered] = useState<GroupRepresentation[]>();
const [filter, setFilter] = useState(""); const [filter, setFilter] = useState("");
@ -75,6 +82,9 @@ export const JoinGroupDialog = ({
setNavigation([...navigation, selectedGroup]); setNavigation([...navigation, selectedGroup]);
} }
groups.forEach((group: Group) => {
group.checked = !!selectedRows.find((r) => r.id === group.id);
});
setGroups(groups); setGroups(groups);
}, },
errorHandler errorHandler
@ -96,8 +106,9 @@ export const JoinGroupDialog = ({
form="group-form" form="group-form"
onClick={() => { onClick={() => {
toggleDialog(); toggleDialog();
onConfirm(navigation[navigation.length - 1]); onConfirm(selectedRows);
}} }}
isDisabled={selectedRows.length === 0}
> >
{t("users:Join")} {t("users:Join")}
</Button>, </Button>,
@ -166,17 +177,43 @@ export const JoinGroupDialog = ({
</ToolbarContent> </ToolbarContent>
</Toolbar> </Toolbar>
<DataList <DataList
onSelectDataListItem={(value) => setGroupId(value)} onSelectDataListItem={(value) => {
setGroupId(value);
}}
aria-label={t("groups")} aria-label={t("groups")}
isCompact isCompact
> >
{(filtered || groups).map((group) => ( {(filtered || groups).map((group: Group) => (
<DataListItem <DataListItem
aria-labelledby={group.name} aria-labelledby={group.name}
key={group.id} key={group.id}
id={group.id} id={group.id}
onClick={(e) => {
if ((e.target as HTMLInputElement).type !== "checkbox") {
setGroupId(group.id);
}
}}
> >
<DataListItemRow data-testid={group.name}> <DataListItemRow data-testid={group.name}>
<DataListCheck
data-testid={`${group.name}-check`}
isChecked={group.checked}
onChange={(checked, e) => {
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"
/>
<DataListItemCells <DataListItemCells
dataListCells={[ dataListCells={[
<DataListCell key={`name-${group.id}`}> <DataListCell key={`name-${group.id}`}>

View file

@ -265,23 +265,25 @@ export const UserGroups = () => {
} }
}; };
const addGroup = async (group: GroupRepresentation): Promise<void> => { const addGroups = async (groups: GroupRepresentation[]): Promise<void> => {
const newGroup = group; const newGroups = groups;
try { newGroups.forEach(async (group) => {
await adminClient.users.addToGroup({ try {
id: id, await adminClient.users.addToGroup({
groupId: newGroup.id!, id: id,
}); groupId: group.id!,
setList(true); });
refresh(); setList(true);
addAlert(t("users:addedGroupMembership"), AlertVariant.success); refresh();
} catch (error) { addAlert(t("users:addedGroupMembership"), AlertVariant.success);
addAlert( } catch (error) {
t("users:addedGroupMembershipError", { error }), addAlert(
AlertVariant.danger t("users:addedGroupMembershipError", { error }),
); AlertVariant.danger
} );
}
});
}; };
return ( return (
@ -292,7 +294,7 @@ export const UserGroups = () => {
<JoinGroupDialog <JoinGroupDialog
open={open} open={open}
onClose={() => setOpen(!open)} onClose={() => setOpen(!open)}
onConfirm={addGroup} onConfirm={addGroups}
toggleDialog={() => toggleModal()} toggleDialog={() => toggleModal()}
username={username} username={username}
/> />