From f2bb0d7c2a42949cbc16c4c60e25542a869eea97 Mon Sep 17 00:00:00 2001 From: Ben Warzeski Date: Wed, 5 Apr 2023 10:34:25 -0400 Subject: [PATCH] refactor: interventsions report component --- .../__snapshots__/index.test.jsx.snap | 30 ++++++++++ .../GradesView/InterventionsReport/hooks.js | 19 +++++++ .../InterventionsReport/hooks.test.js | 56 +++++++++++++++++++ .../GradesView/InterventionsReport/index.jsx | 39 +++++++++++++ .../InterventionsReport/index.test.jsx | 42 ++++++++++++++ .../InterventionsReport/messages.js | 21 +++++++ 6 files changed, 207 insertions(+) create mode 100644 src/components/GradesView/InterventionsReport/__snapshots__/index.test.jsx.snap create mode 100644 src/components/GradesView/InterventionsReport/hooks.js create mode 100644 src/components/GradesView/InterventionsReport/hooks.test.js create mode 100644 src/components/GradesView/InterventionsReport/index.jsx create mode 100644 src/components/GradesView/InterventionsReport/index.test.jsx create mode 100644 src/components/GradesView/InterventionsReport/messages.js diff --git a/src/components/GradesView/InterventionsReport/__snapshots__/index.test.jsx.snap b/src/components/GradesView/InterventionsReport/__snapshots__/index.test.jsx.snap new file mode 100644 index 0000000..2e130db --- /dev/null +++ b/src/components/GradesView/InterventionsReport/__snapshots__/index.test.jsx.snap @@ -0,0 +1,30 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`InterventionsReport component output snapshot 1`] = ` +
+

+ Interventions Report +

+
+
+ Need to find students who may be falling behind? Download the interventions report to obtain engagement metrics such as section attempts and visits. +
+ +
+
+`; diff --git a/src/components/GradesView/InterventionsReport/hooks.js b/src/components/GradesView/InterventionsReport/hooks.js new file mode 100644 index 0000000..493e7f3 --- /dev/null +++ b/src/components/GradesView/InterventionsReport/hooks.js @@ -0,0 +1,19 @@ +import { actions, selectors } from 'data/redux/hooks'; + +const useInterventionsReportData = () => { + const interventionExportUrl = selectors.root.useInterventionExportUrl(); + const showBulkManagement = selectors.root.useShowBulkManagement(); + const downloadInterventionReport = actions.grades.useDownloadInterventionReport(); + + const handleClick = () => { + downloadInterventionReport(); + window.location.assign(interventionExportUrl); + }; + + return { + show: showBulkManagement, + handleClick, + }; +}; + +export default useInterventionsReportData; diff --git a/src/components/GradesView/InterventionsReport/hooks.test.js b/src/components/GradesView/InterventionsReport/hooks.test.js new file mode 100644 index 0000000..357b298 --- /dev/null +++ b/src/components/GradesView/InterventionsReport/hooks.test.js @@ -0,0 +1,56 @@ +import { actions, selectors } from 'data/redux/hooks'; + +import useInterventionsReportData from './hooks'; + +jest.mock('data/redux/hooks', () => ({ + actions: { + grades: { + useDownloadInterventionReport: jest.fn(), + }, + }, + selectors: { + root: { + useInterventionExportUrl: jest.fn(), + useShowBulkManagement: jest.fn(), + }, + }, +})); + +const downloadReport = jest.fn(); +actions.grades.useDownloadInterventionReport.mockReturnValue(downloadReport); +selectors.root.useShowBulkManagement.mockReturnValue(true); +const exportUrl = 'test-intervention-export-url'; +selectors.root.useInterventionExportUrl.mockReturnValue(exportUrl); + +let hook; +let oldLocation; +describe('useInterventionsReportData hooks', () => { + beforeEach(() => { + oldLocation = window.location; + delete window.location; + window.location = { assign: jest.fn() }; + hook = useInterventionsReportData(); + }); + afterEach(() => { + window.location = oldLocation; + }); + describe('behavior', () => { + it('initializes hooks', () => { + expect(selectors.root.useInterventionExportUrl).toHaveBeenCalled(); + expect(selectors.root.useShowBulkManagement).toHaveBeenCalled(); + expect(actions.grades.useDownloadInterventionReport).toHaveBeenCalled(); + }); + }); + describe('output', () => { + test('show from showBulkManagement selector', () => { + expect(hook.show).toEqual(true); + }); + describe('handleClick', () => { + it('downloads interventions report and navigates to export url', () => { + hook.handleClick(); + expect(downloadReport).toHaveBeenCalled(); + expect(window.location.assign).toHaveBeenCalledWith(exportUrl); + }); + }); + }); +}); diff --git a/src/components/GradesView/InterventionsReport/index.jsx b/src/components/GradesView/InterventionsReport/index.jsx new file mode 100644 index 0000000..1f8c90c --- /dev/null +++ b/src/components/GradesView/InterventionsReport/index.jsx @@ -0,0 +1,39 @@ +import React from 'react'; + +import { useIntl } from '@edx/frontend-platform/i18n'; + +import NetworkButton from 'components/NetworkButton'; + +import messages from './messages'; +import useInterventionsReportData from './hooks'; + +/** + * + * Provides download buttons for Bulk Management and Intervention reports, only if + * showBulkManagement is set in redus. + */ +export const InterventionsReport = () => { + const { show, handleClick } = useInterventionsReportData(); + const { formatMessage } = useIntl(); + + return show && ( +
+

+ {formatMessage(messages.title)} +

+
+
+ {formatMessage(messages.description)} +
+ +
+
+ ); +}; + +export default InterventionsReport; diff --git a/src/components/GradesView/InterventionsReport/index.test.jsx b/src/components/GradesView/InterventionsReport/index.test.jsx new file mode 100644 index 0000000..0d1c82c --- /dev/null +++ b/src/components/GradesView/InterventionsReport/index.test.jsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { shallow } from 'enzyme'; + +import { useIntl } from '@edx/frontend-platform/i18n'; + +import NetworkButton from 'components/NetworkButton'; + +import messages from './messages'; +import useInterventionsReportData from './hooks'; +import InterventionsReport from '.'; + +jest.mock('components/NetworkButton', () => 'NetworkButton'); +jest.mock('./hooks', () => jest.fn()); + +const hookProps = { show: true, handleClick: jest.fn() }; +useInterventionsReportData.mockReturnValue(hookProps); + +let el; +describe('InterventionsReport component', () => { + beforeEach(() => { + el = shallow(); + }); + describe('behavior', () => { + it('initializes hooks', () => { + expect(useInterventionsReportData).toHaveBeenCalledWith(); + expect(useIntl).toHaveBeenCalledWith(); + }); + }); + describe('output', () => { + it('does now render if show is false', () => { + useInterventionsReportData.mockReturnValueOnce({ ...hookProps, show: false }); + el = shallow(); + expect(el.isEmptyRender()).toEqual(true); + }); + test('snapshot', () => { + expect(el).toMatchSnapshot(); + const btnProps = el.find(NetworkButton).props(); + expect(btnProps.label).toEqual(messages.downloadBtn); + expect(btnProps.onClick).toEqual(hookProps.handleClick); + }); + }); +}); diff --git a/src/components/GradesView/InterventionsReport/messages.js b/src/components/GradesView/InterventionsReport/messages.js new file mode 100644 index 0000000..373805f --- /dev/null +++ b/src/components/GradesView/InterventionsReport/messages.js @@ -0,0 +1,21 @@ +import { defineMessages } from '@edx/frontend-platform/i18n'; + +const messages = defineMessages({ + title: { + id: 'gradebook.GradesView.InterventionsReport.title', + defaultMessage: 'Interventions Report', + description: 'The title for the Intervention report subsection', + }, + description: { + id: 'gradebook.GradesView.InterventionsReport.description', + defaultMessage: 'Need to find students who may be falling behind? Download the interventions report to obtain engagement metrics such as section attempts and visits.', + description: 'The description for the Intervention report subsection', + }, + downloadBtn: { + id: 'gradebook.GradesView.InterventionsReport.downloadBtn', + defaultMessage: 'Download Interventions', + description: 'The labeled button to download the Intervention report from the Grades View', + }, +}); + +export default messages;