diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx index f2a58b560..270295ab1 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx @@ -16,6 +16,7 @@ import { answerOptionProps } from '../../../../../data/services/cms/types'; import Checker from './components/Checker'; import { FeedbackBox } from './components/Feedback'; import * as hooks from './hooks'; +import { ProblemTypeKeys } from '../../../../../data/constants/problem'; export const AnswerOption = ({ answer, @@ -43,6 +44,7 @@ export const AnswerOption = ({ hasSingleAnswer={hasSingleAnswer} answer={answer} setAnswer={setAnswer} + disabled={problemType === ProblemTypeKeys.NUMERIC} />
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.test.jsx index 439c597a3..514c4e4a3 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.test.jsx @@ -42,6 +42,9 @@ describe('AnswerOption', () => { test('snapshot: renders correct option with selected unselected feedback', () => { expect(shallow()).toMatchSnapshot(); }); + test('snapshot: renders correct option with numeric input problem', () => { + expect(shallow()).toMatchSnapshot(); + }); }); describe('mapStateToProps', () => { const testState = { A: 'pple', B: 'anana', C: 'ucumber' }; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswerOption.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswerOption.test.jsx.snap index dcf72e6e5..8c467209e 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswerOption.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswerOption.test.jsx.snap @@ -18,6 +18,7 @@ exports[`AnswerOption render snapshot: renders correct option with feedback 1`] "title": "Answer 1", } } + disabled={false} hasSingleAnswer={false} setAnswer={[Function]} /> @@ -75,6 +76,82 @@ exports[`AnswerOption render snapshot: renders correct option with feedback 1`] `; +exports[`AnswerOption render snapshot: renders correct option with numeric input problem 1`] = ` + +
+ +
+
+ + + + +
+
+ + + + +
+
+`; + exports[`AnswerOption render snapshot: renders correct option with selected unselected feedback 1`] = ` diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/__snapshots__/index.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/__snapshots__/index.test.jsx.snap index 86f530613..8dceea743 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/__snapshots__/index.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/__snapshots__/index.test.jsx.snap @@ -1,25 +1,55 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Checker component with disabled 1`] = ` + + + + A + + +`; + exports[`Checker component with multiple answers 1`] = ` - - A - + + + + A + + `; exports[`Checker component with single answer 1`] = ` - - A - + + + + A + + `; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/index.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/index.jsx index 70813d0f2..b616207b9 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/index.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/index.jsx @@ -3,24 +3,36 @@ import PropTypes from 'prop-types'; import { Form } from '@edx/paragon'; const Checker = ({ - hasSingleAnswer, answer, setAnswer, + hasSingleAnswer, + answer, + setAnswer, + disabled, }) => { let CheckerType = Form.Checkbox; if (hasSingleAnswer) { CheckerType = Form.Radio; } return ( - setAnswer({ correct: e.target.checked })} - checked={answer.correct} - isValid={answer.correct} - > - {answer.id} - + <> + setAnswer({ correct: e.target.checked })} + checked={answer.correct} + isValid={answer.correct} + disabled={disabled} + /> + + {answer.id} + + ); }; +Checker.defaultProps = { + disabled: false, +}; Checker.propTypes = { hasSingleAnswer: PropTypes.bool.isRequired, answer: PropTypes.shape({ @@ -28,6 +40,7 @@ Checker.propTypes = { id: PropTypes.number, }).isRequired, setAnswer: PropTypes.func.isRequired, + disabled: PropTypes.bool, }; export default Checker; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/index.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/index.test.jsx index 19998a536..0ecb229be 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/index.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/index.test.jsx @@ -19,4 +19,8 @@ describe('Checker component', () => { test('with multiple answers', () => { expect(shallow()).toMatchSnapshot(); }); + + test('with disabled', () => { + expect(shallow()).toMatchSnapshot(); + }); }); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.js b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.js index 5bdb368f2..65d1690f4 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.js @@ -213,6 +213,11 @@ export const typeRowHooks = ({ } }); }; + const updateAnswersToCorrect = () => { + answers.forEach(answer => { + updateAnswer({ ...answer, correct: true }); + }); + }; const onClick = () => { // Dropdown problems can only have one correct answer. When there is more than one correct answer // from a previous problem type, the correct attribute for selected answers need to be set to false. @@ -221,6 +226,11 @@ export const typeRowHooks = ({ clearPreviouslySelectedAnswers(); } } + // Numeric input problems can only have correct answers. Switch all answers to correct when switching + // to numeric input. + if (typeKey === ProblemTypeKeys.NUMERIC) { + updateAnswersToCorrect(); + } if (blockTitle === ProblemTypes[problemType].title) { setBlockTitle(ProblemTypes[typeKey].title); } diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js index 6caf59e1a..1b95bfdfa 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js @@ -3,6 +3,7 @@ import { MockUseState } from '../../../../../../testUtils'; import messages from './messages'; import * as hooks from './hooks'; +import { ProblemTypeKeys, ProblemTypes } from '../../../../../data/constants/problem'; jest.mock('react', () => { const updateState = jest.fn(); @@ -249,55 +250,55 @@ describe('Problem settings hooks', () => { }); describe('Type row hooks', () => { - test('test onClick', () => { - const typekey = 'optionresponse'; - const problemType = 'choiceresponse'; - const blockTitle = 'Multi-select'; - const setBlockTitle = jest.fn(); - const updateField = jest.fn(); - const updateAnswer = jest.fn(); - const answers = [{ - correct: true, - id: 'a', - }, - { - correct: true, - id: 'b', - }, - { - correct: false, - id: 'c', - }]; + const typeRowProps = { + problemType: ProblemTypeKeys.MULTISELECT, + typeKey: ProblemTypeKeys.DROPDOWN, + blockTitle: ProblemTypes[ProblemTypeKeys.MULTISELECT].title, + setBlockTitle: jest.fn(), + updateField: jest.fn(), + updateAnswer: jest.fn(), + correctAnswerCount: 2, + answers: [ + { correct: true, id: 'a' }, + { correct: true, id: 'b' }, + { correct: false, id: 'c' }, + ], + }; + beforeEach(() => { + jest.clearAllMocks(); + }); + test('test onClick Multi-select to Dropdown', () => { + output = hooks.typeRowHooks(typeRowProps); + output.onClick(); + expect(typeRowProps.setBlockTitle).toHaveBeenCalledWith(ProblemTypes[ProblemTypeKeys.DROPDOWN].title); + expect(typeRowProps.updateAnswer).toHaveBeenNthCalledWith(1, { ...typeRowProps.answers[0], correct: false }); + expect(typeRowProps.updateAnswer).toHaveBeenNthCalledWith(2, { ...typeRowProps.answers[1], correct: false }); + expect(typeRowProps.updateAnswer).not.toHaveBeenNthCalledWith(3, { ...typeRowProps.answers[2], correct: false }); + expect(typeRowProps.updateField).toHaveBeenCalledWith({ problemType: ProblemTypeKeys.DROPDOWN }); + }); + test('test onClick Multi-select to Numeric', () => { output = hooks.typeRowHooks({ - answers, - blockTitle, - correctAnswerCount: 2, - problemType, - setBlockTitle, - typeKey: typekey, - updateField, - updateAnswer, + ...typeRowProps, + typeKey: ProblemTypeKeys.NUMERIC, }); output.onClick(); - expect(setBlockTitle).toHaveBeenCalledWith('Dropdown'); - expect(updateAnswer).toHaveBeenNthCalledWith(1, { ...answers[0], correct: false }); - expect(updateAnswer).toHaveBeenNthCalledWith(2, { ...answers[1], correct: false }); - expect(updateAnswer).not.toHaveBeenNthCalledWith(3, { ...answers[2], correct: false }); - expect(updateField).toHaveBeenCalledWith({ problemType: typekey }); + expect(typeRowProps.setBlockTitle).toHaveBeenCalledWith(ProblemTypes[ProblemTypeKeys.NUMERIC].title); + expect(typeRowProps.updateAnswer).toHaveBeenNthCalledWith(1, { ...typeRowProps.answers[0], correct: true }); + expect(typeRowProps.updateAnswer).toHaveBeenNthCalledWith(2, { ...typeRowProps.answers[1], correct: true }); + expect(typeRowProps.updateAnswer).toHaveBeenNthCalledWith(3, { ...typeRowProps.answers[2], correct: true }); + expect(typeRowProps.updateField).toHaveBeenCalledWith({ problemType: ProblemTypeKeys.NUMERIC }); }); }); - describe('Type row hooks', () => { - test('test onClick', () => { - const switchToAdvancedEditor = jest.fn(); - const setConfirmOpen = jest.fn(); - window.scrollTo = jest.fn(); - hooks.confirmSwitchToAdvancedEditor({ - switchToAdvancedEditor, - setConfirmOpen, - }); - expect(switchToAdvancedEditor).toHaveBeenCalled(); - expect(setConfirmOpen).toHaveBeenCalledWith(false); - expect(window.scrollTo).toHaveBeenCalled(); + test('test confirmSwitchToAdvancedEditor hook', () => { + const switchToAdvancedEditor = jest.fn(); + const setConfirmOpen = jest.fn(); + window.scrollTo = jest.fn(); + hooks.confirmSwitchToAdvancedEditor({ + switchToAdvancedEditor, + setConfirmOpen, }); + expect(switchToAdvancedEditor).toHaveBeenCalled(); + expect(setConfirmOpen).toHaveBeenCalledWith(false); + expect(window.scrollTo).toHaveBeenCalled(); }); }); diff --git a/src/editors/data/redux/problem/reducers.js b/src/editors/data/redux/problem/reducers.js index 262f65007..6e7628ea5 100644 --- a/src/editors/data/redux/problem/reducers.js +++ b/src/editors/data/redux/problem/reducers.js @@ -2,7 +2,7 @@ import _ from 'lodash-es'; import { createSlice } from '@reduxjs/toolkit'; import { indexToLetterMap } from '../../../containers/ProblemEditor/data/OLXParser'; import { StrictDict } from '../../../utils'; -import { ShowAnswerTypesKeys } from '../../constants/problem'; +import { ProblemTypeKeys, ShowAnswerTypesKeys } from '../../constants/problem'; const nextAlphaId = (lastId) => String.fromCharCode(lastId.charCodeAt(0) + 1); const initialState = { @@ -108,8 +108,12 @@ const problem = createSlice({ title: '', selectedFeedback: '', unselectedFeedback: '', - correct: false, + correct: state.problemType === ProblemTypeKeys.NUMERIC, }; + let { correctAnswerCount } = state; + if (state.problemType === ProblemTypeKeys.NUMERIC) { + correctAnswerCount += 1; + } const answers = [ ...currAnswers, @@ -117,6 +121,7 @@ const problem = createSlice({ ]; return { ...state, + correctAnswerCount, answers, }; }, diff --git a/src/editors/data/redux/problem/reducers.test.js b/src/editors/data/redux/problem/reducers.test.js index a563821ef..18176176f 100644 --- a/src/editors/data/redux/problem/reducers.test.js +++ b/src/editors/data/redux/problem/reducers.test.js @@ -1,4 +1,5 @@ import { initialState, actions, reducer } from './reducers'; +import { ProblemTypeKeys } from '../../constants/problem'; const testingState = { ...initialState, @@ -71,20 +72,35 @@ describe('problem reducer', () => { }); }); describe('addAnswer', () => { + const answer = { + id: 'A', + correct: false, + selectedFeedback: '', + title: '', + unselectedFeedback: '', + }; it('sets answers', () => { - const answer = { - id: 'A', - correct: false, - selectedFeedback: '', - title: '', - unselectedFeedback: '', - }; expect(reducer({ ...testingState, problemType: 'choiceresponse' }, actions.addAnswer())).toEqual({ ...testingState, problemType: 'choiceresponse', answers: [answer], }); }); + it('sets answers for numeric input', () => { + const numericTestState = { + ...testingState, + problemType: ProblemTypeKeys.NUMERIC, + correctAnswerCount: 0, + }; + expect(reducer(numericTestState, actions.addAnswer())).toEqual({ + ...numericTestState, + correctAnswerCount: 1, + answers: [{ + ...answer, + correct: true, + }], + }); + }); }); describe('updateAnswer', () => { it('sets answers, as well as setting the correctAnswerCount ', () => {