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 <Button
id="modal-cancel" id="modal-cancel"
key="cancel" key="cancel"
variant={ButtonVariant.secondary} variant={ButtonVariant.link}
onClick={() => { onClick={() => {
if (onCancel) onCancel(); if (onCancel) onCancel();
toggleDialog(); toggleDialog();

View file

@ -31,7 +31,7 @@ exports[`Confirmation dialog renders simple confirm dialog 1`] = `
<Button <Button
id="modal-cancel" id="modal-cancel"
onClick={[Function]} onClick={[Function]}
variant="secondary" variant="link"
> >
cancel cancel
</Button>, </Button>,
@ -132,8 +132,8 @@ exports[`Confirmation dialog renders simple confirm dialog 1`] = `
</button> </button>
<button <button
aria-disabled="false" aria-disabled="false"
class="pf-c-button pf-m-secondary" class="pf-c-button pf-m-link"
data-ouia-component-id="OUIA-Generated-Button-secondary-1" data-ouia-component-id="OUIA-Generated-Button-link-1"
data-ouia-component-type="PF4/Button" data-ouia-component-type="PF4/Button"
data-ouia-safe="true" data-ouia-safe="true"
id="modal-cancel" id="modal-cancel"
@ -161,7 +161,7 @@ exports[`Confirmation dialog renders simple confirm dialog 1`] = `
<Button <Button
id="modal-cancel" id="modal-cancel"
onClick={[Function]} onClick={[Function]}
variant="secondary" variant="link"
> >
cancel cancel
</Button>, </Button>,
@ -341,13 +341,13 @@ exports[`Confirmation dialog renders simple confirm dialog 1`] = `
id="modal-cancel" id="modal-cancel"
key="cancel" key="cancel"
onClick={[Function]} onClick={[Function]}
variant="secondary" variant="link"
> >
<button <button
aria-disabled={false} aria-disabled={false}
aria-label={null} aria-label={null}
className="pf-c-button pf-m-secondary" className="pf-c-button pf-m-link"
data-ouia-component-id="OUIA-Generated-Button-secondary-1" data-ouia-component-id="OUIA-Generated-Button-link-1"
data-ouia-component-type="PF4/Button" data-ouia-component-type="PF4/Button"
data-ouia-safe={true} data-ouia-safe={true}
disabled={false} 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 { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
import { RealmRoleForm } from "./RealmRoleForm"; import { RealmRoleForm } from "./RealmRoleForm";
import { useRealm } from "../context/realm-context/RealmContext"; import { useRealm } from "../context/realm-context/RealmContext";
import { AssociatedRolesModal } from "./AssociatedRolesModal";
import { KeycloakTabs } from "../components/keycloak-tabs/KeycloakTabs"; import { KeycloakTabs } from "../components/keycloak-tabs/KeycloakTabs";
const arrayToAttributes = (attributeArray: KeyValueType[]) => { const arrayToAttributes = (attributeArray: KeyValueType[]) => {
@ -57,6 +58,8 @@ export const RealmRoleTabs = () => {
const { addAlert } = useAlerts(); const { addAlert } = useAlerts();
const [open, setOpen] = useState(false);
useEffect(() => { useEffect(() => {
(async () => { (async () => {
if (id) { if (id) {
@ -131,9 +134,12 @@ export const RealmRoleTabs = () => {
}, },
}); });
const toggleModal = () => setOpen(!open);
return ( return (
<> <>
<DeleteConfirm /> <DeleteConfirm />
<AssociatedRolesModal open={open} toggleDialog={() => setOpen(!open)} />
<ViewHeader <ViewHeader
titleKey={name} titleKey={name}
subKey={id ? "" : "roles:roleCreateExplain"} subKey={id ? "" : "roles:roleCreateExplain"}
@ -141,12 +147,19 @@ export const RealmRoleTabs = () => {
id id
? [ ? [
<DropdownItem <DropdownItem
key="action" key="delete-role"
component="button" component="button"
onClick={() => toggleDeleteDialog()} onClick={() => toggleDeleteDialog()}
> >
{t("deleteRole")} {t("deleteRole")}
</DropdownItem>, </DropdownItem>,
<DropdownItem
key="toggle-modal"
component="button"
onClick={() => toggleModal()}
>
{t("addAssociatedRolesText")}
</DropdownItem>,
] ]
: undefined : undefined
} }

View file

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

View file

@ -3,6 +3,9 @@
"attributes": "Attributes", "attributes": "Attributes",
"addAttributeText": "Add an attribute", "addAttributeText": "Add an attribute",
"deleteAttributeText": "Delete an attribute", "deleteAttributeText": "Delete an attribute",
"addAssociatedRolesText": "Add associated roles",
"addAssociatedRolesSuccess": "Associated roles have been added",
"associatedRolesModalTitle": "Add roles to {{name}}",
"title": "Realm roles", "title": "Realm roles",
"createRole": "Create role", "createRole": "Create role",
"importRole": "Import role", "importRole": "Import role",
@ -24,7 +27,7 @@
"roleCreateError": "Could not create role: {{error}}", "roleCreateError": "Could not create role: {{error}}",
"roleImportSuccess": "Role import successful", "roleImportSuccess": "Role import successful",
"roleDeleteConfirm": "Delete role?", "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", "roleDeletedSuccess": "The role has been deleted",
"roleDeleteError": "Could not delete role:", "roleDeleteError": "Could not delete role:",
"roleSaveSuccess": "The role has been saved", "roleSaveSuccess": "The role has been saved",

View file

@ -78,6 +78,14 @@ export const emptyFormatter = (): IFormatter => (
return data ? data : "—"; 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) => { export const getBaseUrl = (adminClient: KeycloakAdminClient) => {
return adminClient.keycloak return adminClient.keycloak
? adminClient.keycloak.authServerUrl! ? adminClient.keycloak.authServerUrl!

View file

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