Skip to content

Commit 4fb18b6

Browse files
committed
feat: add roles tab
1 parent e9a86ff commit 4fb18b6

3 files changed

Lines changed: 81 additions & 18 deletions

File tree

src/authz-module/components/RoleCard/PermissionsRow.tsx

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,25 @@ interface PermissionRowProps {
1717

1818
const PermissionRow = ({ resourceLabel, actions }: PermissionRowProps) => (
1919
<Row className="row align-items-center border px-2 py-2">
20-
<Col md={3}>
20+
<Col md={2}>
2121
<span className="small font-weight-bold">{resourceLabel}</span>
2222
</Col>
2323
<Col>
24-
<div className="w-100 d-flex flex-wrap">
25-
{actions.map(action => (
26-
<Chip
27-
key={action.key}
28-
iconBefore={actionsDictionary[action.key as ActionKey] as ComponentType}
29-
disabled={action.disabled}
30-
className="mr-4 my-2 px-3 bg-primary-100 border-0 permission-chip"
31-
variant="light"
32-
>
33-
{action.label}
34-
</Chip>
24+
<div className="w-100 d-flex flex-wrap align-items-center">
25+
{actions.map((action, index) => (
26+
<>
27+
<Chip
28+
key={action.key}
29+
iconBefore={actionsDictionary[action.key as ActionKey] as ComponentType}
30+
disabled={action.disabled}
31+
className="mx-3 my-2 px-3 bg-primary-100 border-0 permission-chip"
32+
variant="light"
33+
>
34+
{action.label}
35+
</Chip>
36+
{(index === actions.length - 1) ? null
37+
: (<hr className="border-right mx-2" style={{ height: '24px' }} />)}
38+
</>
3539
))}
3640
</div>
3741
</Col>

src/authz-module/libraries-manager/LibrariesTeamManager.test.tsx

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { screen } from '@testing-library/react';
1+
import { fireEvent, screen } from '@testing-library/react';
22
import { renderWrapper } from '@src/setupTest';
33
import { initializeMockApp } from '@edx/frontend-platform/testing';
44
import { useLibrary } from '@src/authz-module/data/hooks';
@@ -24,6 +24,17 @@ jest.mock('./components/TeamTable', () => ({
2424
default: () => <div data-testid="team-table">MockTeamTable</div>,
2525
}));
2626

27+
jest.mock('../components/RoleCard', () => ({
28+
__esModule: true,
29+
default: ({ title, description, permissions }: { title: string, description: string, permissions: any[] }) => (
30+
<div data-testid="role-card">
31+
<div>{title}</div>
32+
<div>{description}</div>
33+
<div>{permissions.length} permissions</div>
34+
</div>
35+
),
36+
}));
37+
2738
describe('LibrariesTeamManager', () => {
2839
beforeEach(() => {
2940
initializeMockApp({
@@ -36,8 +47,19 @@ describe('LibrariesTeamManager', () => {
3647
libraryName: 'Mock Library',
3748
libraryOrg: 'MockOrg',
3849
username: 'mockuser',
39-
roles: ['admin'],
40-
permissions: [],
50+
roles: [
51+
{
52+
name: 'Instructor',
53+
description: 'Can manage content.',
54+
userCount: 3,
55+
permissions: ['view', 'edit'],
56+
},
57+
],
58+
permissions: [
59+
{ key: 'view_library', label: 'view', resource: 'library' },
60+
{ key: 'edit_library', name: 'edit', resource: 'library' },
61+
],
62+
resources: [{ key: 'library', displayName: 'Library' }],
4163
canManageTeam: true,
4264
});
4365

@@ -64,4 +86,19 @@ describe('LibrariesTeamManager', () => {
6486
// TeamTable is rendered
6587
expect(screen.getByTestId('team-table')).toBeInTheDocument();
6688
});
89+
90+
it('renders role cards when "Roles" tab is selected', async () => {
91+
renderWrapper(<LibrariesTeamManager />);
92+
93+
// Click on "Roles" tab
94+
const rolesTab = await screen.findByRole('tab', { name: /roles/i });
95+
fireEvent.click(rolesTab);
96+
97+
const roleCards = await screen.findAllByTestId('role-card');
98+
99+
expect(roleCards.length).toBeGreaterThan(0);
100+
expect(screen.getByText('Instructor')).toBeInTheDocument();
101+
expect(screen.getByText(/Can manage content/i)).toBeInTheDocument();
102+
expect(screen.getByText(/1 permissions/i)).toBeInTheDocument();
103+
});
67104
});

src/authz-module/libraries-manager/LibrariesTeamManager.tsx

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,30 @@
1+
import { useMemo } from 'react';
12
import { useIntl } from '@edx/frontend-platform/i18n';
2-
import { Tab, Tabs } from '@openedx/paragon';
3+
import { Container, Tab, Tabs } from '@openedx/paragon';
34
import { useLibrary } from '@src/authz-module/data/hooks';
45
import TeamTable from './components/TeamTable';
56
import AuthZLayout from '../components/AuthZLayout';
7+
import RoleCard from '../components/RoleCard';
68
import { useLibraryAuthZ } from './context';
9+
import { buildPermissionsByRoleMatrix } from './utils';
710

811
import messages from './messages';
912

1013
const LibrariesTeamManager = () => {
1114
const intl = useIntl();
12-
const { libraryId } = useLibraryAuthZ();
15+
const {
16+
libraryId, roles, permissions, resources,
17+
} = useLibraryAuthZ();
1318
const { data: library } = useLibrary(libraryId);
1419
const rootBradecrumb = intl.formatMessage(messages['library.authz.breadcrumb.root']) || '';
1520
const pageTitle = intl.formatMessage(messages['library.authz.manage.page.title']);
21+
const libraryRoles = useMemo(() => roles.map(role => ({
22+
...role,
23+
permissions: buildPermissionsByRoleMatrix({
24+
rolePermissions: role.permissions, permissions, resources, intl,
25+
}),
26+
})), [roles, permissions, resources, intl]);
27+
1628
return (
1729
<div className="authz-libraries">
1830
<AuthZLayout
@@ -32,7 +44,17 @@ const LibrariesTeamManager = () => {
3244
<TeamTable />
3345
</Tab>
3446
<Tab eventKey="roles" title={intl.formatMessage(messages['library.authz.tabs.roles'])}>
35-
Role tab.
47+
<Container className="p-5">
48+
{libraryRoles && libraryRoles.map(role => (
49+
<RoleCard
50+
key={`${role}-description`}
51+
title={role.name}
52+
userCounter={role.userCount}
53+
description={role.description}
54+
permissions={role.permissions as any[]}
55+
/>
56+
))}
57+
</Container>
3658
</Tab>
3759
<Tab eventKey="permissions" title={intl.formatMessage(messages['library.authz.tabs.permissions'])}>
3860
Permissions tab.

0 commit comments

Comments
 (0)