Added attributes tab to users (#1383)
This commit is contained in:
parent
d3c20812f0
commit
3fa13259e0
8 changed files with 127 additions and 24 deletions
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
22
cypress/support/pages/admin_console/manage/AttributesTab.ts
Normal file
22
cypress/support/pages/admin_console/manage/AttributesTab.ts
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
71
src/user/UserAttributes.tsx
Normal file
71
src/user/UserAttributes.tsx
Normal 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>
|
||||
);
|
||||
};
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue