diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx
index bac86c98a..a829f1b8e 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';
export const AnswerOption = ({
answer,
@@ -39,6 +40,11 @@ export 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 (
@@ -64,8 +70,9 @@ export 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 862b3b214..84d195832 100644
--- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.test.jsx
+++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.test.jsx
@@ -34,7 +34,7 @@ describe('AnswerOption', () => {
};
const answerRange = {
id: 'A',
- title: 'Answer 1',
+ title: '[2,5]',
correct: true,
selectedFeedback: 'selected feedback',
unselectedFeedback: 'unselected feedback',
@@ -62,6 +62,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 171a2ab10..04bf74504 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
@@ -169,7 +169,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",
}
}
@@ -181,7 +181,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 bd2358fc7..25b21a030 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 59e07ff69..5ea030cf5 100644
--- a/src/editors/containers/ProblemEditor/data/OLXParser.js
+++ b/src/editors/containers/ProblemEditor/data/OLXParser.js
@@ -50,6 +50,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]) => {
@@ -418,7 +420,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 f320cdd82..13cc62246 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) {