Skip to content

Commit f2716f0

Browse files
committed
fix: solve conflicts
2 parents fc0b1bf + 6dd835d commit f2716f0

File tree

13 files changed

+123
-53
lines changed

13 files changed

+123
-53
lines changed

catalog-info.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ metadata:
1313
annotations:
1414
openedx.org/arch-interest-groups: ""
1515
spec:
16-
owner: group:frontend-all
16+
owner: group:committers-frontend
1717
type: "service"
1818
lifecycle: "production"

src/components/bulk-email-tool/BulkEmailTool.jsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ export default function BulkEmailTool() {
3434
</h1>
3535
</div>
3636
<div className="row">
37-
<BuildEmailFormExtensible courseId={courseId} cohorts={courseMetadata.cohorts} />
37+
<BuildEmailFormExtensible
38+
courseId={courseId}
39+
cohorts={courseMetadata.cohorts}
40+
courseModes={courseMetadata.courseModes}
41+
/>
3842
</div>
3943
<div className="row py-5">
4044
<PluggableComponent

src/components/bulk-email-tool/bulk-email-form/BulkEmailForm.jsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,12 @@ const FORM_ACTIONS = {
4949
};
5050

5151
function BulkEmailForm(props) {
52-
const { courseId, cohorts, intl } = props;
52+
const {
53+
courseId,
54+
cohorts,
55+
courseModes,
56+
intl,
57+
} = props;
5358
const [{ editor }, dispatch] = useContext(BulkEmailContext);
5459
const [emailFormStatus, setEmailFormStatus] = useState(FORM_SUBMIT_STATES.DEFAULT);
5560
const [emailFormValidation, setEmailFormValidation] = useState({
@@ -312,10 +317,14 @@ function BulkEmailForm(props) {
312317
handleCheckboxes={onRecipientChange}
313318
additionalCohorts={cohorts}
314319
isValid={emailFormValidation.recipients}
320+
courseModes={courseModes}
315321
/>
316322
<Form.Group controlId="emailSubject">
317323
<Form.Label className="h3 text-primary-500">{intl.formatMessage(messages.bulkEmailSubjectLabel)}</Form.Label>
318-
<Form.Control name="emailSubject" className="w-lg-50" onChange={onFormChange} value={editor.emailSubject} />
324+
<Form.Control name="emailSubject" className="w-lg-50" onChange={onFormChange} value={editor.emailSubject} maxLength={128} />
325+
<Form.Control.Feedback className="px-3" type="default">
326+
{intl.formatMessage(messages.bulkEmailFormSubjectTip)}
327+
</Form.Control.Feedback>
319328
{!emailFormValidation.subject && (
320329
<Form.Control.Feedback className="px-3" hasIcon type="invalid">
321330
{intl.formatMessage(messages.bulkEmailFormSubjectError)}
@@ -424,6 +433,12 @@ BulkEmailForm.propTypes = {
424433
courseId: PropTypes.string.isRequired,
425434
cohorts: PropTypes.arrayOf(PropTypes.string),
426435
intl: intlShape.isRequired,
436+
courseModes: PropTypes.arrayOf(
437+
PropTypes.shape({
438+
slug: PropTypes.string.isRequired,
439+
name: PropTypes.string.isRequired,
440+
}),
441+
).isRequired,
427442
};
428443

429444
export default injectIntl(BulkEmailForm);

src/components/bulk-email-tool/bulk-email-form/bulk-email-recipient/BulkEmailRecipient.jsx

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,13 @@ const DEFAULT_GROUPS = {
1414
};
1515

1616
export default function BulkEmailRecipient(props) {
17-
const { handleCheckboxes, selectedGroups, additionalCohorts } = props;
17+
const {
18+
handleCheckboxes,
19+
selectedGroups,
20+
additionalCohorts,
21+
courseModes,
22+
} = props;
23+
const hasCourseModes = courseModes && courseModes.length > 1;
1824
return (
1925
<Form.Group>
2026
<Form.Label>
@@ -50,18 +56,24 @@ export default function BulkEmailRecipient(props) {
5056
description="A selectable choice from a list of potential email recipients"
5157
/>
5258
</Form.Checkbox>
53-
<Form.Checkbox
54-
key="track:verified"
55-
value="track:verified"
56-
disabled={selectedGroups.find((group) => group === DEFAULT_GROUPS.ALL_LEARNERS)}
57-
className="col col-lg-4 col-sm-6 col-12"
58-
>
59-
<FormattedMessage
60-
id="bulk.email.form.recipients.verified"
61-
defaultMessage="Learners in the verified certificate track"
62-
description="A selectable choice from a list of potential email recipients"
63-
/>
64-
</Form.Checkbox>
59+
{
60+
// additional modes
61+
hasCourseModes
62+
&& courseModes.map((courseMode) => (
63+
<Form.Checkbox
64+
key={`track:${courseMode.slug}`}
65+
value={`track:${courseMode.slug}`}
66+
disabled={selectedGroups.find((group) => group === DEFAULT_GROUPS.ALL_LEARNERS)}
67+
className="col col-lg-4 col-sm-6 col-12"
68+
>
69+
<FormattedMessage
70+
id="bulk.email.form.mode.label"
71+
defaultMessage="Learners in the {courseModeName} Track"
72+
values={{ courseModeName: courseMode.name }}
73+
/>
74+
</Form.Checkbox>
75+
))
76+
}
6577
{
6678
// additional cohorts
6779
additionalCohorts
@@ -80,18 +92,6 @@ export default function BulkEmailRecipient(props) {
8092
</Form.Checkbox>
8193
))
8294
}
83-
<Form.Checkbox
84-
key="track:audit"
85-
value="track:audit"
86-
disabled={selectedGroups.find((group) => group === DEFAULT_GROUPS.ALL_LEARNERS)}
87-
className="col col-lg-4 col-sm-6 col-12"
88-
>
89-
<FormattedMessage
90-
id="bulk.email.form.recipients.audit"
91-
defaultMessage="Learners in the audit track"
92-
description="A selectable choice from a list of potential email recipients"
93-
/>
94-
</Form.Checkbox>
9595
<Form.Checkbox
9696
key="learners"
9797
value="learners"
@@ -127,4 +127,10 @@ BulkEmailRecipient.propTypes = {
127127
handleCheckboxes: PropTypes.func.isRequired,
128128
isValid: PropTypes.bool,
129129
additionalCohorts: PropTypes.arrayOf(PropTypes.string),
130+
courseModes: PropTypes.arrayOf(
131+
PropTypes.shape({
132+
slug: PropTypes.string.isRequired,
133+
name: PropTypes.string.isRequired,
134+
}),
135+
).isRequired,
130136
};
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Factory } from 'rosie'; // eslint-disable-line import/no-extraneous-dependencies
2+
3+
/**
4+
* Generates an array of course mode objects using Rosie Factory.
5+
* @returns {Array<Object>} An array of course mode objects with attributes 'slug' and 'name'.
6+
*/
7+
const courseModeFactory = () => {
8+
const AuditModeFactory = Factory.define('AuditModeFactory')
9+
.attr('slug', 'audit')
10+
.attr('name', 'Audit');
11+
12+
const VerifiedModeFactory = Factory.define('VerifiedModeFactory')
13+
.attr('slug', 'verified')
14+
.attr('name', 'Verified Certificate');
15+
16+
return [
17+
AuditModeFactory.build(),
18+
VerifiedModeFactory.build(),
19+
];
20+
};
21+
22+
export default courseModeFactory;

src/components/bulk-email-tool/bulk-email-form/messages.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ const messages = defineMessages({
4141
defaultMessage: 'Subject',
4242
description: 'Email subject line input label. Meant to have colon or equivilant punctuation.',
4343
},
44+
bulkEmailFormSubjectTip: {
45+
id: 'bulk.email.form.subject.tip',
46+
defaultMessage: '(Maximum 128 characters)',
47+
description: 'Default Subject tip',
48+
},
4449
bulkEmailFormSubjectError: {
4550
id: 'bulk.email.form.subject.error',
4651
defaultMessage: 'A subject is required',

src/components/bulk-email-tool/bulk-email-form/test/BulkEmailForm.test.jsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import * as bulkEmailFormApi from '../data/api';
1212
import { BulkEmailContext, BulkEmailProvider } from '../../bulk-email-context';
1313
import { formatDate } from '../../../../utils/formatDateAndTime';
1414
import cohortFactory from '../data/__factories__/bulkEmailFormCohort.factory';
15+
import courseModeFactory from '../data/__factories__/bulkEmailFormCourseMode.factory';
1516

1617
jest.mock('../../text-editor/TextEditor');
1718
jest.mock('../../../PluggableComponent', () => () => null);
@@ -21,20 +22,25 @@ const dispatchMock = jest.fn();
2122

2223
const tomorrow = new Date();
2324
tomorrow.setDate(new Date().getDate() + 1);
25+
const courseMode = courseModeFactory();
2426

2527
function renderBulkEmailForm() {
2628
const { cohorts } = cohortFactory.build();
2729
return (
2830
<BulkEmailProvider>
29-
<BulkEmailForm courseId="test" cohorts={cohorts} />
31+
<BulkEmailForm
32+
courseId="test"
33+
cohorts={cohorts}
34+
courseModes={courseMode}
35+
/>
3036
</BulkEmailProvider>
3137
);
3238
}
3339

3440
function renderBulkEmailFormContext(value) {
3541
return (
3642
<BulkEmailContext.Provider value={[value, dispatchMock]}>
37-
<BulkEmailForm courseId="test" />
43+
<BulkEmailForm courseId="test" courseMode={courseMode} />
3844
</BulkEmailContext.Provider>
3945
);
4046
}
@@ -97,8 +103,8 @@ describe('bulk-email-form', () => {
97103
test('Checking "All Learners" disables each learner group', async () => {
98104
render(renderBulkEmailForm());
99105
fireEvent.click(screen.getByRole('checkbox', { name: 'All Learners' }));
100-
const verifiedLearners = screen.getByRole('checkbox', { name: 'Learners in the verified certificate track' });
101-
const auditLearners = screen.getByRole('checkbox', { name: 'Learners in the audit track' });
106+
const verifiedLearners = screen.getByRole('checkbox', { name: 'Learners in the Verified Certificate Track' });
107+
const auditLearners = screen.getByRole('checkbox', { name: 'Learners in the Audit Track' });
102108
const { cohorts } = cohortFactory.build();
103109
cohorts.forEach(cohort => expect(screen.getByRole('checkbox', { name: `Cohort: ${cohort}` })).toBeDisabled());
104110
expect(verifiedLearners).toBeDisabled();

src/components/bulk-email-tool/bulk-email-task-manager/BulkEmailContentHistory.jsx

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-disable react/no-unstable-nested-components */
22

3-
import React, { useState } from 'react';
3+
import React, { useMemo, useState } from 'react';
44
import PropTypes from 'prop-types';
55
import { useParams } from 'react-router-dom';
66
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
@@ -51,17 +51,15 @@ function BulkEmailContentHistory({ intl }) {
5151
* up a level (the `subject` field). We also convert the `sent_to` data to be a String rather than an array to fix a
5252
* display bug in the table.
5353
*/
54-
function transformDataForTable() {
55-
let tableData = [];
56-
if (emailHistoryData) {
57-
tableData = emailHistoryData.map((item) => ({
58-
...item,
59-
subject: item.email.subject,
60-
sent_to: item.sent_to.join(', '),
61-
}));
62-
}
63-
return tableData;
64-
}
54+
const transformDataForTable = useMemo(() => {
55+
const tableData = emailHistoryData?.map((item) => ({
56+
...item,
57+
subject: item.email.subject,
58+
sent_to: item.sent_to.join(', '),
59+
created: new Date(item.created).toLocaleString(),
60+
}));
61+
return tableData || [];
62+
}, [emailHistoryData]);
6563

6664
/**
6765
* This function is responsible for setting the current `messageContent` state data. This will be the contents of a
@@ -102,7 +100,7 @@ function BulkEmailContentHistory({ intl }) {
102100
* contents of a previously sent message.
103101
*/
104102
const additionalColumns = () => {
105-
const tableData = transformDataForTable();
103+
const tableData = transformDataForTable;
106104

107105
return [
108106
{
@@ -139,7 +137,7 @@ function BulkEmailContentHistory({ intl }) {
139137
{showHistoricalEmailContentTable ? (
140138
<BulkEmailTaskManagerTable
141139
errorRetrievingData={errorRetrievingData}
142-
tableData={transformDataForTable()}
140+
tableData={transformDataForTable}
143141
tableDescription={intl.formatMessage(messages.emailHistoryTableViewMessageInstructions)}
144142
alertWarningMessage={intl.formatMessage(messages.noEmailData)}
145143
alertErrorMessage={intl.formatMessage(messages.errorFetchingEmailHistoryData)}

src/components/bulk-email-tool/bulk-email-task-manager/BulkEmailTaskHistory.jsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState } from 'react';
1+
import React, { useMemo, useState } from 'react';
22
import { useParams } from 'react-router-dom';
33
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
44

@@ -41,6 +41,14 @@ function BulkEmailTaskHistory({ intl }) {
4141
setShowHistoricalTaskContentTable(true);
4242
}
4343

44+
const transformDataForTable = useMemo(() => {
45+
const tableData = emailTaskHistoryData?.map((item) => ({
46+
...item,
47+
created: new Date(item.created).toLocaleString(),
48+
}));
49+
return tableData || [];
50+
}, [emailTaskHistoryData]);
51+
4452
const tableColumns = [
4553
{
4654
Header: `${intl.formatMessage(messages.taskHistoryTableColumnHeaderTaskType)}`,
@@ -95,7 +103,7 @@ function BulkEmailTaskHistory({ intl }) {
95103
{showHistoricalTaskContentTable ? (
96104
<BulkEmailTaskManagerTable
97105
errorRetrievingData={errorRetrievingData}
98-
tableData={emailTaskHistoryData}
106+
tableData={transformDataForTable}
99107
alertWarningMessage={intl.formatMessage(messages.noTaskHistoryData)}
100108
alertErrorMessage={intl.formatMessage(messages.errorFetchingTaskHistoryData)}
101109
columns={tableColumns}

src/components/bulk-email-tool/bulk-email-task-manager/test/BulkEmailContentHistory.test.jsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ describe('BulkEmailContentHistory component', () => {
6666
// verify table contents
6767
const { emails } = emailHistoryData;
6868
const email = emails[0];
69-
expect(await screen.findByText(email.created)).toBeTruthy();
69+
const createdDate = new Date(email.created).toLocaleString();
70+
expect(await screen.findByText(createdDate)).toBeTruthy();
7071
expect(await screen.findByText(email.number_sent)).toBeTruthy();
7172
expect(await screen.findByText(email.requester)).toBeTruthy();
7273
expect(await screen.findByText(email.sent_to.join(', '))).toBeTruthy();
@@ -103,7 +104,8 @@ describe('BulkEmailContentHistory component', () => {
103104
expect(await screen.findByText('Message:')).toBeTruthy();
104105
expect(await screen.findAllByText(email.email.subject)).toBeTruthy();
105106
expect(await screen.findAllByText(email.requester)).toBeTruthy();
106-
expect(await screen.findAllByText(email.created)).toBeTruthy();
107+
const createdDate = new Date(email.created).toLocaleString();
108+
expect(await screen.findAllByText(createdDate)).toBeTruthy();
107109
expect(await screen.findAllByText(email.sent_to.join(', '))).toBeTruthy();
108110
// .replace() call strips the HTML tags from the string
109111
expect(await screen.findByText(email.email.html_message.replace(/<[^>]*>?/gm, ''))).toBeTruthy();

0 commit comments

Comments
 (0)