From ad87d08d04dca7311e29c8d12e4394895fe8450d Mon Sep 17 00:00:00 2001 From: Thomas Tracy Date: Fri, 11 Feb 2022 14:26:41 -0500 Subject: [PATCH] feat: [MICROBA-1664] Add copy email button (#20) The old tool had a button in the modal used to view past emails that allowed you to copy the content into the editor. This replicates that functionality, but without the ability to fill in the subject field of the email. This was done to keep certain components more seperate. --- .../bulk-email-tool/BulkEmailTool.jsx | 13 ++- .../bulk-email-form/BulkEmailForm.jsx | 7 +- .../test/BulkEmailForm.test.jsx | 10 +- .../BulkEmailContentHistory.jsx | 93 +++++++++---------- .../BulkEmailTaskManager.jsx | 6 +- .../test/BulkEmailContentHistory.test.jsx | 10 +- 6 files changed, 70 insertions(+), 69 deletions(-) diff --git a/src/components/bulk-email-tool/BulkEmailTool.jsx b/src/components/bulk-email-tool/BulkEmailTool.jsx index f8a54a2..481623f 100644 --- a/src/components/bulk-email-tool/BulkEmailTool.jsx +++ b/src/components/bulk-email-tool/BulkEmailTool.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import classnames from 'classnames'; import { useParams } from 'react-router-dom'; @@ -15,6 +15,13 @@ export default function BulkEmailTool() { const [courseMetadata, setCourseMetadata] = useState(); const isMobile = useMobileResponsive(); + const textEditorRef = useRef(); + + const copyTextToEditor = (body) => { + if (textEditorRef?.current) { + textEditorRef.current.setContent(body); + } + }; useEffect(() => { async function fetchTabData() { @@ -48,10 +55,10 @@ export default function BulkEmailTool() {
- +
- +
diff --git a/src/components/bulk-email-tool/bulk-email-form/BulkEmailForm.jsx b/src/components/bulk-email-tool/bulk-email-form/BulkEmailForm.jsx index d49ce22..7bdb483 100644 --- a/src/components/bulk-email-tool/bulk-email-form/BulkEmailForm.jsx +++ b/src/components/bulk-email-tool/bulk-email-form/BulkEmailForm.jsx @@ -1,4 +1,4 @@ -import React, { useRef, useState } from 'react'; +import React, { useState } from 'react'; import PropTypes from 'prop-types'; import { Form, Icon, StatefulButton, useCheckboxSetValues, useToggle, @@ -20,7 +20,7 @@ export const FORM_SUBMIT_STATES = { }; export default function BulkEmailForm(props) { - const { courseId, cohorts } = props; + const { courseId, cohorts, editorRef } = props; const [subject, setSubject] = useState(''); const [emailFormStatus, setEmailFormStatus] = useState(FORM_SUBMIT_STATES.DEFAULT); const [emailFormValidation, setEmailFormValidation] = useState({ @@ -31,7 +31,6 @@ export default function BulkEmailForm(props) { }); const [selectedRecipients, { add, remove }] = useCheckboxSetValues([]); const [isTaskAlertOpen, openTaskAlert, closeTaskAlert] = useToggle(false); - const editorRef = useRef(null); const resetEmailForm = useTimeout(() => { setEmailFormStatus(FORM_SUBMIT_STATES.COMPLETED_DEFAULT); }, 3000); @@ -243,4 +242,6 @@ BulkEmailForm.defaultProps = { BulkEmailForm.propTypes = { courseId: PropTypes.string.isRequired, cohorts: PropTypes.arrayOf(PropTypes.string), + editorRef: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({ current: PropTypes.instanceOf(Element) })]) + .isRequired, }; diff --git a/src/components/bulk-email-tool/bulk-email-form/test/BulkEmailForm.test.jsx b/src/components/bulk-email-tool/bulk-email-form/test/BulkEmailForm.test.jsx index 5ad15d3..7371656 100644 --- a/src/components/bulk-email-tool/bulk-email-form/test/BulkEmailForm.test.jsx +++ b/src/components/bulk-email-tool/bulk-email-form/test/BulkEmailForm.test.jsx @@ -18,17 +18,17 @@ describe('bulk-email-form', () => { beforeEach(() => jest.resetModules()); afterEach(cleanup); test('it renders', () => { - render(); + render(); expect(screen.getByText('Submit')).toBeTruthy(); }); test('it shows a warning when clicking submit', async () => { - render(); + render(); fireEvent.click(screen.getByText('Submit')); const warning = await screen.findByText('CAUTION!', { exact: false }); expect(warning).toBeTruthy(); }); test('Prevent form POST if invalid', async () => { - render(); + render(); fireEvent.click(screen.getByText('Submit')); expect(await screen.findByRole('button', { name: /continue/i })).toBeInTheDocument(); fireEvent.click(screen.getByRole('button', { name: /continue/i })); @@ -36,7 +36,7 @@ describe('bulk-email-form', () => { expect(await screen.findByText('A subject is required')).toBeInTheDocument(); }); test('Shows complete message on completed POST', async () => { - render(); + render(); fireEvent.click(screen.getByRole('checkbox', { name: 'Myself' })); expect(screen.getByRole('checkbox', { name: 'Myself' })).toBeChecked(); fireEvent.change(screen.getByRole('textbox', { name: 'Subject:' }), { target: { value: 'test subject' } }); @@ -51,7 +51,7 @@ describe('bulk-email-form', () => { throw Error('api-response-error'); }); await act(async () => { - render(); + render(); const subjectLine = screen.getByRole('textbox', { name: 'Subject:' }); const recipient = screen.getByRole('checkbox', { name: 'Myself' }); fireEvent.click(recipient); diff --git a/src/components/bulk-email-tool/bulk-email-task-manager/BulkEmailContentHistory.jsx b/src/components/bulk-email-tool/bulk-email-task-manager/BulkEmailContentHistory.jsx index a1afdf4..28b241a 100644 --- a/src/components/bulk-email-tool/bulk-email-task-manager/BulkEmailContentHistory.jsx +++ b/src/components/bulk-email-tool/bulk-email-task-manager/BulkEmailContentHistory.jsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; import { useParams } from 'react-router-dom'; -import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; +import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { Button, Icon, Modal, StatefulButton, @@ -11,14 +11,13 @@ import messages from './messages'; import { getSentEmailHistory } from './data/api'; import BulkEmailTaskManagerTable from './BulkEmailHistoryTable'; -export function BulkEmailContentHistory({ intl }) { +export function BulkEmailContentHistory({ intl, copyTextToEditor }) { const { courseId } = useParams(); const BUTTON_STATE = { DEFAULT: 'default', PENDING: 'pending', COMPLETE: 'complete', }; - const [emailHistoryData, setEmailHistoryData] = useState(); const [errorRetrievingData, setErrorRetrievingData] = useState(false); const [showHistoricalEmailContentTable, setShowHistoricalEmailContentTable] = useState(false); @@ -91,47 +90,42 @@ export function BulkEmailContentHistory({ intl }) { body={(
-

- {intl.formatMessage(messages.modalMessageSubject)} -

-

- {messageContent.subject} -

+

{intl.formatMessage(messages.modalMessageSubject)}

+

{messageContent.subject}

-

- {intl.formatMessage(messages.modalMessageSentBy)} -

-

- {messageContent.requester} -

+

{intl.formatMessage(messages.modalMessageSentBy)}

+

{messageContent.requester}

-

- {intl.formatMessage(messages.modalMessageTimeSent)} -

-

- {messageContent.created} -

+

{intl.formatMessage(messages.modalMessageTimeSent)}

+

{messageContent.created}

-

- {intl.formatMessage(messages.modalMessageSentTo)} -

-

- {messageContent.sent_to} -

+

{intl.formatMessage(messages.modalMessageSentTo)}

+

{messageContent.sent_to}


-

- {intl.formatMessage(messages.modalMessageBody)} -

+

{intl.formatMessage(messages.modalMessageBody)}

+ {/* eslint-disable-next-line react/no-danger */}
)} onClose={() => setIsMessageModalOpen(false)} + buttons={[ + , + ]} />
); @@ -167,35 +161,31 @@ export function BulkEmailContentHistory({ intl }) { const additionalColumns = () => { const tableData = transformDataForTable(); - return ( - [ - { - id: 'view_message', - Header: '', - Cell: ({ row }) => ( - - ), - }, - ] - ); + return [ + { + id: 'view_message', + Header: '', + Cell: ({ row }) => ( + + ), + }, + ]; }; return (
+
{messageContent && renderMessageModal()}
- {messageContent && renderMessageModal()} -
-
-

- {intl.formatMessage(messages.emailHistoryTableSectionButtonHeader)} -

+

{intl.formatMessage(messages.emailHistoryTableSectionButtonHeader)}

{ await fetchSentEmailHistoryData(); }} + onClick={async () => { + await fetchSentEmailHistoryData(); + }} labels={{ default: `${intl.formatMessage(messages.emailHistoryTableSectionButton)}`, pending: `${intl.formatMessage(messages.emailHistoryTableSectionButton)}`, @@ -209,7 +199,7 @@ export function BulkEmailContentHistory({ intl }) { > {intl.formatMessage(messages.emailHistoryTableSectionButton)} - { showHistoricalEmailContentTable && ( + {showHistoricalEmailContentTable && (
@@ -19,7 +20,7 @@ export function BulkEmailTaskManager({ intl }) {

{intl.formatMessage(messages.emailTaskHistoryHeader)}

- +
@@ -30,6 +31,7 @@ export function BulkEmailTaskManager({ intl }) { BulkEmailTaskManager.propTypes = { intl: intlShape.isRequired, + copyTextToEditor: PropTypes.func.isRequired, }; export default injectIntl(BulkEmailTaskManager); diff --git a/src/components/bulk-email-tool/bulk-email-task-manager/test/BulkEmailContentHistory.test.jsx b/src/components/bulk-email-tool/bulk-email-task-manager/test/BulkEmailContentHistory.test.jsx index 1a696d8..80b0ac1 100644 --- a/src/components/bulk-email-tool/bulk-email-task-manager/test/BulkEmailContentHistory.test.jsx +++ b/src/components/bulk-email-tool/bulk-email-task-manager/test/BulkEmailContentHistory.test.jsx @@ -19,7 +19,7 @@ describe('BulkEmailContentHistory component', () => { afterEach(cleanup); test('renders correctly', async () => { - render(); + render(); const tableDescription = await screen.findByText( 'To see the content of previously sent emails, click this button:', ); @@ -33,7 +33,7 @@ describe('BulkEmailContentHistory component', () => { const emailHistoryData = buildEmailContentHistoryData(1); getSentEmailHistory.mockImplementation(() => emailHistoryData); - render(); + render(); const showEmailContentHistoryButton = await screen.findByText('Show Sent Email History'); fireEvent.click(showEmailContentHistoryButton); @@ -68,7 +68,7 @@ describe('BulkEmailContentHistory component', () => { const emailHistoryData = buildEmailContentHistoryData(1); getSentEmailHistory.mockImplementation(() => emailHistoryData); - render(); + render(); const showEmailContentHistoryButton = await screen.findByText('Show Sent Email History'); fireEvent.click(showEmailContentHistoryButton); @@ -101,7 +101,7 @@ describe('BulkEmailContentHistory component', () => { const emailHistoryData = buildEmailContentHistoryData(0); getSentEmailHistory.mockImplementation(() => emailHistoryData); // render the component - render(); + render(); // press the `show sent email history` button to initiate data retrieval const showEmailContentHistoryButton = await screen.findByText('Show Sent Email History'); fireEvent.click(showEmailContentHistoryButton); @@ -117,7 +117,7 @@ describe('BulkEmailContentHistory component', () => { throw new Error(); }); // render the component - render(); + render(); // press the `show sent email history` button to initiate data retrieval const showEmailContentHistoryButton = await screen.findByText('Show Sent Email History'); fireEvent.click(showEmailContentHistoryButton);