diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx index fead9b3fd..f2a58b560 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx @@ -28,6 +28,8 @@ export const AnswerOption = ({ const dispatch = useDispatch(); const removeAnswer = hooks.removeAnswer({ answer, dispatch }); const setAnswer = hooks.setAnswer({ answer, hasSingleAnswer, dispatch }); + const setSelectedFeedback = hooks.setSelectedFeedback({ answer, hasSingleAnswer, dispatch }); + const setUnselectedFeedback = hooks.setUnselectedFeedback({ answer, hasSingleAnswer, dispatch }); const { isFeedbackVisible, toggleFeedback } = hooks.useFeedback(answer); return ( @@ -57,7 +59,8 @@ export const AnswerOption = ({ 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 92adf2628..dcf72e6e5 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 @@ -50,7 +50,8 @@ exports[`AnswerOption render snapshot: renders correct option with feedback 1`] } } problemType="multiplechoiceresponse" - setAnswer={[Function]} + setSelectedFeedback={[Function]} + setUnselectedFeedback={[Function]} /> @@ -126,7 +127,8 @@ exports[`AnswerOption render snapshot: renders correct option with selected unse } } problemType="choiceresponse" - setAnswer={[Function]} + setSelectedFeedback={[Function]} + setUnselectedFeedback={[Function]} /> diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackBox.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackBox.jsx index 1322a5b90..f3e59e9bc 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackBox.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackBox.jsx @@ -7,10 +7,12 @@ import FeedbackControl from './FeedbackControl'; import { messages } from './messages'; export const FeedbackBox = ({ - answer, setAnswer, intl, + answer, + intl, + setSelectedFeedback, + setUnselectedFeedback, }) => { const props = { - onChange: (e) => setAnswer({ selectedFeedback: e.target.value }), answer, intl, }; @@ -22,6 +24,7 @@ export const FeedbackBox = ({ feedback={answer.selectedFeedback} labelMessage={messages.selectedFeedbackLabel} labelMessageBoldUnderline={messages.selectedFeedbackLabelBoldUnderlineText} + onChange={setSelectedFeedback} {...props} /> @@ -37,6 +41,8 @@ export const FeedbackBox = ({ FeedbackBox.propTypes = { answer: answerOptionProps.isRequired, setAnswer: PropTypes.func.isRequired, + setSelectedFeedback: PropTypes.func.isRequired, + setUnselectedFeedback: PropTypes.func.isRequired, intl: intlShape.isRequired, }; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackBox.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackBox.test.jsx index 117c48255..351c3516e 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackBox.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackBox.test.jsx @@ -12,7 +12,6 @@ const answerWithFeedback = { const props = { answer: answerWithFeedback, intl: {}, - setAnswer: jest.fn(), }; describe('FeedbackBox component', () => { diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/__snapshots__/FeedbackBox.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/__snapshots__/FeedbackBox.test.jsx.snap index 54fd1c164..8fe0ca86f 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/__snapshots__/FeedbackBox.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/__snapshots__/FeedbackBox.test.jsx.snap @@ -31,7 +31,6 @@ exports[`FeedbackBox component renders 1`] = ` "id": "authoring.answerwidget.feedback.selected.label.boldunderline", } } - onChange={[Function]} /> `; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/hooks.js b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/hooks.js index 9a4cfe6c1..19b7852c9 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/hooks.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/hooks.js @@ -9,24 +9,40 @@ export const state = StrictDict({ }); export const removeAnswer = ({ answer, dispatch }) => () => { - dispatch(actions.problem.deleteAnswer({ id: answer.id })); + dispatch(actions.problem.deleteAnswer({ id: answer.id, correct: answer.correct })); }; export const setAnswer = ({ answer, hasSingleAnswer, dispatch }) => (payload) => { dispatch(actions.problem.updateAnswer({ id: answer.id, hasSingleAnswer, ...payload })); }; +export const setSelectedFeedback = ({ answer, hasSingleAnswer, dispatch }) => (e) => { + dispatch(actions.problem.updateAnswer({ + id: answer.id, + hasSingleAnswer, + selectedFeedback: e.target.value, + })); +}; + +export const setUnselectedFeedback = ({ answer, hasSingleAnswer, dispatch }) => (e) => { + dispatch(actions.problem.updateAnswer({ + id: answer.id, + hasSingleAnswer, + unselectedFeedback: e.target.value, + })); +}; + export const useFeedback = (answer) => { const [isFeedbackVisible, setIsFeedbackVisible] = module.state.isFeedbackVisible(false); useEffect(() => { // Show feedback fields if feedback is present - const isVisible = !!answer.selectedFeedback || !!answer.unselectedFeedback || !!answer.feedback; + const isVisible = !!answer.selectedFeedback || !!answer.unselectedFeedback; setIsFeedbackVisible(isVisible); }, [answer]); const toggleFeedback = (open) => { // Do not allow to hide if feedback is added - if (!!answer.selectedFeedback || !!answer.unselectedFeedback || !!answer.feedback) { + if (!!answer.selectedFeedback || !!answer.unselectedFeedback) { setIsFeedbackVisible(true); return; } diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/hook.test.js b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/hooks.test.js similarity index 50% rename from src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/hook.test.js rename to src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/hooks.test.js index f5e34dfbd..d70b39dde 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/hook.test.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/hooks.test.js @@ -1,4 +1,7 @@ import { useEffect } from 'react'; +import { useDispatch } from 'react-redux'; + +import { actions } from '../../../../../data/redux'; import { MockUseState } from '../../../../../../testUtils'; import { ProblemTypeKeys } from '../../../../../data/constants/problem'; import * as module from './hooks'; @@ -28,7 +31,7 @@ const answerWithOnlyFeedback = { id: 'A', title: 'Answer 1', correct: true, - feedback: 'some feedback', + selectedFeedback: 'some feedback', }; describe('Answer Options Hooks', () => { @@ -36,6 +39,59 @@ describe('Answer Options Hooks', () => { describe('state hooks', () => { state.testGetter(state.keys.isFeedbackVisible); }); + describe('removeAnswer', () => { + test('it dispatches actions.problem.deleteAnswer', () => { + const answer = { id: 'A', correct: false }; + const dispatch = useDispatch(); + module.removeAnswer({ answer, dispatch })(); + expect(dispatch).toHaveBeenCalledWith(actions.problem.deleteAnswer({ + id: answer.id, + correct: answer.correct, + })); + }); + }); + describe('setAnswer', () => { + test('it dispatches actions.problem.updateAnswer', () => { + const answer = { id: 'A' }; + const hasSingleAnswer = false; + const dispatch = useDispatch(); + const payload = { random: 'string' }; + module.setAnswer({ answer, hasSingleAnswer, dispatch })(payload); + expect(dispatch).toHaveBeenCalledWith(actions.problem.updateAnswer({ + id: answer.id, + hasSingleAnswer, + ...payload, + })); + }); + }); + describe('setSelectedFeedback', () => { + test('it dispatches actions.problem.updateAnswer', () => { + const answer = { id: 'A' }; + const hasSingleAnswer = false; + const dispatch = useDispatch(); + const e = { target: { value: 'string' } }; + module.setSelectedFeedback({ answer, hasSingleAnswer, dispatch })(e); + expect(dispatch).toHaveBeenCalledWith(actions.problem.updateAnswer({ + id: answer.id, + hasSingleAnswer, + selectedFeedback: e.target.value, + })); + }); + }); + describe('setUnselectedFeedback', () => { + test('it dispatches actions.problem.updateAnswer', () => { + const answer = { id: 'A' }; + const hasSingleAnswer = false; + const dispatch = useDispatch(); + const e = { target: { value: 'string' } }; + module.setUnselectedFeedback({ answer, hasSingleAnswer, dispatch })(e); + expect(dispatch).toHaveBeenCalledWith(actions.problem.updateAnswer({ + id: answer.id, + hasSingleAnswer, + unselectedFeedback: e.target.value, + })); + }); + }); describe('useFeedback hook', () => { beforeEach(() => { state.mock(); }); afterEach(() => { state.restore(); }); diff --git a/src/editors/data/redux/problem/reducers.js b/src/editors/data/redux/problem/reducers.js index 454f44725..60107f6e5 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 { ProblemTypeKeys, ShowAnswerTypesKeys } from '../../constants/problem'; +import { ShowAnswerTypesKeys } from '../../constants/problem'; const nextAlphaId = (lastId) => String.fromCharCode(lastId.charCodeAt(0) + 1); const initialState = { @@ -103,14 +103,11 @@ const problem = createSlice({ const newOption = { id: currAnswers.length ? nextAlphaId(currAnswers[currAnswers.length - 1].id) : 'A', title: '', - selectedFeedback: undefined, - unselectedFeedback: undefined, + selectedFeedback: '', + unselectedFeedback: '', correct: false, }; - if (state.problemType === ProblemTypeKeys.MULTISELECT) { - newOption.unselectedFeedback = ''; - } - newOption.selectedFeedback = ''; + const answers = [ ...currAnswers, newOption, diff --git a/src/editors/data/redux/problem/reducers.test.js b/src/editors/data/redux/problem/reducers.test.js index 737404097..a563821ef 100644 --- a/src/editors/data/redux/problem/reducers.test.js +++ b/src/editors/data/redux/problem/reducers.test.js @@ -26,6 +26,14 @@ describe('problem reducer', () => { [ ['updateQuestion', 'question'], ].map(args => setterTest(...args)); + describe('setEnableTypeSelection', () => { + it('sets given problemType to null', () => { + expect(reducer(testingState, actions.setEnableTypeSelection())).toEqual({ + ...testingState, + problemType: null, + }); + }); + }); describe('load', () => { it('sets answers', () => { const answer = { @@ -33,7 +41,7 @@ describe('problem reducer', () => { correct: false, selectedFeedback: '', title: '', - unselectedFeedback: undefined, + unselectedFeedback: '', }; expect(reducer(testingState, actions.addAnswer(answer))).toEqual({ ...testingState, @@ -99,7 +107,7 @@ describe('problem reducer', () => { }); describe('deleteAnswer', () => { it('sets answers, as well as setting the correctAnswerCount ', () => { - const answer = { id: 'A' }; + const answer = { id: 'A', correct: false }; expect(reducer( { ...testingState,