feat: [MICROBA-1688] Add cohorts to recipients (#16)

This commit is contained in:
Thomas Tracy
2022-02-03 13:48:52 -05:00
committed by GitHub
parent 7d2fa72768
commit a168c90ca2
6 changed files with 69 additions and 11 deletions

View File

@@ -6,7 +6,7 @@ import { Spinner } from '@edx/paragon';
import { ErrorPage } from '@edx/frontend-platform/react';
import BulkEmailTaskManager from './bulk-email-task-manager/BulkEmailTaskManager';
import Navigationtabs from '../navigation-tabs/NavigationTabs';
import { getCourseHomeCourseMetadata } from './data/api';
import { getCohorts, getCourseHomeCourseMetadata } from './data/api';
import useMobileResponsive from '../../utils/useMobileResponsive';
import BulkEmailForm from './bulk-email-form';
@@ -18,20 +18,25 @@ export default function BulkEmailTool() {
useEffect(() => {
async function fetchTabData() {
let data;
let metadataResponse;
let cohortsResponse;
try {
data = await getCourseHomeCourseMetadata(courseId);
metadataResponse = await getCourseHomeCourseMetadata(courseId);
cohortsResponse = await getCohorts(courseId);
} catch (e) {
setCourseMetadata({
isStaff: false,
tabs: [],
cohorts: [],
});
return;
}
const { tabs, is_staff: isStaff } = data;
const { tabs, is_staff: isStaff } = metadataResponse;
const { cohorts } = cohortsResponse;
setCourseMetadata({
isStaff,
tabs: [...tabs],
cohorts: cohorts.map(({ name }) => name),
});
}
fetchTabData();
@@ -43,7 +48,7 @@ export default function BulkEmailTool() {
<Navigationtabs courseId={courseId} tabData={courseMetadata.tabs} />
<div className={classnames({ 'border border-primary-200': !isMobile })}>
<div className="row">
<BulkEmailForm courseId={courseId} />
<BulkEmailForm courseId={courseId} cohorts={courseMetadata.cohorts} />
</div>
<div className="row">
<BulkEmailTaskManager courseId={courseId} />

View File

@@ -20,7 +20,7 @@ export const FORM_SUBMIT_STATES = {
};
export default function BulkEmailForm(props) {
const { courseId } = props;
const { courseId, cohorts } = props;
const [subject, setSubject] = useState('');
const [emailFormStatus, setEmailFormStatus] = useState(FORM_SUBMIT_STATES.DEFAULT);
const [emailFormValidation, setEmailFormValidation] = useState({
@@ -126,6 +126,7 @@ export default function BulkEmailForm(props) {
<BulkEmailRecipient
selectedGroups={selectedRecipients}
handleCheckboxes={onRecipientChange}
additionalCohorts={cohorts}
isValid={emailFormValidation.recipients}
/>
<Form.Group controlId="emailSubject">
@@ -220,6 +221,11 @@ export default function BulkEmailForm(props) {
);
}
BulkEmailForm.defaultProps = {
cohorts: [],
};
BulkEmailForm.propTypes = {
courseId: PropTypes.string.isRequired,
cohorts: PropTypes.arrayOf(PropTypes.string),
};

View File

@@ -12,7 +12,7 @@ const DEFAULT_GROUPS = {
};
export default function BulkEmailRecipient(props) {
const { handleCheckboxes, selectedGroups } = props;
const { handleCheckboxes, selectedGroups, additionalCohorts } = props;
return (
<Form.Group>
<Form.Label>
@@ -22,8 +22,13 @@ export default function BulkEmailRecipient(props) {
description="A label before the list of potential recipients"
/>
</Form.Label>
<Form.CheckboxSet name="recipientGroups" onChange={handleCheckboxes} value={selectedGroups}>
<Form.Checkbox key="myself" value="myself">
<Form.CheckboxSet
name="recipientGroups"
className="w-75 flex-wrap flex-row justify-content-between"
onChange={handleCheckboxes}
value={selectedGroups}
>
<Form.Checkbox key="myself" value="myself" className="mt-2.5">
<FormattedMessage
id="bulk.email.form.recipients.myself"
defaultMessage="Myself"
@@ -70,6 +75,23 @@ export default function BulkEmailRecipient(props) {
description="A selectable choice from a list of potential email recipients"
/>
</Form.Checkbox>
{
// additional cohorts
additionalCohorts
&& additionalCohorts.map((cohort) => (
<Form.Checkbox
key={cohort}
value={`cohort:${cohort}`}
disabled={selectedGroups.find((group) => group === (DEFAULT_GROUPS.AUDIT || DEFAULT_GROUPS.VERIFIED))}
>
<FormattedMessage
id="bulk.email.form.cohort.label"
defaultMessage="Cohort: {cohort}"
values={{ cohort }}
/>
</Form.Checkbox>
))
}
</Form.CheckboxSet>
{!props.isValid && (
<Form.Control.Feedback className="px-3" hasIcon type="invalid">
@@ -86,10 +108,12 @@ export default function BulkEmailRecipient(props) {
BulkEmailRecipient.defaultProps = {
isValid: true,
additionalCohorts: [],
};
BulkEmailRecipient.propTypes = {
selectedGroups: PropTypes.arrayOf(PropTypes.string).isRequired,
handleCheckboxes: PropTypes.func.isRequired,
isValid: PropTypes.bool,
additionalCohorts: PropTypes.arrayOf(PropTypes.string),
};

View File

@@ -0,0 +1,9 @@
import { Factory } from 'rosie'; // eslint-disable-line import/no-extraneous-dependencies
export default Factory.define('cohort')
.sequence('id', (i) => i)
.sequence('name', i => `test cohort ${i}`)
.attrs({
assignment_type: 'test',
user_count: 1,
});

View File

@@ -14,3 +14,9 @@ export async function getCourseHomeCourseMetadata(courseId) {
const { data } = await getAuthenticatedHttpClient().get(courseHomeMetadataUrl);
return data;
}
export async function getCohorts(courseId) {
const url = `${getConfig().LMS_BASE_URL}/courses/${courseId}/cohorts/`;
const { data } = await getAuthenticatedHttpClient().get(url);
return data;
}

View File

@@ -3,10 +3,11 @@
*/
import React from 'react';
import { Factory } from 'rosie';
import { render, screen } from '../../../setupTest';
import { render, screen, cleanup } from '../../../setupTest';
import BulkEmailTool from '../BulkEmailTool';
import { getCourseHomeCourseMetadata } from '../data/api';
import { getCourseHomeCourseMetadata, getCohorts } from '../data/api';
import '../data/__factories__/courseMetadata.factory';
import '../data/__factories__/cohort.factory';
jest.mock('../text-editor/TextEditor');
jest.mock('../bulk-email-task-manager/api', () => ({
@@ -16,6 +17,7 @@ jest.mock('../bulk-email-task-manager/api', () => ({
jest.mock('../data/api', () => ({
__esModule: true,
getCourseHomeCourseMetadata: jest.fn(() => {}),
getCohorts: jest.fn(() => {}),
}));
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
@@ -25,15 +27,21 @@ jest.mock('react-router-dom', () => ({
}));
describe('BulkEmailTool', () => {
beforeEach(() => jest.resetModules());
afterEach(cleanup);
test('BulkEmailTool renders properly when given course metadata', async () => {
const courseMetadata = Factory.build('courseMetadata');
const cohorts = { cohorts: [Factory.build('cohort'), Factory.build('cohort')] };
getCourseHomeCourseMetadata.mockImplementation(() => courseMetadata);
getCohorts.mockImplementation(() => cohorts);
render(<BulkEmailTool />);
expect(await screen.findByText('Course')).toBeTruthy();
});
test('BulkEmailTool renders error page on no staff user', async () => {
const courseMetadata = Factory.build('courseMetadata', { is_staff: false });
const cohorts = { cohorts: [Factory.build('cohort'), Factory.build('cohort')] };
getCourseHomeCourseMetadata.mockImplementation(() => courseMetadata);
getCohorts.mockImplementation(() => cohorts);
render(<BulkEmailTool />);
expect(
await screen.findByText('An unexpected error occurred. Please click the button below to refresh the page.'),