Skip to content
2 changes: 2 additions & 0 deletions src/group-configurations/GroupConfigurations.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ let store;
const courseId = 'course-v1:org+101+101';
const enrollmentTrackGroups = groupConfigurationResponseMock.allGroupConfigurations[0];
const contentGroups = groupConfigurationResponseMock.allGroupConfigurations[1];
const teamGroups = groupConfigurationResponseMock.allGroupConfigurations[2];

const renderComponent = () => render(
<CourseAuthoringProvider courseId={courseId}>
Expand Down Expand Up @@ -61,6 +62,7 @@ describe('<GroupConfigurations />', () => {
).toBeInTheDocument();
expect(getByText(contentGroups.name)).toBeInTheDocument();
expect(getByText(enrollmentTrackGroups.name)).toBeInTheDocument();
expect(getByText(teamGroups.name)).toBeInTheDocument();
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ module.exports = {
usage: null,
name: 'Enrollment Track Groups',
parameters: {
course_id: 'course-v1:org+101+101',
courseId: 'course-v1:org+101+101',
},
read_only: true,
readOnly: true,
scheme: 'enrollment_track',
version: 3,
},
Expand Down Expand Up @@ -68,6 +68,39 @@ module.exports = {
scheme: 'cohort',
version: 3,
},
{
active: true,
description: 'Partition for segmenting users by team-set',
groups: [
{
id: 6,
name: 'My Team 1',
usage: [],
version: 1,
},
{
id: 7,
name: 'My Team 2',
usage: [
{
label: 'Subsection / Unit',
url: '/container/block-v1:org+101+101+type@vertical+block@e960cb847be24b8c835ae1a0184d7831',
},
],
version: 1,
},
],
id: 92768376,
usage: null,
name: 'Team Group: My Group',
parameters: {
courseId: 'course-v1:org+101+101',
teamSetId: '0ec11208-335f-4b48-9475-136f02cc30f3"',
},
readOnly: true,
scheme: 'team',
version: 3,
},
],
experimentGroupConfigurations: [
{
Expand Down
1 change: 1 addition & 0 deletions src/group-configurations/__mocks__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { default as contentGroupsMock } from './contentGroupsMock';
export { default as enrollmentTrackGroupsMock } from './enrollmentTrackGroupsMock';
export { default as experimentGroupConfigurationsMock } from './experimentGroupConfigurationsMock';
export { default as groupConfigurationResponseMock } from './groupConfigurationResponseMock';
export { default as teamGroupsMock } from './teamGroupsMock';
35 changes: 35 additions & 0 deletions src/group-configurations/__mocks__/teamGroupsMock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { AvailableGroup } from '@src/group-configurations/types';

const teamGroupsMock: AvailableGroup = {
active: true,
description: 'Partition for segmenting users by team-set',
groups: [
{
id: 6,
name: 'My Team 1',
usage: [],
version: 1,
},
{
id: 7,
name: 'My Team 2',
usage: [
{
label: 'Subsection / Unit',
url: '/container/block-v1:org+101+101+type@vertical+block@e960cb847be24b8c835ae1a0184d7831',
},
],
version: 1,
},
],
id: 92768376,
name: 'Team Group: My Group',
parameters: {
courseId: 'course-v1:org+101+101',
},
readOnly: true,
scheme: 'team',
version: 3,
};

export default teamGroupsMock;
9 changes: 9 additions & 0 deletions src/group-configurations/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { SavingErrorAlert } from '../generic/saving-error-alert';
import messages from './messages';
import ContentGroupsSection from './content-groups-section';
import ExperimentConfigurationsSection from './experiment-configurations-section';
import TeamGroupsSection from './team-groups-section';
import EnrollmentTrackGroupsSection from './enrollment-track-groups-section';
import GroupConfigurationSidebar from './group-configuration-sidebar';
import { useGroupConfigurations } from './hooks';
Expand Down Expand Up @@ -62,6 +63,7 @@ const GroupConfigurations = () => {
? allGroupConfigurations[0]
: null;
const contentGroup = allGroupConfigurations?.[shouldShowEnrollmentTrack ? 1 : 0];
const teamGroups = allGroupConfigurations.filter((group) => group.scheme === 'team');

return (
<>
Expand All @@ -83,6 +85,13 @@ const GroupConfigurations = () => {
gap={3}
data-testid="group-configurations-main-content-wrapper"
>
{!!teamGroups && teamGroups.length > 0 && (
teamGroups.map((teamGroup) => (
<TeamGroupsSection
availableGroup={teamGroup}
/>
))
)}
{!!enrollmentTrackGroup && (
<EnrollmentTrackGroupsSection
availableGroup={enrollmentTrackGroup}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React from 'react';

import { initializeMocks, render } from '@src/testUtils';
import { teamGroupsMock } from '@src/group-configurations/__mocks__';
import TeamGroupsSection from '.';

const renderComponent = (
props: Partial<React.ComponentProps<typeof TeamGroupsSection>> = {},
) => {
initializeMocks();
return render(
<TeamGroupsSection
availableGroup={teamGroupsMock}
{...props}
/>,
);
};

describe('<TeamGroupsSection />', () => {
it('renders component correctly', () => {
const { getByText, getAllByTestId } = renderComponent();

expect(getByText(teamGroupsMock.name)).toBeInTheDocument();
expect(getAllByTestId('content-group-card')).toHaveLength(
teamGroupsMock.groups.length,
);
});

it('renders the team group name as a heading', () => {
const { getByRole } = renderComponent();

const heading = getByRole('heading', { name: teamGroupsMock.name });
expect(heading).toBeInTheDocument();
expect(heading).toHaveClass('configuration-section-name');
});

it('renders all team groups from the availableGroup prop', () => {
const { getByText } = renderComponent();

teamGroupsMock.groups.forEach((group) => {
expect(getByText(group.name)).toBeInTheDocument();
});
});

it('renders content group cards as read-only', () => {
const { getAllByTestId } = renderComponent();

const cards = getAllByTestId('content-group-card');
expect(cards).toHaveLength(teamGroupsMock.groups.length);

// Verify that no edit or delete buttons are present (read-only mode)
const editButtons = document.querySelectorAll('[data-testid="content-group-card-header-edit"]');
const deleteButtons = document.querySelectorAll('[data-testid="content-group-card-header-delete"]');

expect(editButtons).toHaveLength(0);
expect(deleteButtons).toHaveLength(0);
});

it('renders with custom availableGroup prop', () => {
const customGroup = {
...teamGroupsMock,
name: 'Custom Team Group',
groups: [
{
id: 100,
name: 'Custom Team 1',
usage: [],
version: 1,
},
],
};

const { getByText, getAllByTestId } = renderComponent({
availableGroup: customGroup,
});

expect(getByText('Custom Team Group')).toBeInTheDocument();
expect(getByText('Custom Team 1')).toBeInTheDocument();
expect(getAllByTestId('content-group-card')).toHaveLength(1);
});
});
25 changes: 25 additions & 0 deletions src/group-configurations/team-groups-section/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';

import { AvailableGroup } from '@src/group-configurations/types';
import ContentGroupCard from '@src/group-configurations/content-groups-section/ContentGroupCard';

interface TeamGroupsSectionProps {
availableGroup: AvailableGroup;
}

const TeamGroupsSection: React.FC<TeamGroupsSectionProps> = ({
availableGroup: { groups, name },
}) => (
<div className="mt-2.5">
<h2 className="lead text-black mb-3 configuration-section-name">{name}</h2>
{groups.map((group) => (
<ContentGroupCard
group={group}
key={group.id}
readOnly
/>
))}
</div>
);

export default TeamGroupsSection;
27 changes: 27 additions & 0 deletions src/group-configurations/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export interface Usage {
label: string;
url: string;
}

export interface Group {
id: number;
name: string;
usage: Usage[];
version: number;
}

export interface AvailableGroupParameters {
courseId: string;
}

export interface AvailableGroup {
active?: boolean;
description?: string;
groups: Group[];
id: number;
name: string;
parameters?: AvailableGroupParameters;
readOnly?: boolean;
scheme: string;
version: number;
}