Added attributes tab to users (#1383)

This commit is contained in:
alevid99 2021-10-25 16:38:54 +02:00 committed by GitHub
parent d3c20812f0
commit 3fa13259e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 127 additions and 24 deletions

View file

@ -1,6 +1,7 @@
import ListingPage from "../support/pages/admin_console/ListingPage";
import GroupModal from "../support/pages/admin_console/manage/groups/GroupModal";
import GroupDetailPage from "../support/pages/admin_console/manage/groups/GroupDetailPage";
import AttributesTab from "../support/pages/admin_console/manage//AttributesTab";
import MoveGroupModal from "../support/pages/admin_console/manage/groups/MoveGroupModal";
import { SearchGroupPage } from "../support/pages/admin_console/manage/groups/SearchGroup";
import Masthead from "../support/pages/admin_console/Masthead";
@ -21,6 +22,7 @@ describe("Group test", () => {
const searchGroupPage = new SearchGroupPage();
const moveGroupModal = new MoveGroupModal();
const modalUtils = new ModalUtils();
const attributesTab = new AttributesTab();
let groupName = "group";
@ -205,8 +207,8 @@ describe("Group test", () => {
it("Attributes CRUD test", () => {
clickGroup(groups[0]);
detailPage
.clickAttributesTab()
attributesTab
.goToAttributesTab()
.fillAttribute("key", "value")
.saveAttribute();

View file

@ -4,6 +4,7 @@ import CreateUserPage from "../support/pages/admin_console/manage/users/CreateUs
import Masthead from "../support/pages/admin_console/Masthead";
import ListingPage from "../support/pages/admin_console/ListingPage";
import UserDetailsPage from "../support/pages/admin_console/manage/users/UserDetailsPage";
import AttributesTab from "../support/pages/admin_console/manage/AttributesTab";
import ModalUtils from "../support/util/ModalUtils";
import { keycloakBefore } from "../support/util/keycloak_before";
import GroupModal from "../support/pages/admin_console/manage/groups/GroupModal";
@ -53,6 +54,7 @@ describe("Users test", () => {
const modalUtils = new ModalUtils();
const listingPage = new ListingPage();
const userDetailsPage = new UserDetailsPage();
const attributesTab = new AttributesTab();
let itemId = "user_crud";
@ -116,6 +118,19 @@ describe("Users test", () => {
listingPage.searchItem(itemId).itemExist(itemId);
});
it("User attributes test", () => {
listingPage.searchItem(itemId).itemExist(itemId);
listingPage.goToItemDetails(itemId);
attributesTab
.goToAttributesTab()
.fillAttribute("key", "value")
.saveAttribute();
masthead.checkNotificationMessage("The user has been saved");
});
it("Add user to groups test", () => {
// Go to user groups
listingPage.searchItem(itemId).itemExist(itemId);

View file

@ -0,0 +1,22 @@
export default class AttributesTab {
private keyInput = '[name="attributes[0].key"]';
private valueInput = '[name="attributes[0].value"]';
private saveAttributeBtn = "save-attributes";
private attributesTab = "attributes";
goToAttributesTab() {
cy.findByTestId(this.attributesTab).click();
return this;
}
fillAttribute(key: string, value: string) {
cy.get(this.keyInput).type(key).get(this.valueInput).type(value);
return this;
}
saveAttribute() {
cy.findByTestId(this.saveAttributeBtn).click();
return this;
}
}

View file

@ -3,17 +3,12 @@ const expect = chai.expect;
export default class GroupDetailPage {
private groupNamesColumn = '[data-label="Group name"] > a';
private memberTab = "members";
private attributesTab = "attributes";
private memberNameColumn = 'tbody > tr > [data-label="Name"]';
private includeSubGroupsCheck = "includeSubGroupsCheck";
private addMembers = "addMember";
private addMember = "add";
private memberUsernameColumn = 'tbody > tr > [data-label="Username"]';
private keyInput = '[name="attributes[0].key"]';
private valueInput = '[name="attributes[0].value"]';
private saveAttributeBtn = ".pf-c-form__actions > .pf-m-primary";
checkListSubGroup(subGroups: string[]) {
cy.get(this.groupNamesColumn).should((groups) => {
expect(groups).to.have.length(subGroups.length);
@ -70,23 +65,8 @@ export default class GroupDetailPage {
return this;
}
clickAttributesTab() {
cy.findByTestId(this.attributesTab).click();
return this;
}
clickAddMembers() {
cy.findByTestId(this.addMembers).click();
return this;
}
fillAttribute(key: string, value: string) {
cy.get(this.keyInput).type(key).get(this.valueInput).type(value);
return this;
}
saveAttribute() {
cy.get(this.saveAttributeBtn).click();
return this;
}
}

View file

@ -192,7 +192,12 @@ export const AttributesForm = ({
</TableComposable>
{!noSaveCancelButtons && (
<ActionGroup className="kc-attributes__action-group">
<Button variant="primary" type="submit" isDisabled={!watchLast}>
<Button
data-testid="save-attributes"
variant="primary"
type="submit"
isDisabled={!watchLast}
>
{t("common:save")}
</Button>
<Button

View file

@ -0,0 +1,71 @@
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { useFieldArray, useForm } from "react-hook-form";
import {
AlertVariant,
PageSection,
PageSectionVariants,
} from "@patternfly/react-core";
import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation";
import { useAlerts } from "../components/alert/Alerts";
import {
arrayToAttributes,
AttributeForm,
AttributesForm,
attributesToArray,
} from "../components/attribute-form/AttributeForm";
import { useAdminClient } from "../context/auth/AdminClient";
type UserAttributesProps = {
user: UserRepresentation;
};
export const UserAttributes = ({ user }: UserAttributesProps) => {
const { t } = useTranslation("users");
const adminClient = useAdminClient();
const { addAlert, addError } = useAlerts();
const form = useForm<AttributeForm>({ mode: "onChange" });
const { fields, append, remove } = useFieldArray({
control: form.control,
name: "attributes",
});
const convertAttributes = (attr?: Record<string, any>) => {
const attributes = attributesToArray(attr || user.attributes!);
attributes.push({ key: "", value: "" });
return attributes;
};
useEffect(() => {
form.setValue("attributes", convertAttributes());
}, [user]);
const save = async (attributeForm: AttributeForm) => {
try {
const attributes = arrayToAttributes(attributeForm.attributes);
await adminClient.users.update({ id: user.id! }, { ...user, attributes });
form.setValue("attributes", convertAttributes(attributes));
addAlert(t("userSaved"), AlertVariant.success);
} catch (error) {
addError("groups:groupUpdateError", error);
}
};
return (
<PageSection variant={PageSectionVariants.light}>
<AttributesForm
form={form}
save={save}
array={{ fields, append, remove }}
reset={() =>
form.reset({
attributes: convertAttributes(),
})
}
/>
</PageSection>
);
};

View file

@ -26,6 +26,7 @@ import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
import { toUser } from "./routes/User";
import { toUsers } from "./routes/Users";
import { UserRoleMapping } from "./UserRoleMapping";
import { UserAttributes } from "./UserAttributes";
export const UsersTabs = () => {
const { t } = useTranslation("users");
@ -175,6 +176,13 @@ export const UsersTabs = () => {
)}
</PageSection>
</Tab>
<Tab
eventKey="attributes"
data-testid="attributes"
title={<TabTitleText>{t("common:attributes")}</TabTitleText>}
>
<UserAttributes user={user} />
</Tab>
<Tab
eventKey="groups"
data-testid="user-groups-tab"

View file

@ -3,7 +3,7 @@ import { generatePath } from "react-router-dom";
import type { RouteDef } from "../../route-config";
import { UsersTabs } from "../UsersTabs";
export type UserTab = "settings" | "groups" | "consents";
export type UserTab = "settings" | "groups" | "consents" | "attributes";
export type UserParams = {
realm: string;