From 571cb66fc940fd04c2e74ea7f508218e8bfc7987 Mon Sep 17 00:00:00 2001 From: Ben Warzeski Date: Tue, 5 Oct 2021 11:37:26 -0400 Subject: [PATCH 1/3] add unit tests for ReviewModal ReviewActions components --- .../ReviewModal/ReviewActions.test.jsx | 67 +++++++++++++++ .../ReviewModal/StartGradingButton.jsx | 4 +- .../ReviewModal/StartGradingButton.test.jsx | 75 ++++++++++++++++ .../ReviewModal/SubmissionNavigation.jsx | 16 ++-- .../ReviewModal/SubmissionNavigation.test.jsx | 86 +++++++++++++++++++ .../__snapshots__/ReviewActions.test.jsx.snap | 63 ++++++++++++++ .../StartGradingButton.test.jsx.snap | 33 +++++++ .../SubmissionNavigation.test.jsx.snap | 57 ++++++++++++ 8 files changed, 391 insertions(+), 10 deletions(-) create mode 100644 src/containers/ReviewModal/ReviewActions.test.jsx create mode 100644 src/containers/ReviewModal/StartGradingButton.test.jsx create mode 100644 src/containers/ReviewModal/SubmissionNavigation.test.jsx create mode 100644 src/containers/ReviewModal/__snapshots__/ReviewActions.test.jsx.snap create mode 100644 src/containers/ReviewModal/__snapshots__/StartGradingButton.test.jsx.snap create mode 100644 src/containers/ReviewModal/__snapshots__/SubmissionNavigation.test.jsx.snap diff --git a/src/containers/ReviewModal/ReviewActions.test.jsx b/src/containers/ReviewModal/ReviewActions.test.jsx new file mode 100644 index 0000000..6c439d5 --- /dev/null +++ b/src/containers/ReviewModal/ReviewActions.test.jsx @@ -0,0 +1,67 @@ +import React from 'react'; +import { shallow } from 'enzyme'; + +import actions from 'data/actions'; +import selectors from 'data/selectors'; + +import { ReviewActions, mapStateToProps, mapDispatchToProps } from './ReviewActions'; + +jest.mock('@edx/paragon', () => ({ + ActionRow: () => 'ActionRow', + Button: () => 'Button', +})); +jest.mock('data/selectors', () => ({ + __esModule: true, + default: { + app: { showRubric: (state) => ({ showRubric: state }) }, + grading: { + selected: { + username: (state) => ({ username: state }), + gradingStatus: (state) => ({ gradingStatus: state }), + }, + }, + }, +})); +jest.mock('components/StatusBadge', () => 'StatusBadge'); +jest.mock('./StartGradingButton', () => 'StartGradingButton'); +jest.mock('./SubmissionNavigation', () => 'SubmissionNavigation'); + +describe('ReviewActions component', () => { + describe('component', () => { + const props = { + gradingStatus: 'grading-status', + username: 'test-username', + showRubric: false, + }; + beforeEach(() => { + props.toggleShowRubric = jest.fn().mockName('this.props.toggleShowRubric'); + }); + test('snapshot: do not show rubric', () => { + expect(shallow()).toMatchSnapshot(); + }); + test('snapshot: show rubric', () => { + expect(shallow()).toMatchSnapshot(); + }); + }); + describe('mapStateToProps', () => { + let mapped; + const testState = { some: 'test-state' }; + beforeEach(() => { + mapped = mapStateToProps(testState); + }); + test('username loads from grading.selected.username', () => { + expect(mapped.username).toEqual(selectors.grading.selected.username(testState)); + }); + test('gradingStatus loads from grading.selected.gradingStatus', () => { + expect(mapped.gradingStatus).toEqual(selectors.grading.selected.gradingStatus(testState)); + }); + test('showRubric loads from app.showRubric', () => { + expect(mapped.showRubric).toEqual(selectors.app.showRubric(testState)); + }); + }); + describe('mapDispatchToProps', () => { + it('loads toggleShowRubric from actions.app.toggleShowRubric', () => { + expect(mapDispatchToProps.toggleShowRubric).toEqual(actions.app.toggleShowRubric); + }); + }); +}); diff --git a/src/containers/ReviewModal/StartGradingButton.jsx b/src/containers/ReviewModal/StartGradingButton.jsx index 47e58e9..64337e7 100644 --- a/src/containers/ReviewModal/StartGradingButton.jsx +++ b/src/containers/ReviewModal/StartGradingButton.jsx @@ -7,12 +7,11 @@ import { } from '@edx/paragon'; import { Cancel, Highlight } from '@edx/paragon/icons'; -import actions from 'data/actions'; import selectors from 'data/selectors'; import thunkActions from 'data/thunkActions'; import { gradingStatuses as statuses } from 'data/services/lms/constants'; -const buttonArgs = { +export const buttonArgs = { [statuses.ungraded]: { label: 'Start Grading', iconAfter: Highlight, @@ -58,7 +57,6 @@ export const mapStateToProps = (state) => ({ }); export const mapDispatchToProps = { - toggleShowRubric: actions.app.toggleShowRubric, startGrading: thunkActions.grading.startGrading, stopGrading: thunkActions.grading.stopGrading, }; diff --git a/src/containers/ReviewModal/StartGradingButton.test.jsx b/src/containers/ReviewModal/StartGradingButton.test.jsx new file mode 100644 index 0000000..625aa00 --- /dev/null +++ b/src/containers/ReviewModal/StartGradingButton.test.jsx @@ -0,0 +1,75 @@ +import React from 'react'; +import { shallow } from 'enzyme'; + +import selectors from 'data/selectors'; +import thunkActions from 'data/thunkActions'; +import { gradingStatuses as statuses } from 'data/services/lms/constants'; + +import { + StartGradingButton, + mapStateToProps, + mapDispatchToProps, +} from './StartGradingButton'; + +jest.mock('@edx/paragon', () => ({ + Button: () => 'Button', +})); +jest.mock('@edx/paragon/icons', () => ({ + Cancel: 'Cancel', + Highlight: 'Highlight', +})); +jest.mock('data/selectors', () => ({ + __esModule: true, + default: { + grading: { + selected: { + gradingStatus: (state) => ({ gradingStatus: state }), + }, + }, + }, +})); + +describe('StartGradingButton component', () => { + describe('component', () => { + const props = {}; + beforeEach(() => { + props.startGrading = jest.fn().mockName('this.props.startGrading'); + props.stopGrading = jest.fn().mockName('this.props.stopGrading'); + }); + const render = (gradingStatus) => shallow( + , + ); + test('snapshot: locked (null)', () => { + const el = render(statuses.locked); + expect(el).toMatchSnapshot(); + expect(el).toEqual({}); + }); + test('snapshot: ungraded (startGrading callback)', () => { + expect(render(statuses.ungraded)).toMatchSnapshot(); + }); + test('snapshot: graded (startGrading callback', () => { + expect(render(statuses.graded)).toMatchSnapshot(); + }); + test('snapshot: inProgress (stopGrading callback', () => { + expect(render(statuses.inProgress)).toMatchSnapshot(); + }); + }); + describe('mapStateToProps', () => { + let mapped; + const testState = { some: 'test-state' }; + beforeEach(() => { + mapped = mapStateToProps(testState); + }); + test('gradingStatus loads from grading.selected.gradingStatus', () => { + expect(mapped.gradingStatus).toEqual(selectors.grading.selected.gradingStatus(testState)); + }); + }); + describe('mapDispatchToProps', () => { + it('loads startGrading from thunkActions.grading.stargGrading', () => { + expect(mapDispatchToProps.startGrading).toEqual(thunkActions.grading.startGrading); + }); + it('loads stopGrading from thunkActions.grading.stopGrading', () => { + expect(mapDispatchToProps.stopGrading).toEqual(thunkActions.grading.stopGrading); + }); + }); +}); diff --git a/src/containers/ReviewModal/SubmissionNavigation.jsx b/src/containers/ReviewModal/SubmissionNavigation.jsx index 3239a4d..8cbf6b7 100644 --- a/src/containers/ReviewModal/SubmissionNavigation.jsx +++ b/src/containers/ReviewModal/SubmissionNavigation.jsx @@ -40,26 +40,28 @@ export const SubmissionNavigation = ({ ); SubmissionNavigation.defaultProps = { + hasPrevSubmission: false, + hasNextSubmission: false, }; SubmissionNavigation.propTypes = { - hasPrevSubmission: PropTypes.bool.isRequired, - hasNextSubmission: PropTypes.bool.isRequired, - loadPrev: PropTypes.func.isRequired, - loadNext: PropTypes.func.isRequired, activeIndex: PropTypes.number.isRequired, + hasNextSubmission: PropTypes.bool, + hasPrevSubmission: PropTypes.bool, + loadNext: PropTypes.func.isRequired, + loadPrev: PropTypes.func.isRequired, selectionLength: PropTypes.number.isRequired, }; export const mapStateToProps = (state) => ({ - hasPrevSubmission: selectors.grading.prev.doesExist(state), - hasNextSubmission: selectors.grading.next.doesExist(state), activeIndex: selectors.grading.activeIndex(state), + hasNextSubmission: selectors.grading.next.doesExist(state), + hasPrevSubmission: selectors.grading.prev.doesExist(state), selectionLength: selectors.grading.selectionLength(state), }); export const mapDispatchToProps = { - loadPrev: thunkActions.grading.loadPrev, loadNext: thunkActions.grading.loadNext, + loadPrev: thunkActions.grading.loadPrev, }; export default connect(mapStateToProps, mapDispatchToProps)(SubmissionNavigation); diff --git a/src/containers/ReviewModal/SubmissionNavigation.test.jsx b/src/containers/ReviewModal/SubmissionNavigation.test.jsx new file mode 100644 index 0000000..bc9cadd --- /dev/null +++ b/src/containers/ReviewModal/SubmissionNavigation.test.jsx @@ -0,0 +1,86 @@ +import React from 'react'; +import { shallow } from 'enzyme'; + +import selectors from 'data/selectors'; +import thunkActions from 'data/thunkActions'; +import { gradingStatuses as statuses } from 'data/services/lms/constants'; + +import { + SubmissionNavigation, + mapStateToProps, + mapDispatchToProps, +} from './SubmissionNavigation'; + +jest.mock('@edx/paragon', () => ({ + Icon: () => 'Icon', + IconButton: () => 'IconButton', +})); +jest.mock('@edx/paragon/icons', () => ({ + ChevronLeft: 'ChevronLeft', + ChevronRight: 'ChevronRight', +})); +jest.mock('data/selectors', () => ({ + __esModule: true, + default: { + grading: { + prev: { + doesExist: (state) => ({ prevDoesExist: state }), + }, + next: { + doesExist: (state) => ({ nextDoesExist: state }), + }, + activeIndex: (state) => ({ activeIndex: state }), + selectionLength: (state) => ({ selectionlength: state }), + }, + }, +})); + +describe('SubmissionNavigation component', () => { + describe('component', () => { + const props = { + activeIndex: 4, + selectionLength: 5, + }; + beforeEach(() => { + props.loadNext = jest.fn().mockName('this.props.loadNext'); + props.loadPrev = jest.fn().mockName('this.props.loadPrev'); + }); + test('snapshot: no prev submission (disabled)', () => { + expect(shallow( + , + )).toMatchSnapshot(); + }); + test('snapshot: no next submission (disabled)', () => { + expect(shallow( + , + )).toMatchSnapshot(); + }); + }); + describe('mapStateToProps', () => { + let mapped; + const testState = { some: 'test-state' }; + beforeEach(() => { + mapped = mapStateToProps(testState); + }); + test('activeIndex loads from grading.activeIndex', () => { + expect(mapped.activeIndex).toEqual(selectors.grading.activeIndex(testState)); + }); + test('hasNextSubmission loads from grading.next.doesExist', () => { + expect(mapped.hasNextSubmission).toEqual(selectors.grading.next.doesExist(testState)); + }); + test('hasPrevSubmission loads from grading.prev.doesExist', () => { + expect(mapped.hasPrevSubmission).toEqual(selectors.grading.prev.doesExist(testState)); + }); + test('selectionLength loads from grading.selectionLength', () => { + expect(mapped.selectionLength).toEqual(selectors.grading.selectionLength(testState)); + }); + }); + describe('mapDispatchToProps', () => { + it('loads loadNext from thunkActions.grading.loadNext', () => { + expect(mapDispatchToProps.loadNext).toEqual(thunkActions.grading.loadNext); + }); + it('loads loadPrev from thunkActions.grading.loadPrev', () => { + expect(mapDispatchToProps.loadPrev).toEqual(thunkActions.grading.loadPrev); + }); + }); +}); diff --git a/src/containers/ReviewModal/__snapshots__/ReviewActions.test.jsx.snap b/src/containers/ReviewModal/__snapshots__/ReviewActions.test.jsx.snap new file mode 100644 index 0000000..8e413b4 --- /dev/null +++ b/src/containers/ReviewModal/__snapshots__/ReviewActions.test.jsx.snap @@ -0,0 +1,63 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ReviewActions component component snapshot: do not show rubric 1`] = ` +
+ + + test-username + + +
+ + + +
+
+
+`; + +exports[`ReviewActions component component snapshot: show rubric 1`] = ` +
+ + + test-username + + +
+ + + +
+
+
+`; diff --git a/src/containers/ReviewModal/__snapshots__/StartGradingButton.test.jsx.snap b/src/containers/ReviewModal/__snapshots__/StartGradingButton.test.jsx.snap new file mode 100644 index 0000000..800c162 --- /dev/null +++ b/src/containers/ReviewModal/__snapshots__/StartGradingButton.test.jsx.snap @@ -0,0 +1,33 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`StartGradingButton component component snapshot: graded (startGrading callback 1`] = ` + +`; + +exports[`StartGradingButton component component snapshot: inProgress (stopGrading callback 1`] = ` + +`; + +exports[`StartGradingButton component component snapshot: locked (null) 1`] = `""`; + +exports[`StartGradingButton component component snapshot: ungraded (startGrading callback) 1`] = ` + +`; diff --git a/src/containers/ReviewModal/__snapshots__/SubmissionNavigation.test.jsx.snap b/src/containers/ReviewModal/__snapshots__/SubmissionNavigation.test.jsx.snap new file mode 100644 index 0000000..af1c43c --- /dev/null +++ b/src/containers/ReviewModal/__snapshots__/SubmissionNavigation.test.jsx.snap @@ -0,0 +1,57 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SubmissionNavigation component component snapshot: no next submission (disabled) 1`] = ` + + + + 5 + of + 5 + + + +`; + +exports[`SubmissionNavigation component component snapshot: no prev submission (disabled) 1`] = ` + + + + 1 + of + 5 + + + +`; From e56f22891ea5fe903e041ebe067e9965ae12f6be Mon Sep 17 00:00:00 2001 From: Ben Warzeski Date: Tue, 5 Oct 2021 15:07:02 -0400 Subject: [PATCH 2/3] re-organize ReviewModal and ReviewActions --- .../__snapshots__/ReviewActions.test.jsx.snap | 0 .../__snapshots__/StartGradingButton.test.jsx.snap | 0 .../__snapshots__/SubmissionNavigation.test.jsx.snap | 0 .../components}/StartGradingButton.jsx | 0 .../components}/StartGradingButton.test.jsx | 0 .../components}/SubmissionNavigation.jsx | 0 .../components}/SubmissionNavigation.test.jsx | 0 .../ReviewActions.jsx => ReviewActions/index.jsx} | 5 ++--- .../ReviewActions.test.jsx => ReviewActions/index.test.jsx} | 6 +++--- src/containers/ReviewModal/index.jsx | 2 +- 10 files changed, 6 insertions(+), 7 deletions(-) rename src/containers/{ReviewModal => ReviewActions}/__snapshots__/ReviewActions.test.jsx.snap (100%) rename src/containers/{ReviewModal => ReviewActions}/__snapshots__/StartGradingButton.test.jsx.snap (100%) rename src/containers/{ReviewModal => ReviewActions}/__snapshots__/SubmissionNavigation.test.jsx.snap (100%) rename src/containers/{ReviewModal => ReviewActions/components}/StartGradingButton.jsx (100%) rename src/containers/{ReviewModal => ReviewActions/components}/StartGradingButton.test.jsx (100%) rename src/containers/{ReviewModal => ReviewActions/components}/SubmissionNavigation.jsx (100%) rename src/containers/{ReviewModal => ReviewActions/components}/SubmissionNavigation.test.jsx (100%) rename src/containers/{ReviewModal/ReviewActions.jsx => ReviewActions/index.jsx} (91%) rename src/containers/{ReviewModal/ReviewActions.test.jsx => ReviewActions/index.test.jsx} (93%) diff --git a/src/containers/ReviewModal/__snapshots__/ReviewActions.test.jsx.snap b/src/containers/ReviewActions/__snapshots__/ReviewActions.test.jsx.snap similarity index 100% rename from src/containers/ReviewModal/__snapshots__/ReviewActions.test.jsx.snap rename to src/containers/ReviewActions/__snapshots__/ReviewActions.test.jsx.snap diff --git a/src/containers/ReviewModal/__snapshots__/StartGradingButton.test.jsx.snap b/src/containers/ReviewActions/__snapshots__/StartGradingButton.test.jsx.snap similarity index 100% rename from src/containers/ReviewModal/__snapshots__/StartGradingButton.test.jsx.snap rename to src/containers/ReviewActions/__snapshots__/StartGradingButton.test.jsx.snap diff --git a/src/containers/ReviewModal/__snapshots__/SubmissionNavigation.test.jsx.snap b/src/containers/ReviewActions/__snapshots__/SubmissionNavigation.test.jsx.snap similarity index 100% rename from src/containers/ReviewModal/__snapshots__/SubmissionNavigation.test.jsx.snap rename to src/containers/ReviewActions/__snapshots__/SubmissionNavigation.test.jsx.snap diff --git a/src/containers/ReviewModal/StartGradingButton.jsx b/src/containers/ReviewActions/components/StartGradingButton.jsx similarity index 100% rename from src/containers/ReviewModal/StartGradingButton.jsx rename to src/containers/ReviewActions/components/StartGradingButton.jsx diff --git a/src/containers/ReviewModal/StartGradingButton.test.jsx b/src/containers/ReviewActions/components/StartGradingButton.test.jsx similarity index 100% rename from src/containers/ReviewModal/StartGradingButton.test.jsx rename to src/containers/ReviewActions/components/StartGradingButton.test.jsx diff --git a/src/containers/ReviewModal/SubmissionNavigation.jsx b/src/containers/ReviewActions/components/SubmissionNavigation.jsx similarity index 100% rename from src/containers/ReviewModal/SubmissionNavigation.jsx rename to src/containers/ReviewActions/components/SubmissionNavigation.jsx diff --git a/src/containers/ReviewModal/SubmissionNavigation.test.jsx b/src/containers/ReviewActions/components/SubmissionNavigation.test.jsx similarity index 100% rename from src/containers/ReviewModal/SubmissionNavigation.test.jsx rename to src/containers/ReviewActions/components/SubmissionNavigation.test.jsx diff --git a/src/containers/ReviewModal/ReviewActions.jsx b/src/containers/ReviewActions/index.jsx similarity index 91% rename from src/containers/ReviewModal/ReviewActions.jsx rename to src/containers/ReviewActions/index.jsx index de2317a..66ed1b0 100644 --- a/src/containers/ReviewModal/ReviewActions.jsx +++ b/src/containers/ReviewActions/index.jsx @@ -11,9 +11,8 @@ import actions from 'data/actions'; import selectors from 'data/selectors'; import StatusBadge from 'components/StatusBadge'; -import StartGradingButton from './StartGradingButton'; -import SubmissionNavigation from './SubmissionNavigation'; -import './ReviewModal.scss'; +import StartGradingButton from './components/StartGradingButton'; +import SubmissionNavigation from './components/SubmissionNavigation'; export const ReviewActions = ({ gradingStatus, diff --git a/src/containers/ReviewModal/ReviewActions.test.jsx b/src/containers/ReviewActions/index.test.jsx similarity index 93% rename from src/containers/ReviewModal/ReviewActions.test.jsx rename to src/containers/ReviewActions/index.test.jsx index 6c439d5..37ac095 100644 --- a/src/containers/ReviewModal/ReviewActions.test.jsx +++ b/src/containers/ReviewActions/index.test.jsx @@ -4,7 +4,7 @@ import { shallow } from 'enzyme'; import actions from 'data/actions'; import selectors from 'data/selectors'; -import { ReviewActions, mapStateToProps, mapDispatchToProps } from './ReviewActions'; +import { ReviewActions, mapStateToProps, mapDispatchToProps } from '.'; jest.mock('@edx/paragon', () => ({ ActionRow: () => 'ActionRow', @@ -23,8 +23,8 @@ jest.mock('data/selectors', () => ({ }, })); jest.mock('components/StatusBadge', () => 'StatusBadge'); -jest.mock('./StartGradingButton', () => 'StartGradingButton'); -jest.mock('./SubmissionNavigation', () => 'SubmissionNavigation'); +jest.mock('./components/StartGradingButton', () => 'StartGradingButton'); +jest.mock('./components/SubmissionNavigation', () => 'SubmissionNavigation'); describe('ReviewActions component', () => { describe('component', () => { diff --git a/src/containers/ReviewModal/index.jsx b/src/containers/ReviewModal/index.jsx index 131231e..311c516 100644 --- a/src/containers/ReviewModal/index.jsx +++ b/src/containers/ReviewModal/index.jsx @@ -14,7 +14,7 @@ import actions from 'data/actions'; import ResponseDisplay from 'components/ResponseDisplay'; import Rubric from 'containers/Rubric'; -import ReviewActions from './ReviewActions'; +import ReviewActions from 'containers/ReviewActions'; import './ReviewModal.scss'; From e7ae3b91942f883f7d769037bc6afcc59eccdfd9 Mon Sep 17 00:00:00 2001 From: Ben Warzeski Date: Tue, 5 Oct 2021 16:26:09 -0400 Subject: [PATCH 3/3] ReviewActions re-org and adding ConfirmModals --- src/App.scss | 3 + src/components/ConfirmModal.jsx | 47 ++++++++ src/components/ConfirmModal.test.jsx | 27 +++++ .../__snapshots__/ConfirmModal.test.jsx.snap | 57 ++++++++++ .../StartGradingButton.test.jsx.snap | 33 ------ ...ions.test.jsx.snap => index.test.jsx.snap} | 0 .../components/OverrideGradeConfirmModal.jsx | 27 +++++ .../OverrideGradeConfirmModal.test.jsx | 19 ++++ .../components/StartGradingButton.jsx | 104 ++++++++++++++---- .../components/StartGradingButton.test.jsx | 16 ++- .../components/StopGradingConfirmModal.jsx | 27 +++++ .../StopGradingConfirmModal.test.jsx | 19 ++++ .../OverrideGradeConfirmModal.test.jsx.snap | 25 +++++ .../StartGradingButton.test.jsx.snap | 69 ++++++++++++ .../StopGradingConfirmModal.test.jsx.snap | 25 +++++ .../SubmissionNavigation.test.jsx.snap | 0 16 files changed, 441 insertions(+), 57 deletions(-) create mode 100644 src/components/ConfirmModal.jsx create mode 100644 src/components/ConfirmModal.test.jsx create mode 100644 src/components/__snapshots__/ConfirmModal.test.jsx.snap delete mode 100644 src/containers/ReviewActions/__snapshots__/StartGradingButton.test.jsx.snap rename src/containers/ReviewActions/__snapshots__/{ReviewActions.test.jsx.snap => index.test.jsx.snap} (100%) create mode 100644 src/containers/ReviewActions/components/OverrideGradeConfirmModal.jsx create mode 100644 src/containers/ReviewActions/components/OverrideGradeConfirmModal.test.jsx create mode 100644 src/containers/ReviewActions/components/StopGradingConfirmModal.jsx create mode 100644 src/containers/ReviewActions/components/StopGradingConfirmModal.test.jsx create mode 100644 src/containers/ReviewActions/components/__snapshots__/OverrideGradeConfirmModal.test.jsx.snap create mode 100644 src/containers/ReviewActions/components/__snapshots__/StartGradingButton.test.jsx.snap create mode 100644 src/containers/ReviewActions/components/__snapshots__/StopGradingConfirmModal.test.jsx.snap rename src/containers/ReviewActions/{ => components}/__snapshots__/SubmissionNavigation.test.jsx.snap (100%) diff --git a/src/App.scss b/src/App.scss index 7dfe894..33c8e59 100755 --- a/src/App.scss +++ b/src/App.scss @@ -74,4 +74,7 @@ $input-focus-box-shadow: $input-box-shadow; // hack to get upgrade to paragon 4. right: 1rem !important; } } + .confirm-modal .pgn__modal-body { + overflow: hidden; + } } diff --git a/src/components/ConfirmModal.jsx b/src/components/ConfirmModal.jsx new file mode 100644 index 0000000..b60b214 --- /dev/null +++ b/src/components/ConfirmModal.jsx @@ -0,0 +1,47 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import { AlertModal, ActionRow, Button } from '@edx/paragon'; + +export const ConfirmModal = ({ + title, + isOpen, + onCancel, + cancelText, + onConfirm, + confirmText, + content, +}) => ( + + + + + )} + > +

{content}

+
+); +ConfirmModal.defaultProps = { + isOpen: false, +}; +ConfirmModal.propTypes = { + isOpen: PropTypes.bool, + title: PropTypes.string.isRequired, + cancelText: PropTypes.string.isRequired, + onCancel: PropTypes.func.isRequired, + onConfirm: PropTypes.func.isRequired, + confirmText: PropTypes.string.isRequired, + content: PropTypes.string.isRequired, +}; + +export default ConfirmModal; diff --git a/src/components/ConfirmModal.test.jsx b/src/components/ConfirmModal.test.jsx new file mode 100644 index 0000000..bc5fb05 --- /dev/null +++ b/src/components/ConfirmModal.test.jsx @@ -0,0 +1,27 @@ +import { shallow } from 'enzyme'; + +import { ConfirmModal } from './ConfirmModal'; + +jest.mock('@edx/paragon', () => ({ + ActionRow: () => 'ActionRow', + AlertModal: () => 'AlertModal', + Button: () => 'Button', +})); + +describe('ConfirmModal', () => { + const props = { + isOpen: false, + title: 'test-title', + cancelText: 'test-cancel-text', + confirmText: 'test-confirm-text', + content: 'test-content', + onCancel: jest.fn().mockName('this.props.onCancel'), + onConfirm: jest.fn().mockName('this.props.onConfirm'), + }; + test('snapshot: closed', () => { + expect(shallow()).toMatchSnapshot(); + }); + test('snapshot: open', () => { + expect(shallow()).toMatchSnapshot(); + }); +}); diff --git a/src/components/__snapshots__/ConfirmModal.test.jsx.snap b/src/components/__snapshots__/ConfirmModal.test.jsx.snap new file mode 100644 index 0000000..dad16c1 --- /dev/null +++ b/src/components/__snapshots__/ConfirmModal.test.jsx.snap @@ -0,0 +1,57 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ConfirmModal snapshot: closed 1`] = ` + + + + + } + isOpen={false} + title="test-title" +> +

+ test-content +

+
+`; + +exports[`ConfirmModal snapshot: open 1`] = ` + + + + + } + isOpen={true} + title="test-title" +> +

+ test-content +

+
+`; diff --git a/src/containers/ReviewActions/__snapshots__/StartGradingButton.test.jsx.snap b/src/containers/ReviewActions/__snapshots__/StartGradingButton.test.jsx.snap deleted file mode 100644 index 800c162..0000000 --- a/src/containers/ReviewActions/__snapshots__/StartGradingButton.test.jsx.snap +++ /dev/null @@ -1,33 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`StartGradingButton component component snapshot: graded (startGrading callback 1`] = ` - -`; - -exports[`StartGradingButton component component snapshot: inProgress (stopGrading callback 1`] = ` - -`; - -exports[`StartGradingButton component component snapshot: locked (null) 1`] = `""`; - -exports[`StartGradingButton component component snapshot: ungraded (startGrading callback) 1`] = ` - -`; diff --git a/src/containers/ReviewActions/__snapshots__/ReviewActions.test.jsx.snap b/src/containers/ReviewActions/__snapshots__/index.test.jsx.snap similarity index 100% rename from src/containers/ReviewActions/__snapshots__/ReviewActions.test.jsx.snap rename to src/containers/ReviewActions/__snapshots__/index.test.jsx.snap diff --git a/src/containers/ReviewActions/components/OverrideGradeConfirmModal.jsx b/src/containers/ReviewActions/components/OverrideGradeConfirmModal.jsx new file mode 100644 index 0000000..43a7a2f --- /dev/null +++ b/src/containers/ReviewActions/components/OverrideGradeConfirmModal.jsx @@ -0,0 +1,27 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import ConfirmModal from 'components/ConfirmModal'; + +export const OverrideGradeConfirmModal = ({ + isOpen, + onCancel, + onConfirm, +}) => ( + +); +OverrideGradeConfirmModal.propTypes = { + isOpen: PropTypes.bool.isRequired, + onCancel: PropTypes.func.isRequired, + onConfirm: PropTypes.func.isRequired, +}; + +export default OverrideGradeConfirmModal; diff --git a/src/containers/ReviewActions/components/OverrideGradeConfirmModal.test.jsx b/src/containers/ReviewActions/components/OverrideGradeConfirmModal.test.jsx new file mode 100644 index 0000000..dfe80f3 --- /dev/null +++ b/src/containers/ReviewActions/components/OverrideGradeConfirmModal.test.jsx @@ -0,0 +1,19 @@ +import { shallow } from 'enzyme'; + +import { OverrideGradeConfirmModal } from './OverrideGradeConfirmModal'; + +jest.mock('components/ConfirmModal', () => 'ConfirmModal'); + +describe('OverrideGradeConfirmModal', () => { + const props = { + isOpen: false, + onCancel: jest.fn().mockName('this.props.onCancel'), + onConfirm: jest.fn().mockName('this.props.onConfirm'), + }; + test('snapshot: closed', () => { + expect(shallow()).toMatchSnapshot(); + }); + test('snapshot: open', () => { + expect(shallow()).toMatchSnapshot(); + }); +}); diff --git a/src/containers/ReviewActions/components/StartGradingButton.jsx b/src/containers/ReviewActions/components/StartGradingButton.jsx index 64337e7..22eea53 100644 --- a/src/containers/ReviewActions/components/StartGradingButton.jsx +++ b/src/containers/ReviewActions/components/StartGradingButton.jsx @@ -11,6 +11,9 @@ import selectors from 'data/selectors'; import thunkActions from 'data/thunkActions'; import { gradingStatuses as statuses } from 'data/services/lms/constants'; +import StopGradingConfirmModal from './StopGradingConfirmModal'; +import OverrideGradeConfirmModal from './OverrideGradeConfirmModal'; + export const buttonArgs = { [statuses.ungraded]: { label: 'Start Grading', @@ -26,26 +29,89 @@ export const buttonArgs = { }, }; -export const StartGradingButton = ({ - gradingStatus, - startGrading, - stopGrading, -}) => { - if (gradingStatus === statuses.locked) { - return null; +export class StartGradingButton extends React.Component { + constructor(props) { + super(props); + this.state = { + showConfirmStopGrading: false, + showConfirmOverrideGrade: false, + }; + + this.showConfirmStopGrading = this.showConfirmStopGrading.bind(this); + this.hideConfirmStopGrading = this.hideConfirmStopGrading.bind(this); + this.showConfirmOverrideGrade = this.showConfirmOverrideGrade.bind(this); + this.hideConfirmOverrideGrade = this.hideConfirmOverrideGrade.bind(this); + this.confirmStopGrading = this.confirmStopGrading.bind(this); + this.confirmOverrideGrade = this.confirmOverrideGrade.bind(this); + this.handleClick = this.handleClick.bind(this); } - const args = buttonArgs[gradingStatus]; - const onClick = ((gradingStatus === statuses.inProgress) ? stopGrading : startGrading); - return ( - - ); -}; + + showConfirmStopGrading() { + this.setState({ showConfirmStopGrading: true }); + } + + hideConfirmStopGrading() { + this.setState({ showConfirmStopGrading: false }); + } + + showConfirmOverrideGrade() { + this.setState({ showConfirmOverrideGrade: true }); + } + + hideConfirmOverrideGrade() { + this.setState({ showConfirmOverrideGrade: false }); + } + + confirmStopGrading() { + this.hideConfirmStopGrading(); + this.props.stopGrading(); + } + + confirmOverrideGrade() { + this.hideConfirmOverrideGrade(); + this.props.startGrading(); + } + + handleClick() { + if (this.props.gradingStatus === statuses.inProgress) { + this.showConfirmStopGrading(); + } else if (this.props.gradingStatus === statuses.graded) { + this.showConfirmOverrideGrade(); + } else { + this.props.startGrading(); + } + } + + render() { + const { gradingStatus } = this.props; + if (gradingStatus === statuses.locked) { + return null; + } + const args = buttonArgs[gradingStatus]; + return ( + <> + + + + + ); + } +} + StartGradingButton.propTypes = { gradingStatus: PropTypes.string.isRequired, startGrading: PropTypes.func.isRequired, diff --git a/src/containers/ReviewActions/components/StartGradingButton.test.jsx b/src/containers/ReviewActions/components/StartGradingButton.test.jsx index 625aa00..1161914 100644 --- a/src/containers/ReviewActions/components/StartGradingButton.test.jsx +++ b/src/containers/ReviewActions/components/StartGradingButton.test.jsx @@ -29,6 +29,8 @@ jest.mock('data/selectors', () => ({ }, })); +let el; + describe('StartGradingButton component', () => { describe('component', () => { const props = {}; @@ -40,18 +42,22 @@ describe('StartGradingButton component', () => { , ); test('snapshot: locked (null)', () => { - const el = render(statuses.locked); + el = render(statuses.locked); expect(el).toMatchSnapshot(); expect(el).toEqual({}); }); test('snapshot: ungraded (startGrading callback)', () => { expect(render(statuses.ungraded)).toMatchSnapshot(); }); - test('snapshot: graded (startGrading callback', () => { - expect(render(statuses.graded)).toMatchSnapshot(); + test('snapshot: graded, confirmOverride (startGrading callback)', () => { + el = render(statuses.graded); + el.setState({ showConfirmOverrideGrade: true }); + expect(el.instance().render()).toMatchSnapshot(); }); - test('snapshot: inProgress (stopGrading callback', () => { - expect(render(statuses.inProgress)).toMatchSnapshot(); + test('snapshot: inProgress, confirmStop (stopGrading callback)', () => { + el = render(statuses.inProgress); + el.setState({ showConfirmStopGrading: true }); + expect(el.instance().render()).toMatchSnapshot(); }); }); describe('mapStateToProps', () => { diff --git a/src/containers/ReviewActions/components/StopGradingConfirmModal.jsx b/src/containers/ReviewActions/components/StopGradingConfirmModal.jsx new file mode 100644 index 0000000..3598b92 --- /dev/null +++ b/src/containers/ReviewActions/components/StopGradingConfirmModal.jsx @@ -0,0 +1,27 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import ConfirmModal from 'components/ConfirmModal'; + +export const StopGradingConfirmModal = ({ + isOpen, + onCancel, + onConfirm, +}) => ( + +); +StopGradingConfirmModal.propTypes = { + isOpen: PropTypes.bool.isRequired, + onCancel: PropTypes.func.isRequired, + onConfirm: PropTypes.func.isRequired, +}; + +export default StopGradingConfirmModal; diff --git a/src/containers/ReviewActions/components/StopGradingConfirmModal.test.jsx b/src/containers/ReviewActions/components/StopGradingConfirmModal.test.jsx new file mode 100644 index 0000000..83b7ba7 --- /dev/null +++ b/src/containers/ReviewActions/components/StopGradingConfirmModal.test.jsx @@ -0,0 +1,19 @@ +import { shallow } from 'enzyme'; + +import { StopGradingConfirmModal } from './StopGradingConfirmModal'; + +jest.mock('components/ConfirmModal', () => 'ConfirmModal'); + +describe('StopGradingConfirmModal', () => { + const props = { + isOpen: false, + onCancel: jest.fn().mockName('this.props.onCancel'), + onConfirm: jest.fn().mockName('this.props.onConfirm'), + }; + test('snapshot: closed', () => { + expect(shallow()).toMatchSnapshot(); + }); + test('snapshot: open', () => { + expect(shallow()).toMatchSnapshot(); + }); +}); diff --git a/src/containers/ReviewActions/components/__snapshots__/OverrideGradeConfirmModal.test.jsx.snap b/src/containers/ReviewActions/components/__snapshots__/OverrideGradeConfirmModal.test.jsx.snap new file mode 100644 index 0000000..a62f2f9 --- /dev/null +++ b/src/containers/ReviewActions/components/__snapshots__/OverrideGradeConfirmModal.test.jsx.snap @@ -0,0 +1,25 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`OverrideGradeConfirmModal snapshot: closed 1`] = ` + +`; + +exports[`OverrideGradeConfirmModal snapshot: open 1`] = ` + +`; diff --git a/src/containers/ReviewActions/components/__snapshots__/StartGradingButton.test.jsx.snap b/src/containers/ReviewActions/components/__snapshots__/StartGradingButton.test.jsx.snap new file mode 100644 index 0000000..28c3315 --- /dev/null +++ b/src/containers/ReviewActions/components/__snapshots__/StartGradingButton.test.jsx.snap @@ -0,0 +1,69 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`StartGradingButton component component snapshot: graded, confirmOverride (startGrading callback) 1`] = ` + + + + + +`; + +exports[`StartGradingButton component component snapshot: inProgress, confirmStop (stopGrading callback) 1`] = ` + + + + + +`; + +exports[`StartGradingButton component component snapshot: locked (null) 1`] = `""`; + +exports[`StartGradingButton component component snapshot: ungraded (startGrading callback) 1`] = ` + + + + + +`; diff --git a/src/containers/ReviewActions/components/__snapshots__/StopGradingConfirmModal.test.jsx.snap b/src/containers/ReviewActions/components/__snapshots__/StopGradingConfirmModal.test.jsx.snap new file mode 100644 index 0000000..4cd3a8a --- /dev/null +++ b/src/containers/ReviewActions/components/__snapshots__/StopGradingConfirmModal.test.jsx.snap @@ -0,0 +1,25 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`StopGradingConfirmModal snapshot: closed 1`] = ` + +`; + +exports[`StopGradingConfirmModal snapshot: open 1`] = ` + +`; diff --git a/src/containers/ReviewActions/__snapshots__/SubmissionNavigation.test.jsx.snap b/src/containers/ReviewActions/components/__snapshots__/SubmissionNavigation.test.jsx.snap similarity index 100% rename from src/containers/ReviewActions/__snapshots__/SubmissionNavigation.test.jsx.snap rename to src/containers/ReviewActions/components/__snapshots__/SubmissionNavigation.test.jsx.snap