diff --git a/src/group-configurations/GroupConfigurations.test.jsx b/src/group-configurations/GroupConfigurations.test.jsx index a6efd4b133..3cc4ace4f9 100644 --- a/src/group-configurations/GroupConfigurations.test.jsx +++ b/src/group-configurations/GroupConfigurations.test.jsx @@ -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( @@ -61,6 +62,7 @@ describe('', () => { ).toBeInTheDocument(); expect(getByText(contentGroups.name)).toBeInTheDocument(); expect(getByText(enrollmentTrackGroups.name)).toBeInTheDocument(); + expect(getByText(teamGroups.name)).toBeInTheDocument(); }); }); diff --git a/src/group-configurations/__mocks__/groupConfigurationResponseMock.js b/src/group-configurations/__mocks__/groupConfigurationResponseMock.js index b7f5540697..b35823be1d 100644 --- a/src/group-configurations/__mocks__/groupConfigurationResponseMock.js +++ b/src/group-configurations/__mocks__/groupConfigurationResponseMock.js @@ -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, }, @@ -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: [ { diff --git a/src/group-configurations/__mocks__/index.ts b/src/group-configurations/__mocks__/index.ts index bb3f889849..7dce333c9c 100644 --- a/src/group-configurations/__mocks__/index.ts +++ b/src/group-configurations/__mocks__/index.ts @@ -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'; diff --git a/src/group-configurations/__mocks__/teamGroupsMock.ts b/src/group-configurations/__mocks__/teamGroupsMock.ts new file mode 100644 index 0000000000..0310b3556b --- /dev/null +++ b/src/group-configurations/__mocks__/teamGroupsMock.ts @@ -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; diff --git a/src/group-configurations/index.jsx b/src/group-configurations/index.jsx index b1e3a859e8..70c763264f 100644 --- a/src/group-configurations/index.jsx +++ b/src/group-configurations/index.jsx @@ -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'; @@ -62,6 +63,7 @@ const GroupConfigurations = () => { ? allGroupConfigurations[0] : null; const contentGroup = allGroupConfigurations?.[shouldShowEnrollmentTrack ? 1 : 0]; + const teamGroups = allGroupConfigurations.filter((group) => group.scheme === 'team'); return ( <> @@ -83,6 +85,13 @@ const GroupConfigurations = () => { gap={3} data-testid="group-configurations-main-content-wrapper" > + {!!teamGroups && teamGroups.length > 0 && ( + teamGroups.map((teamGroup) => ( + + )) + )} {!!enrollmentTrackGroup && ( > = {}, +) => { + initializeMocks(); + return render( + , + ); +}; + +describe('', () => { + 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); + }); +}); diff --git a/src/group-configurations/team-groups-section/index.tsx b/src/group-configurations/team-groups-section/index.tsx new file mode 100644 index 0000000000..9907ba76ca --- /dev/null +++ b/src/group-configurations/team-groups-section/index.tsx @@ -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 = ({ + availableGroup: { groups, name }, +}) => ( +
+

{name}

+ {groups.map((group) => ( + + ))} +
+); + +export default TeamGroupsSection; diff --git a/src/group-configurations/types.ts b/src/group-configurations/types.ts new file mode 100644 index 0000000000..163cba5aa3 --- /dev/null +++ b/src/group-configurations/types.ts @@ -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; +}