refactor: bulk management controls component
This commit is contained in:
@@ -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';
|
||||
|
||||
/**
|
||||
* <BulkManagementControls />
|
||||
* 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 && (
|
||||
<div className="d-flex">
|
||||
<NetworkButton
|
||||
label={messages.downloadGradesBtn}
|
||||
onClick={this.handleClickExportGrades}
|
||||
/>
|
||||
<ImportGradesButton />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -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(<BulkManagementControls {...props} />)).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(<BulkManagementControls {...props} showBulkManagement />);
|
||||
});
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`BulkManagementControls render snapshot - show - network and import buttons 1`] = `
|
||||
<div
|
||||
className="d-flex"
|
||||
>
|
||||
<NetworkButton
|
||||
label={
|
||||
Object {
|
||||
"defaultMessage": "Download Grades",
|
||||
"description": "A labeled button that allows an admin user to download course grades all at once (in bulk).",
|
||||
"id": "gradebook.GradesView.BulkManagementControls.bulkManagementLabel",
|
||||
}
|
||||
}
|
||||
onClick={[MockFunction]}
|
||||
/>
|
||||
<ImportGradesButton />
|
||||
</div>
|
||||
`;
|
||||
18
src/components/GradesView/BulkManagementControls/hooks.js
Normal file
18
src/components/GradesView/BulkManagementControls/hooks.js
Normal file
@@ -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;
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
32
src/components/GradesView/BulkManagementControls/index.jsx
Normal file
32
src/components/GradesView/BulkManagementControls/index.jsx
Normal file
@@ -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';
|
||||
|
||||
/**
|
||||
* <BulkManagementControls />
|
||||
* 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 && (
|
||||
<div className="d-flex">
|
||||
<NetworkButton
|
||||
label={messages.downloadGradesBtn}
|
||||
onClick={handleClickExportGrades}
|
||||
/>
|
||||
<ImportGradesButton />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BulkManagementControls;
|
||||
@@ -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(<BulkManagementControls />);
|
||||
expect(useBulkManagementControlsData).toHaveBeenCalledWith();
|
||||
});
|
||||
describe('render', () => {
|
||||
test('snapshot - show - network and import buttons', () => {
|
||||
expect(shallow(<BulkManagementControls />)).toMatchSnapshot();
|
||||
});
|
||||
test('snapshot - empty if show is not truthy', () => {
|
||||
useBulkManagementControlsData.mockReturnValueOnce({ ...hookProps, show: false });
|
||||
expect(shallow(<BulkManagementControls />).isEmptyRender()).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user