Merge pull request #316 from jenny-s51/associated-roles-modal

realm roles: associated roles modal (roles list)
This commit is contained in:
mfrances17 2021-01-28 13:18:59 -05:00 committed by GitHub
commit f406018903
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 172 additions and 13 deletions

View file

@ -79,7 +79,7 @@ export const ConfirmDialogModal = ({
<Button
id="modal-cancel"
key="cancel"
variant={ButtonVariant.secondary}
variant={ButtonVariant.link}
onClick={() => {
if (onCancel) onCancel();
toggleDialog();

View file

@ -31,7 +31,7 @@ exports[`Confirmation dialog renders simple confirm dialog 1`] = `
<Button
id="modal-cancel"
onClick={[Function]}
variant="secondary"
variant="link"
>
cancel
</Button>,
@ -132,8 +132,8 @@ exports[`Confirmation dialog renders simple confirm dialog 1`] = `
</button>
<button
aria-disabled="false"
class="pf-c-button pf-m-secondary"
data-ouia-component-id="OUIA-Generated-Button-secondary-1"
class="pf-c-button pf-m-link"
data-ouia-component-id="OUIA-Generated-Button-link-1"
data-ouia-component-type="PF4/Button"
data-ouia-safe="true"
id="modal-cancel"
@ -161,7 +161,7 @@ exports[`Confirmation dialog renders simple confirm dialog 1`] = `
<Button
id="modal-cancel"
onClick={[Function]}
variant="secondary"
variant="link"
>
cancel
</Button>,
@ -341,13 +341,13 @@ exports[`Confirmation dialog renders simple confirm dialog 1`] = `
id="modal-cancel"
key="cancel"
onClick={[Function]}
variant="secondary"
variant="link"
>
<button
aria-disabled={false}
aria-label={null}
className="pf-c-button pf-m-secondary"
data-ouia-component-id="OUIA-Generated-Button-secondary-1"
className="pf-c-button pf-m-link"
data-ouia-component-id="OUIA-Generated-Button-link-1"
data-ouia-component-type="PF4/Button"
data-ouia-safe={true}
disabled={false}

View file

@ -0,0 +1,135 @@
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { Button, Modal, ModalVariant } from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
import { useForm } from "react-hook-form";
import { useAdminClient } from "../context/auth/AdminClient";
import RoleRepresentation from "keycloak-admin/lib/defs/roleRepresentation";
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
import { boolFormatter } from "../util";
export type AssociatedRolesModalProps = {
open: boolean;
toggleDialog: () => void;
};
const attributesToArray = (attributes: { [key: string]: string }): any => {
if (!attributes || Object.keys(attributes).length == 0) {
return [
{
key: "",
value: "",
},
];
}
return Object.keys(attributes).map((key) => ({
key: key,
value: attributes[key],
}));
};
export const AssociatedRolesModal = (props: AssociatedRolesModalProps) => {
const { t } = useTranslation("roles");
const form = useForm<RoleRepresentation>({ mode: "onChange" });
const [name, setName] = useState("");
const adminClient = useAdminClient();
const [selectedRows, setSelectedRows] = useState<RoleRepresentation[]>([]);
const { id } = useParams<{ id: string }>();
const loader = async () => {
const allRoles = await adminClient.roles.find();
const roles = allRoles.filter((x) => x.name != name);
return roles;
};
useEffect(() => {
(async () => {
if (id) {
const fetchedRole = await adminClient.roles.findOneById({ id });
setName(fetchedRole.name!);
setupForm(fetchedRole);
} else {
setName(t("createRole"));
}
})();
}, []);
const setupForm = (role: RoleRepresentation) => {
Object.entries(role).map((entry) => {
if (entry[0] === "attributes") {
form.setValue(entry[0], attributesToArray(entry[1]));
} else {
form.setValue(entry[0], entry[1]);
}
});
};
return (
<Modal
title={t("roles:associatedRolesModalTitle", { name })}
isOpen={props.open}
onClose={props.toggleDialog}
variant={ModalVariant.large}
actions={[
<Button
key="add"
variant="primary"
isDisabled={selectedRows.length === 0}
onClick={() => {
props.toggleDialog();
}}
>
{t("common:add")}
</Button>,
<Button
key="cancel"
variant="link"
onClick={() => {
props.toggleDialog();
}}
>
{t("common:cancel")}
</Button>,
]}
>
<KeycloakDataTable
key="role-list-modal"
loader={loader}
ariaLabelKey="roles:roleList"
searchPlaceholderKey="roles:searchFor"
canSelectAll
// isPaginated
onSelect={(rows) => {
setSelectedRows([...rows]);
}}
columns={[
{
name: "name",
displayKey: "roles:roleName",
},
{
name: "composite",
displayKey: "roles:composite",
cellFormatters: [boolFormatter()],
},
{
name: "description",
displayKey: "roles:description",
},
]}
emptyState={
<ListEmptyState
hasIcon={true}
message={t("noRolesInThisRealm")}
instructions={t("noRolesInThisRealmInstructions")}
primaryActionText={t("createRole")}
// onPrimaryAction={goToCreate}
/>
}
/>
</Modal>
);
};

View file

@ -19,6 +19,7 @@ import { ViewHeader } from "../components/view-header/ViewHeader";
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
import { RealmRoleForm } from "./RealmRoleForm";
import { useRealm } from "../context/realm-context/RealmContext";
import { AssociatedRolesModal } from "./AssociatedRolesModal";
import { KeycloakTabs } from "../components/keycloak-tabs/KeycloakTabs";
const arrayToAttributes = (attributeArray: KeyValueType[]) => {
@ -57,6 +58,8 @@ export const RealmRoleTabs = () => {
const { addAlert } = useAlerts();
const [open, setOpen] = useState(false);
useEffect(() => {
(async () => {
if (id) {
@ -131,9 +134,12 @@ export const RealmRoleTabs = () => {
},
});
const toggleModal = () => setOpen(!open);
return (
<>
<DeleteConfirm />
<AssociatedRolesModal open={open} toggleDialog={() => setOpen(!open)} />
<ViewHeader
titleKey={name}
subKey={id ? "" : "roles:roleCreateExplain"}
@ -141,12 +147,19 @@ export const RealmRoleTabs = () => {
id
? [
<DropdownItem
key="action"
key="delete-role"
component="button"
onClick={() => toggleDeleteDialog()}
>
{t("deleteRole")}
</DropdownItem>,
<DropdownItem
key="toggle-modal"
component="button"
onClick={() => toggleModal()}
>
{t("addAssociatedRolesText")}
</DropdownItem>,
]
: undefined
}

View file

@ -90,7 +90,7 @@ export const RealmRolesSection = () => {
}
actions={[
{
title: t("common:Delete"),
title: t("common:delete"),
onRowClick: (role) => {
setSelectedRole(role);
toggleDeleteDialog();

View file

@ -3,6 +3,9 @@
"attributes": "Attributes",
"addAttributeText": "Add an attribute",
"deleteAttributeText": "Delete an attribute",
"addAssociatedRolesText": "Add associated roles",
"addAssociatedRolesSuccess": "Associated roles have been added",
"associatedRolesModalTitle": "Add roles to {{name}}",
"title": "Realm roles",
"createRole": "Create role",
"importRole": "Import role",
@ -24,7 +27,7 @@
"roleCreateError": "Could not create role: {{error}}",
"roleImportSuccess": "Role import successful",
"roleDeleteConfirm": "Delete role?",
"roleDeleteConfirmDialog": "This action will permanently delete the role {{selectedRoleName}} and cannot be undone.",
"roleDeleteConfirmDialog": "This action will permanently delete the role {{name}} and cannot be undone.",
"roleDeletedSuccess": "The role has been deleted",
"roleDeleteError": "Could not delete role:",
"roleSaveSuccess": "The role has been saved",

View file

@ -78,6 +78,14 @@ export const emptyFormatter = (): IFormatter => (
return data ? data : "—";
};
export const boolFormatter = (): IFormatter => (data?: IFormatterValueType) => {
const boolVal = data?.toString();
return (boolVal
? boolVal.charAt(0).toUpperCase() + boolVal.slice(1)
: undefined) as string;
};
export const getBaseUrl = (adminClient: KeycloakAdminClient) => {
return adminClient.keycloak
? adminClient.keycloak.authServerUrl!

View file

@ -27,7 +27,7 @@ describe("Realms test", function () {
it("should create Test realm", function () {
sidebarPage.goToCreateRealm();
createRealmPage.fillRealmName("Test").createRealm();
createRealmPage.fillRealmName("Test realm").createRealm();
masthead.checkNotificationMessage("Realm created");
});
@ -35,7 +35,7 @@ describe("Realms test", function () {
it("should change to Test realm", function () {
sidebarPage.getCurrentRealm().should("eq", "Master");
sidebarPage.goToRealm("Test").getCurrentRealm().should("eq", "Test");
sidebarPage.goToRealm("Test realm").getCurrentRealm().should("eq", "Test realm");
});
});
});