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:
parent
ba4e576a2d
commit
820af6abca
3 changed files with 87 additions and 28 deletions
|
@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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}`}>
|
||||||
|
|
|
@ -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}
|
||||||
/>
|
/>
|
||||||
|
|
Loading…
Reference in a new issue