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;