From 23aa3977eec9cb791bd29ba2926d3745591b049e Mon Sep 17 00:00:00 2001 From: ihor-romaniuk Date: Mon, 9 Dec 2024 16:13:11 +0100 Subject: [PATCH] fix: answer range format validation and error message in Numerical input --- .../AnswerWidget/AnswerOption.jsx | 17 ++- .../AnswerWidget/AnswerOption.test.jsx | 5 +- .../__snapshots__/AnswerOption.test.jsx.snap | 118 +++++++++++++++++- .../EditProblemView/AnswerWidget/messages.js | 5 + .../ProblemEditor/data/OLXParser.js | 4 +- .../ProblemEditor/data/ReactStateOLXParser.js | 8 +- 6 files changed, 144 insertions(+), 13 deletions(-) diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx index 57012064ac..825001bbaf 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx @@ -17,6 +17,7 @@ import { FeedbackBox } from './components/Feedback'; import * as hooks from './hooks'; import { ProblemTypeKeys } from '../../../../../data/constants/problem'; import ExpandableTextArea from '../../../../../sharedComponents/ExpandableTextArea'; +import { answerRangeFormatRegex } from '../../../data/OLXParser'; const AnswerOption = ({ answer, @@ -42,6 +43,11 @@ const AnswerOption = ({ const setUnselectedFeedback = hooks.setUnselectedFeedback({ answer, hasSingleAnswer, dispatch }); const { isFeedbackVisible, toggleFeedback } = hooks.useFeedback(answer); + const validateAnswerTitle = (value) => { + const cleanedValue = value.replace(/^\s+|\s+$/g, ''); + return !cleanedValue.length || answerRangeFormatRegex.test(cleanedValue); + }; + const getInputArea = () => { if ([ProblemTypeKeys.SINGLESELECT, ProblemTypeKeys.MULTISELECT].includes(problemType)) { return ( @@ -72,8 +78,9 @@ const AnswerOption = ({ ); } // Return Answer Range View + const isValidValue = validateAnswerTitle(answer.title); return ( -
+ + {!isValidValue && ( + + + + )}
-
- + ); }; 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 59c05a67e5..d2375a3f38 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.test.jsx @@ -41,7 +41,7 @@ describe('AnswerOption', () => { }; const answerRange = { id: 'A', - title: 'Answer 1', + title: '[2,5]', correct: true, selectedFeedback: 'selected feedback', unselectedFeedback: 'unselected feedback', @@ -72,6 +72,9 @@ describe('AnswerOption', () => { test('snapshot: renders correct option with numeric input problem and answer range', () => { expect(shallow().snapshot).toMatchSnapshot(); }); + test('snapshot: renders incorrect option with numeric input problem and answer range', () => { + expect(shallow().snapshot).toMatchSnapshot(); + }); }); describe('mapStateToProps', () => { 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 d1443d903a..7d3d981ca4 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 @@ -178,7 +178,7 @@ exports[`AnswerOption render snapshot: renders correct option with numeric input "id": "A", "isAnswerRange": true, "selectedFeedback": "selected feedback", - "title": "Answer 1", + "title": "[2,5]", "unselectedFeedback": "unselected feedback", } } @@ -190,7 +190,9 @@ exports[`AnswerOption render snapshot: renders correct option with numeric input
-
+
-
+ `; + +exports[`AnswerOption render snapshot: renders incorrect option with numeric input problem and answer range 1`] = ` + +
+ +
+
+ + + + + +
+ +
+
+ + + +
+
+ + + + +
+
+`; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/messages.js b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/messages.js index bd2358fc7f..25b21a030a 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/messages.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/messages.js @@ -72,6 +72,11 @@ const messages = defineMessages({ defaultMessage: 'Enter min and max values separated by a comma. Use a bracket to include the number next to it in the range, or a parenthesis to exclude the number. For example, to identify the correct answers as 5, 6, or 7, but not 8, specify [5,8).', description: 'Helper text describing usage of answer ranges', }, + answerRangeErrorText: { + id: 'authoring.answerwidget.answer.answerRangeErrorText', + defaultMessage: 'Error: Invalid range format. Use brackets or parentheses with values separated by a comma.', + description: 'Error text describing wrong format of answer ranges', + }, }); export default messages; diff --git a/src/editors/containers/ProblemEditor/data/OLXParser.js b/src/editors/containers/ProblemEditor/data/OLXParser.js index cbdb163c53..d9302226be 100644 --- a/src/editors/containers/ProblemEditor/data/OLXParser.js +++ b/src/editors/containers/ProblemEditor/data/OLXParser.js @@ -55,6 +55,8 @@ export const responseKeys = [ 'choicetextresponse', ]; +export const answerRangeFormatRegex = /^[([]\s*\d+(\.\d+)?\s*,\s*\d+(\.\d+)?\s*[)\]]$/m; + export const stripNonTextTags = ({ input, tag }) => { const stripedTags = {}; Object.entries(input).forEach(([key, value]) => { @@ -423,7 +425,7 @@ export class OLXParser { [type]: defaultValue, }; } - const isAnswerRange = /[([]\s*\d*,\s*\d*\s*[)\]]/gm.test(numericalresponse['@_answer']); + const isAnswerRange = answerRangeFormatRegex.test(numericalresponse['@_answer']); answers.push({ id: indexToLetterMap[answers.length], title: numericalresponse['@_answer'], diff --git a/src/editors/containers/ProblemEditor/data/ReactStateOLXParser.js b/src/editors/containers/ProblemEditor/data/ReactStateOLXParser.js index 673763de61..d8c02933b0 100644 --- a/src/editors/containers/ProblemEditor/data/ReactStateOLXParser.js +++ b/src/editors/containers/ProblemEditor/data/ReactStateOLXParser.js @@ -413,16 +413,18 @@ class ReactStateOLXParser { const lowerBoundFloat = Number(numerator) / Number(denominator); lowerBoundInt = lowerBoundFloat; } else { - // these regex replaces remove everything that is not a decimal or positive/negative numer + // these regex replaces remove everything that is not a decimal or positive/negative number lowerBoundInt = Number(rawLowerBound.replace(/[^0-9-.]/gm, '')); } - if (rawUpperBound.includes('/')) { + if (!rawUpperBound) { + upperBoundInt = lowerBoundInt; + } else if (rawUpperBound.includes('/')) { upperBoundFraction = rawUpperBound.replace(/[^0-9-/]/gm, ''); const [numerator, denominator] = upperBoundFraction.split('/'); const upperBoundFloat = Number(numerator) / Number(denominator); upperBoundInt = upperBoundFloat; } else { - // these regex replaces remove everything that is not a decimal or positive/negative numer + // these regex replaces remove everything that is not a decimal or positive/negative number upperBoundInt = Number(rawUpperBound.replace(/[^0-9-.]/gm, '')); } if (lowerBoundInt > upperBoundInt) {