diff --git a/src/components/GradesView/BulkManagementControls.jsx b/src/components/GradesView/BulkManagementControls.jsx
deleted file mode 100644
index d7fcbc9..0000000
--- a/src/components/GradesView/BulkManagementControls.jsx
+++ /dev/null
@@ -1,71 +0,0 @@
-/* eslint-disable react/sort-comp, react/button-has-type */
-import React from 'react';
-import PropTypes from 'prop-types';
-import { connect } from 'react-redux';
-
-import { views } from 'data/constants/app';
-import actions from 'data/actions';
-import selectors from 'data/selectors';
-
-import NetworkButton from 'components/NetworkButton';
-import ImportGradesButton from './ImportGradesButton';
-
-import messages from './BulkManagementControls.messages';
-
-/**
- *
- * Provides download buttons for Bulk Management and Intervention reports, only if
- * showBulkManagement is set in redus.
- */
-export class BulkManagementControls extends React.Component {
- constructor(props) {
- super(props);
- this.handleClickExportGrades = this.handleClickExportGrades.bind(this);
- this.handleViewActivityLog = this.handleViewActivityLog.bind(this);
- }
-
- handleClickExportGrades() {
- this.props.downloadBulkGradesReport();
- window.location.assign(this.props.gradeExportUrl);
- }
-
- handleViewActivityLog() {
- this.props.setView(views.bulkManagementHistory);
- }
-
- render() {
- return this.props.showBulkManagement && (
-
-
-
-
- );
- }
-}
-
-BulkManagementControls.defaultProps = {
- showBulkManagement: false,
-};
-
-BulkManagementControls.propTypes = {
- // redux
- downloadBulkGradesReport: PropTypes.func.isRequired,
- gradeExportUrl: PropTypes.string.isRequired,
- showBulkManagement: PropTypes.bool,
- setView: PropTypes.func.isRequired,
-};
-
-export const mapStateToProps = (state) => ({
- gradeExportUrl: selectors.root.gradeExportUrl(state),
- showBulkManagement: selectors.root.showBulkManagement(state),
-});
-
-export const mapDispatchToProps = {
- downloadBulkGradesReport: actions.grades.downloadReport.bulkGrades,
- setView: actions.app.setView,
-};
-
-export default connect(mapStateToProps, mapDispatchToProps)(BulkManagementControls);
diff --git a/src/components/GradesView/BulkManagementControls.test.jsx b/src/components/GradesView/BulkManagementControls.test.jsx
deleted file mode 100644
index f9fa5e2..0000000
--- a/src/components/GradesView/BulkManagementControls.test.jsx
+++ /dev/null
@@ -1,124 +0,0 @@
-import React from 'react';
-import { shallow } from 'enzyme';
-
-import actions from 'data/actions';
-import selectors from 'data/selectors';
-import { views } from 'data/constants/app';
-
-import {
- BulkManagementControls,
- mapStateToProps,
- mapDispatchToProps,
-} from './BulkManagementControls';
-
-jest.mock('./ImportGradesButton', () => 'ImportGradesButton');
-jest.mock('components/NetworkButton', () => 'NetworkButton');
-jest.mock('data/selectors', () => ({
- __esModule: true,
- default: {
- root: {
- gradeExportUrl: (state) => ({ gradeExportUrl: state }),
- interventionExportUrl: (state) => ({ interventionExportUrl: state }),
- showBulkManagement: (state) => ({ showBulkManagement: state }),
- },
- },
-}));
-jest.mock('data/actions', () => ({
- __esModule: true,
- default: {
- app: { setView: jest.fn() },
- grades: {
- downloadReport: {
- bulkGrades: jest.fn(),
- intervention: jest.fn(),
- },
- },
- },
-}));
-
-describe('BulkManagementControls', () => {
- describe('component', () => {
- let el;
- let props = {
- gradeExportUrl: 'gradesGoHere',
- interventionExportUrl: 'interventionsGoHere',
- };
- beforeEach(() => {
- props = {
- ...props,
- downloadBulkGradesReport: jest.fn(),
- downloadInterventionReport: jest.fn(),
- setView: jest.fn(),
- };
- });
- test('snapshot - empty if showBulkManagement is not truthy', () => {
- expect(shallow()).toEqual({});
- });
- describe('behavior', () => {
- const oldWindowLocation = window.location;
-
- beforeAll(() => {
- delete window.location;
- window.location = Object.defineProperties(
- {},
- {
- ...Object.getOwnPropertyDescriptors(oldWindowLocation),
- assign: {
- configurable: true,
- value: jest.fn(),
- },
- },
- );
- });
- beforeEach(() => {
- window.location.assign.mockReset();
- el = shallow();
- });
- afterAll(() => {
- // restore `window.location` to the `jsdom` `Location` object
- window.location = oldWindowLocation;
- });
- describe('handleViewActivityLog', () => {
- it('calls props.setView(views.bulkManagementHistory)', () => {
- el.instance().handleViewActivityLog();
- expect(props.setView).toHaveBeenCalledWith(views.bulkManagementHistory);
- });
- });
- describe('handleClickExportGrades', () => {
- const assertions = [
- 'calls props.downloadBulkGradesReport',
- 'sets location to props.gradeExportUrl',
- ];
- it(assertions.join(' and '), () => {
- el.instance().handleClickExportGrades();
- expect(props.downloadBulkGradesReport).toHaveBeenCalled();
- expect(window.location.assign).toHaveBeenCalledWith(props.gradeExportUrl);
- });
- });
- });
- });
-
- describe('mapStateToProps', () => {
- let mapped;
- const testState = { do: 'not', test: 'me' };
- beforeEach(() => {
- mapped = mapStateToProps(testState);
- });
- test('gradeExportUrl from root.gradeExportUrl', () => {
- expect(mapped.gradeExportUrl).toEqual(selectors.root.gradeExportUrl(testState));
- });
- test('showBulkManagement from root.showBulkManagement', () => {
- expect(mapped.showBulkManagement).toEqual(selectors.root.showBulkManagement(testState));
- });
- });
- describe('mapDispatchToProps', () => {
- test('downloadBulkGradesReport from actions.grades.downloadReport.bulkGrades', () => {
- expect(
- mapDispatchToProps.downloadBulkGradesReport,
- ).toEqual(actions.grades.downloadReport.bulkGrades);
- });
- test('setView from actions.app.setView', () => {
- expect(mapDispatchToProps.setView).toEqual(actions.app.setView);
- });
- });
-});
diff --git a/src/components/GradesView/BulkManagementControls/__snapshots__/index.test.jsx.snap b/src/components/GradesView/BulkManagementControls/__snapshots__/index.test.jsx.snap
new file mode 100644
index 0000000..a97d82f
--- /dev/null
+++ b/src/components/GradesView/BulkManagementControls/__snapshots__/index.test.jsx.snap
@@ -0,0 +1,19 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`BulkManagementControls render snapshot - show - network and import buttons 1`] = `
+
+
+
+
+`;
diff --git a/src/components/GradesView/BulkManagementControls/hooks.js b/src/components/GradesView/BulkManagementControls/hooks.js
new file mode 100644
index 0000000..21786f8
--- /dev/null
+++ b/src/components/GradesView/BulkManagementControls/hooks.js
@@ -0,0 +1,18 @@
+import { actions, selectors } from 'data/redux/hooks';
+
+export const useBulkManagementControlsData = () => {
+ const gradeExportUrl = selectors.root.useGradeExportUrl();
+ const showBulkManagement = selectors.root.useShowBulkManagement();
+ const downloadBulkGradesReport = actions.grades.downloadReport.useBulkGrades();
+
+ const handleClickExportGrades = () => {
+ downloadBulkGradesReport();
+ window.location.assign(gradeExportUrl);
+ };
+
+ return {
+ show: showBulkManagement,
+ handleClickExportGrades,
+ };
+};
+export default useBulkManagementControlsData;
diff --git a/src/components/GradesView/BulkManagementControls/hooks.test.js b/src/components/GradesView/BulkManagementControls/hooks.test.js
new file mode 100644
index 0000000..59fb6fd
--- /dev/null
+++ b/src/components/GradesView/BulkManagementControls/hooks.test.js
@@ -0,0 +1,72 @@
+import { actions, selectors } from 'data/redux/hooks';
+
+import useBulkManagementControlsData from './hooks';
+
+jest.mock('data/redux/hooks', () => ({
+ actions: {
+ grades: {
+ downloadReport: { useBulkGrades: jest.fn() },
+ },
+ },
+ selectors: {
+ root: {
+ useGradeExportUrl: jest.fn(),
+ useShowBulkManagement: jest.fn(),
+ },
+ },
+}));
+
+const downloadBulkGrades = jest.fn();
+actions.grades.downloadReport.useBulkGrades.mockReturnValue(downloadBulkGrades);
+const gradeExportUrl = 'test-grade-export-url';
+selectors.root.useGradeExportUrl.mockReturnValue(gradeExportUrl);
+selectors.root.useShowBulkManagement.mockReturnValue(true);
+
+let hook;
+describe('useBulkManagementControlsData', () => {
+ const oldWindowLocation = window.location;
+ beforeAll(() => {
+ delete window.location;
+ window.location = Object.defineProperties(
+ {},
+ {
+ ...Object.getOwnPropertyDescriptors(oldWindowLocation),
+ assign: { configurable: true, value: jest.fn() },
+ },
+ );
+ });
+ beforeEach(() => {
+ window.location.assign.mockReset();
+ hook = useBulkManagementControlsData();
+ });
+ afterAll(() => {
+ // restore `window.location` to the `jsdom` `Location` object
+ window.location = oldWindowLocation;
+ });
+ describe('initialization', () => {
+ it('initializes redux hooks', () => {
+ expect(selectors.root.useGradeExportUrl).toHaveBeenCalledWith();
+ expect(selectors.root.useShowBulkManagement).toHaveBeenCalledWith();
+ expect(actions.grades.downloadReport.useBulkGrades).toHaveBeenCalledWith();
+ });
+ });
+ describe('output', () => {
+ it('forwards show from showBulkManagement', () => {
+ expect(hook.show).toEqual(true);
+ selectors.root.useShowBulkManagement.mockReturnValue(false);
+ hook = useBulkManagementControlsData();
+ expect(hook.show).toEqual(false);
+ });
+ describe('handleClickExportGrades', () => {
+ beforeEach(() => {
+ hook.handleClickExportGrades();
+ });
+ it('downloads bulk grades report', () => {
+ expect(downloadBulkGrades).toHaveBeenCalledWith();
+ });
+ it('sets window location to grade export url', () => {
+ expect(window.location.assign).toHaveBeenCalledWith(gradeExportUrl);
+ });
+ });
+ });
+});
diff --git a/src/components/GradesView/BulkManagementControls/index.jsx b/src/components/GradesView/BulkManagementControls/index.jsx
new file mode 100644
index 0000000..911c33a
--- /dev/null
+++ b/src/components/GradesView/BulkManagementControls/index.jsx
@@ -0,0 +1,32 @@
+/* eslint-disable react/sort-comp, react/button-has-type */
+import React from 'react';
+
+import NetworkButton from 'components/NetworkButton';
+import ImportGradesButton from '../ImportGradesButton';
+
+import useBulkManagementControlsData from './hooks';
+import messages from './messages';
+
+/**
+ *
+ * Provides download buttons for Bulk Management and Intervention reports, only if
+ * showBulkManagement is set in redus.
+ */
+export const BulkManagementControls = () => {
+ const {
+ show,
+ handleClickExportGrades,
+ } = useBulkManagementControlsData();
+
+ return show && (
+
+
+
+
+ );
+};
+
+export default BulkManagementControls;
diff --git a/src/components/GradesView/BulkManagementControls/index.test.jsx b/src/components/GradesView/BulkManagementControls/index.test.jsx
new file mode 100644
index 0000000..e5e3ca6
--- /dev/null
+++ b/src/components/GradesView/BulkManagementControls/index.test.jsx
@@ -0,0 +1,32 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import useBulkManagementControlsData from './hooks';
+import BulkManagementControls from '.';
+
+jest.mock('../ImportGradesButton', () => 'ImportGradesButton');
+jest.mock('components/NetworkButton', () => 'NetworkButton');
+
+jest.mock('./hooks', () => jest.fn());
+
+const hookProps = {
+ show: true,
+ handleClickExportGrades: jest.fn(),
+};
+useBulkManagementControlsData.mockReturnValue(hookProps);
+
+describe('BulkManagementControls', () => {
+ describe('behavior', () => {
+ shallow();
+ expect(useBulkManagementControlsData).toHaveBeenCalledWith();
+ });
+ describe('render', () => {
+ test('snapshot - show - network and import buttons', () => {
+ expect(shallow()).toMatchSnapshot();
+ });
+ test('snapshot - empty if show is not truthy', () => {
+ useBulkManagementControlsData.mockReturnValueOnce({ ...hookProps, show: false });
+ expect(shallow().isEmptyRender()).toEqual(true);
+ });
+ });
+});
diff --git a/src/components/GradesView/BulkManagementControls.messages.js b/src/components/GradesView/BulkManagementControls/messages.js
similarity index 100%
rename from src/components/GradesView/BulkManagementControls.messages.js
rename to src/components/GradesView/BulkManagementControls/messages.js
diff --git a/src/data/redux/hooks/actions.js b/src/data/redux/hooks/actions.js
index 09c01c0..b1b5b4e 100644
--- a/src/data/redux/hooks/actions.js
+++ b/src/data/redux/hooks/actions.js
@@ -17,7 +17,14 @@ const filters = StrictDict({
useUpdateTrack: actionHook(actions.filters.update.track),
});
+const grades = StrictDict({
+ downloadReport: {
+ useBulkGrades: actionHook(actions.grades.downloadReport.bulkGrades),
+ },
+});
+
export default StrictDict({
app,
filters,
+ grades,
});
diff --git a/src/data/redux/hooks/actions.test.js b/src/data/redux/hooks/actions.test.js
index cbf0384..5e66d03 100644
--- a/src/data/redux/hooks/actions.test.js
+++ b/src/data/redux/hooks/actions.test.js
@@ -15,6 +15,11 @@ jest.mock('data/actions', () => ({
assignmentLimits: jest.fn(),
},
},
+ grades: {
+ downloadReport: {
+ bulkGrades: jest.fn(),
+ },
+ },
}));
jest.mock('./utils', () => ({
actionHook: (action) => ({ actionHook: action }),
@@ -49,4 +54,11 @@ describe('action hooks', () => {
);
testActionHook(hookKeys.useUpdateTrack, actionGroup.updateTrack);
});
+ describe('grades', () => {
+ beforeEach(() => { hooks = actionHooks.grades; });
+ test('downloadReport.useBulkGrades', () => {
+ expect(hooks.downloadReport.useBulkGrades)
+ .toEqual(actionHook(actions.grades.downloadReport.bulkGrades));
+ });
+ });
});