feat: add team groups section in group configurations (#1728)
This commit is contained in:
committed by
GitHub
parent
c4a09a2b43
commit
e8cd7c2dcc
@@ -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}>
|
||||
@@ -61,6 +62,7 @@ describe('<GroupConfigurations />', () => {
|
||||
).toBeInTheDocument();
|
||||
expect(getByText(contentGroups.name)).toBeInTheDocument();
|
||||
expect(getByText(enrollmentTrackGroups.name)).toBeInTheDocument();
|
||||
expect(getByText(teamGroups.name)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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: [
|
||||
{
|
||||
|
||||
@@ -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
src/group-configurations/__mocks__/teamGroupsMock.ts
Normal file
35
src/group-configurations/__mocks__/teamGroupsMock.ts
Normal file
@@ -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;
|
||||
@@ -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';
|
||||
@@ -59,9 +60,12 @@ const GroupConfigurations = () => {
|
||||
}
|
||||
|
||||
const enrollmentTrackGroup = shouldShowEnrollmentTrack
|
||||
? allGroupConfigurations[0]
|
||||
? allGroupConfigurations.find((group) => group.scheme === 'enrollment_track')
|
||||
: null;
|
||||
const contentGroup = allGroupConfigurations?.[shouldShowEnrollmentTrack ? 1 : 0];
|
||||
|
||||
const contentGroup = allGroupConfigurations.find((group) => group.scheme === 'cohort');
|
||||
|
||||
const teamGroups = allGroupConfigurations.filter((group) => group.scheme === 'team');
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -83,6 +87,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}
|
||||
|
||||
@@ -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
src/group-configurations/team-groups-section/index.tsx
Normal file
25
src/group-configurations/team-groups-section/index.tsx
Normal file
@@ -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
src/group-configurations/types.ts
Normal file
27
src/group-configurations/types.ts
Normal file
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user