fix: separate feedback onchange for each field (#214)

This commit is contained in:
Kristin Aoki
2023-01-26 09:48:43 -05:00
committed by GitHub
parent 83acc741f5
commit afec2865c0
9 changed files with 106 additions and 21 deletions

View File

@@ -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 = ({
<FeedbackBox
problemType={problemType}
answer={answer}
setAnswer={setAnswer}
setSelectedFeedback={setSelectedFeedback}
setUnselectedFeedback={setUnselectedFeedback}
intl={intl}
/>
</Collapsible.Body>

View File

@@ -50,7 +50,8 @@ exports[`AnswerOption render snapshot: renders correct option with feedback 1`]
}
}
problemType="multiplechoiceresponse"
setAnswer={[Function]}
setSelectedFeedback={[Function]}
setUnselectedFeedback={[Function]}
/>
</Body>
</div>
@@ -126,7 +127,8 @@ exports[`AnswerOption render snapshot: renders correct option with selected unse
}
}
problemType="choiceresponse"
setAnswer={[Function]}
setSelectedFeedback={[Function]}
setUnselectedFeedback={[Function]}
/>
</Body>
</div>

View File

@@ -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}
/>
<FeedbackControl
@@ -29,6 +32,7 @@ export const FeedbackBox = ({
feedback={answer.unselectedFeedback}
labelMessage={messages.unSelectedFeedbackLabel}
labelMessageBoldUnderline={messages.unSelectedFeedbackLabelBoldUnderlineText}
onChange={setUnselectedFeedback}
{...props}
/>
</div>
@@ -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,
};

View File

@@ -12,7 +12,6 @@ const answerWithFeedback = {
const props = {
answer: answerWithFeedback,
intl: {},
setAnswer: jest.fn(),
};
describe('FeedbackBox component', () => {

View File

@@ -31,7 +31,6 @@ exports[`FeedbackBox component renders 1`] = `
"id": "authoring.answerwidget.feedback.selected.label.boldunderline",
}
}
onChange={[Function]}
/>
<FeedbackControl
answer={
@@ -60,7 +59,6 @@ exports[`FeedbackBox component renders 1`] = `
"id": "authoring.answerwidget.feedback.unselected.label.boldunderline",
}
}
onChange={[Function]}
/>
</div>
`;

View File

@@ -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;
}

View File

@@ -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(); });

View File

@@ -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,

View File

@@ -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,