+`;
diff --git a/src/components/GradesTab/EditModal/__snapshots__/ModalHeaders.test.jsx.snap b/src/components/GradesTab/EditModal/__snapshots__/ModalHeaders.test.jsx.snap
new file mode 100644
index 0000000..8efe704
--- /dev/null
+++ b/src/components/GradesTab/EditModal/__snapshots__/ModalHeaders.test.jsx.snap
@@ -0,0 +1,51 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ModalHeaders Component snapshots gradeOverrideHistoryError is and empty and open is true modal open and StatusAlert showing 1`] = `
+
+
+
+
+
+
+`;
+
+exports[`ModalHeaders Component snapshots gradeOverrideHistoryError is empty and open is false modal closed and StatusAlert closed 1`] = `
+
+
+
+
+
+
+`;
diff --git a/src/components/GradesTab/EditModal/__snapshots__/test.jsx.snap b/src/components/GradesTab/EditModal/__snapshots__/test.jsx.snap
new file mode 100644
index 0000000..a5fc671
--- /dev/null
+++ b/src/components/GradesTab/EditModal/__snapshots__/test.jsx.snap
@@ -0,0 +1,75 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`EditMoal Component snapshots gradeOverrideHistoryError is and empty and open is true modal open and StatusAlert showing 1`] = `
+
+
+
+
+
+ Showing most recent actions (max 5). To see more, please contact support.
+
+
+ Note: Once you save, your changes will be visible to students.
+
+
+ }
+ buttons={
+ Array [
+ ,
+ ]
+ }
+ closeText="Cancel"
+ onClose={[MockFunction this.closeAssignmentModal]}
+ open={true}
+ title="Edit Grades"
+/>
+`;
+
+exports[`EditMoal Component snapshots gradeOverrideHistoryError is empty and open is false modal closed and StatusAlert closed 1`] = `
+
+
+
+
+
+ Showing most recent actions (max 5). To see more, please contact support.
+
+
+ Note: Once you save, your changes will be visible to students.
+
+
+ }
+ buttons={
+ Array [
+ ,
+ ]
+ }
+ closeText="Cancel"
+ onClose={[MockFunction this.closeAssignmentModal]}
+ open={false}
+ title="Edit Grades"
+/>
+`;
diff --git a/src/components/GradesTab/EditModal/index.jsx b/src/components/GradesTab/EditModal/index.jsx
index c96c18b..85e281e 100644
--- a/src/components/GradesTab/EditModal/index.jsx
+++ b/src/components/GradesTab/EditModal/index.jsx
@@ -16,6 +16,15 @@ import thunkActions from 'data/thunkActions';
import OverrideTable from './OverrideTable';
import ModalHeaders from './ModalHeaders';
+/**
+ *
+ * Wrapper component for the modal that allows editing the grade for an individual
+ * unit, for a given student.
+ * Provides a StatusAlert with override fetch errors if any are found, an OverrideTable
+ * (with appropriate headers) for managing the actual override, and a submit button for
+ * adjusting the grade.
+ * (also provides a close button that clears the modal state)
+ */
export class EditModal extends React.Component {
constructor(props) {
super(props);
diff --git a/src/components/GradesTab/EditModal/test.jsx b/src/components/GradesTab/EditModal/test.jsx
new file mode 100644
index 0000000..465ab66
--- /dev/null
+++ b/src/components/GradesTab/EditModal/test.jsx
@@ -0,0 +1,132 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+
+import actions from 'data/actions';
+import selectors from 'data/selectors';
+import thunkActions from 'data/thunkActions';
+
+import {
+ EditModal,
+ mapDispatchToProps,
+ mapStateToProps,
+} from '.';
+
+jest.mock('./OverrideTable', () => 'OverrideTable');
+jest.mock('./ModalHeaders', () => 'ModalHeaders');
+jest.mock('@edx/paragon', () => ({
+ Button: () => 'Button',
+ Modal: () => 'Modal',
+ StatusAlert: () => 'StatusAlert',
+}));
+jest.mock('data/actions', () => ({
+ __esModule: true,
+ default: {
+ app: { closeModal: jest.fn() },
+ grades: { doneViewingAssignment: jest.fn() },
+ },
+}));
+jest.mock('data/thunkActions', () => ({
+ __esModule: true,
+ default: {
+ grades: { updateGrades: jest.fn() },
+ },
+}));
+jest.mock('data/selectors', () => ({
+ __esModule: true,
+ default: {
+ app: {
+ modalState: {
+ open: jest.fn(state => ({ isModalOpen: state })),
+ },
+ },
+ grades: {
+ gradeOverrideHistoryError: jest.fn(state => ({ overrideHistoryError: state })),
+ },
+ },
+}));
+describe('EditMoal', () => {
+ let props;
+ beforeEach(() => {
+ props = {
+ gradeOverrideHistoryError: 'Weve been trying to contact you regarding...',
+ open: true,
+ closeModal: jest.fn(),
+ doneViewingAssignment: jest.fn(),
+ updateGrades: jest.fn(),
+ };
+ });
+
+ describe('Component', () => {
+ describe('behavior', () => {
+ let el;
+ beforeEach(() => {
+ el = shallow();
+ });
+ describe('closeAssignmentModal', () => {
+ it('calls props.doneViewingAssignment and props.closeModal', () => {
+ el.instance().closeAssignmentModal();
+ expect(props.doneViewingAssignment).toHaveBeenCalledWith();
+ expect(props.closeModal).toHaveBeenCalledWith();
+ });
+ });
+ describe('handleAdjustedGradeClick', () => {
+ it('calls props.updateGardes and this.closeAssignmentModal', () => {
+ el.instance().closeAssignmentModal = jest.fn();
+ el.instance().handleAdjustedGradeClick();
+ expect(props.updateGrades).toHaveBeenCalledWith();
+ expect(el.instance().closeAssignmentModal).toHaveBeenCalledWith();
+ });
+ });
+ });
+ describe('snapshots', () => {
+ let el;
+ beforeEach(() => {
+ el = shallow();
+ el.instance().closeAssignmentModal = jest.fn().mockName('this.closeAssignmentModal');
+ el.instance().handleAdjustedGradeClick = jest.fn().mockName(
+ 'this.handleAdjustedGradeClick',
+ );
+ });
+ describe('gradeOverrideHistoryError is and empty and open is true', () => {
+ test('modal open and StatusAlert showing', () => {
+ expect(el.instance().render()).toMatchSnapshot();
+ });
+ });
+ describe('gradeOverrideHistoryError is empty and open is false', () => {
+ test('modal closed and StatusAlert closed', () => {
+ el.setProps({ open: false, gradeOverrideHistoryError: '' });
+ expect(el.instance().render()).toMatchSnapshot();
+ });
+ });
+ });
+ });
+
+ describe('mapStateToProps', () => {
+ const testState = { martha: 'why did you say that name?!' };
+ let mapped;
+ beforeEach(() => {
+ mapped = mapStateToProps(testState);
+ });
+ test('gradeOverrideHistoryError from grades.gradeOverrideHistoryError', () => {
+ expect(
+ mapped.gradeOverrideHistoryError,
+ ).toEqual(selectors.grades.gradeOverrideHistoryError(testState));
+ });
+ test('open from app.modalState.open', () => {
+ expect(mapped.open).toEqual(selectors.app.modalState.open(testState));
+ });
+ });
+ describe('mapDispatchToProps', () => {
+ test('closeModal from actions.app.closeModal', () => {
+ expect(mapDispatchToProps.closeModal).toEqual(actions.app.closeModal);
+ });
+ test('doneViewingAssignemtn from actions.grades.doneViewingAssignment', () => {
+ expect(
+ mapDispatchToProps.doneViewingAssignment,
+ ).toEqual(actions.grades.doneViewingAssignment);
+ });
+ test('updateGrades from thunkActions.grades.updateGrades', () => {
+ expect(mapDispatchToProps.updateGrades).toEqual(thunkActions.grades.updateGrades);
+ });
+ });
+});
diff --git a/src/data/selectors/app.js b/src/data/selectors/app.js
index 186b89d..347fa28 100644
--- a/src/data/selectors/app.js
+++ b/src/data/selectors/app.js
@@ -109,6 +109,8 @@ export default StrictDict({
courseGradeFilterValidity,
courseGradeLimits,
editUpdateData,
+ isFilterMenuClosed,
+ isFilterMenuOpening,
...simpleSelectors,
modalState: StrictDict(modalSelectors),
filterMenu: StrictDict({