Skip to content

Commit e8cd7c2

Browse files
authored
feat: add team groups section in group configurations (#1728)
1 parent c4a09a2 commit e8cd7c2

File tree

8 files changed

+219
-4
lines changed

8 files changed

+219
-4
lines changed

src/group-configurations/GroupConfigurations.test.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ let store;
2020
const courseId = 'course-v1:org+101+101';
2121
const enrollmentTrackGroups = groupConfigurationResponseMock.allGroupConfigurations[0];
2222
const contentGroups = groupConfigurationResponseMock.allGroupConfigurations[1];
23+
const teamGroups = groupConfigurationResponseMock.allGroupConfigurations[2];
2324

2425
const renderComponent = () => render(
2526
<CourseAuthoringProvider courseId={courseId}>
@@ -61,6 +62,7 @@ describe('<GroupConfigurations />', () => {
6162
).toBeInTheDocument();
6263
expect(getByText(contentGroups.name)).toBeInTheDocument();
6364
expect(getByText(enrollmentTrackGroups.name)).toBeInTheDocument();
65+
expect(getByText(teamGroups.name)).toBeInTheDocument();
6466
});
6567
});
6668

src/group-configurations/__mocks__/groupConfigurationResponseMock.js

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ module.exports = {
2626
usage: null,
2727
name: 'Enrollment Track Groups',
2828
parameters: {
29-
course_id: 'course-v1:org+101+101',
29+
courseId: 'course-v1:org+101+101',
3030
},
31-
read_only: true,
31+
readOnly: true,
3232
scheme: 'enrollment_track',
3333
version: 3,
3434
},
@@ -68,6 +68,39 @@ module.exports = {
6868
scheme: 'cohort',
6969
version: 3,
7070
},
71+
{
72+
active: true,
73+
description: 'Partition for segmenting users by team-set',
74+
groups: [
75+
{
76+
id: 6,
77+
name: 'My Team 1',
78+
usage: [],
79+
version: 1,
80+
},
81+
{
82+
id: 7,
83+
name: 'My Team 2',
84+
usage: [
85+
{
86+
label: 'Subsection / Unit',
87+
url: '/container/block-v1:org+101+101+type@vertical+block@e960cb847be24b8c835ae1a0184d7831',
88+
},
89+
],
90+
version: 1,
91+
},
92+
],
93+
id: 92768376,
94+
usage: null,
95+
name: 'Team Group: My Group',
96+
parameters: {
97+
courseId: 'course-v1:org+101+101',
98+
teamSetId: '0ec11208-335f-4b48-9475-136f02cc30f3"',
99+
},
100+
readOnly: true,
101+
scheme: 'team',
102+
version: 3,
103+
},
71104
],
72105
experimentGroupConfigurations: [
73106
{

src/group-configurations/__mocks__/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export { default as contentGroupsMock } from './contentGroupsMock';
22
export { default as enrollmentTrackGroupsMock } from './enrollmentTrackGroupsMock';
33
export { default as experimentGroupConfigurationsMock } from './experimentGroupConfigurationsMock';
44
export { default as groupConfigurationResponseMock } from './groupConfigurationResponseMock';
5+
export { default as teamGroupsMock } from './teamGroupsMock';
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { AvailableGroup } from '@src/group-configurations/types';
2+
3+
const teamGroupsMock: AvailableGroup = {
4+
active: true,
5+
description: 'Partition for segmenting users by team-set',
6+
groups: [
7+
{
8+
id: 6,
9+
name: 'My Team 1',
10+
usage: [],
11+
version: 1,
12+
},
13+
{
14+
id: 7,
15+
name: 'My Team 2',
16+
usage: [
17+
{
18+
label: 'Subsection / Unit',
19+
url: '/container/block-v1:org+101+101+type@vertical+block@e960cb847be24b8c835ae1a0184d7831',
20+
},
21+
],
22+
version: 1,
23+
},
24+
],
25+
id: 92768376,
26+
name: 'Team Group: My Group',
27+
parameters: {
28+
courseId: 'course-v1:org+101+101',
29+
},
30+
readOnly: true,
31+
scheme: 'team',
32+
version: 3,
33+
};
34+
35+
export default teamGroupsMock;

src/group-configurations/index.jsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { SavingErrorAlert } from '../generic/saving-error-alert';
1212
import messages from './messages';
1313
import ContentGroupsSection from './content-groups-section';
1414
import ExperimentConfigurationsSection from './experiment-configurations-section';
15+
import TeamGroupsSection from './team-groups-section';
1516
import EnrollmentTrackGroupsSection from './enrollment-track-groups-section';
1617
import GroupConfigurationSidebar from './group-configuration-sidebar';
1718
import { useGroupConfigurations } from './hooks';
@@ -59,9 +60,12 @@ const GroupConfigurations = () => {
5960
}
6061

6162
const enrollmentTrackGroup = shouldShowEnrollmentTrack
62-
? allGroupConfigurations[0]
63+
? allGroupConfigurations.find((group) => group.scheme === 'enrollment_track')
6364
: null;
64-
const contentGroup = allGroupConfigurations?.[shouldShowEnrollmentTrack ? 1 : 0];
65+
66+
const contentGroup = allGroupConfigurations.find((group) => group.scheme === 'cohort');
67+
68+
const teamGroups = allGroupConfigurations.filter((group) => group.scheme === 'team');
6569

6670
return (
6771
<>
@@ -83,6 +87,13 @@ const GroupConfigurations = () => {
8387
gap={3}
8488
data-testid="group-configurations-main-content-wrapper"
8589
>
90+
{!!teamGroups && teamGroups.length > 0 && (
91+
teamGroups.map((teamGroup) => (
92+
<TeamGroupsSection
93+
availableGroup={teamGroup}
94+
/>
95+
))
96+
)}
8697
{!!enrollmentTrackGroup && (
8798
<EnrollmentTrackGroupsSection
8899
availableGroup={enrollmentTrackGroup}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import React from 'react';
2+
3+
import { initializeMocks, render } from '@src/testUtils';
4+
import { teamGroupsMock } from '@src/group-configurations/__mocks__';
5+
import TeamGroupsSection from '.';
6+
7+
const renderComponent = (
8+
props: Partial<React.ComponentProps<typeof TeamGroupsSection>> = {},
9+
) => {
10+
initializeMocks();
11+
return render(
12+
<TeamGroupsSection
13+
availableGroup={teamGroupsMock}
14+
{...props}
15+
/>,
16+
);
17+
};
18+
19+
describe('<TeamGroupsSection />', () => {
20+
it('renders component correctly', () => {
21+
const { getByText, getAllByTestId } = renderComponent();
22+
23+
expect(getByText(teamGroupsMock.name)).toBeInTheDocument();
24+
expect(getAllByTestId('content-group-card')).toHaveLength(
25+
teamGroupsMock.groups.length,
26+
);
27+
});
28+
29+
it('renders the team group name as a heading', () => {
30+
const { getByRole } = renderComponent();
31+
32+
const heading = getByRole('heading', { name: teamGroupsMock.name });
33+
expect(heading).toBeInTheDocument();
34+
expect(heading).toHaveClass('configuration-section-name');
35+
});
36+
37+
it('renders all team groups from the availableGroup prop', () => {
38+
const { getByText } = renderComponent();
39+
40+
teamGroupsMock.groups.forEach((group) => {
41+
expect(getByText(group.name)).toBeInTheDocument();
42+
});
43+
});
44+
45+
it('renders content group cards as read-only', () => {
46+
const { getAllByTestId } = renderComponent();
47+
48+
const cards = getAllByTestId('content-group-card');
49+
expect(cards).toHaveLength(teamGroupsMock.groups.length);
50+
51+
// Verify that no edit or delete buttons are present (read-only mode)
52+
const editButtons = document.querySelectorAll('[data-testid="content-group-card-header-edit"]');
53+
const deleteButtons = document.querySelectorAll('[data-testid="content-group-card-header-delete"]');
54+
55+
expect(editButtons).toHaveLength(0);
56+
expect(deleteButtons).toHaveLength(0);
57+
});
58+
59+
it('renders with custom availableGroup prop', () => {
60+
const customGroup = {
61+
...teamGroupsMock,
62+
name: 'Custom Team Group',
63+
groups: [
64+
{
65+
id: 100,
66+
name: 'Custom Team 1',
67+
usage: [],
68+
version: 1,
69+
},
70+
],
71+
};
72+
73+
const { getByText, getAllByTestId } = renderComponent({
74+
availableGroup: customGroup,
75+
});
76+
77+
expect(getByText('Custom Team Group')).toBeInTheDocument();
78+
expect(getByText('Custom Team 1')).toBeInTheDocument();
79+
expect(getAllByTestId('content-group-card')).toHaveLength(1);
80+
});
81+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React from 'react';
2+
3+
import { AvailableGroup } from '@src/group-configurations/types';
4+
import ContentGroupCard from '@src/group-configurations/content-groups-section/ContentGroupCard';
5+
6+
interface TeamGroupsSectionProps {
7+
availableGroup: AvailableGroup;
8+
}
9+
10+
const TeamGroupsSection: React.FC<TeamGroupsSectionProps> = ({
11+
availableGroup: { groups, name },
12+
}) => (
13+
<div className="mt-2.5">
14+
<h2 className="lead text-black mb-3 configuration-section-name">{name}</h2>
15+
{groups.map((group) => (
16+
<ContentGroupCard
17+
group={group}
18+
key={group.id}
19+
readOnly
20+
/>
21+
))}
22+
</div>
23+
);
24+
25+
export default TeamGroupsSection;

src/group-configurations/types.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
export interface Usage {
2+
label: string;
3+
url: string;
4+
}
5+
6+
export interface Group {
7+
id: number;
8+
name: string;
9+
usage: Usage[];
10+
version: number;
11+
}
12+
13+
export interface AvailableGroupParameters {
14+
courseId: string;
15+
}
16+
17+
export interface AvailableGroup {
18+
active?: boolean;
19+
description?: string;
20+
groups: Group[];
21+
id: number;
22+
name: string;
23+
parameters?: AvailableGroupParameters;
24+
readOnly?: boolean;
25+
scheme: string;
26+
version: number;
27+
}

0 commit comments

Comments
 (0)