Skip to content

Commit c24cb0d

Browse files
feat(validation): validation added to numeric input with new endpoint to see if is a valid math expression
1 parent 14d2c72 commit c24cb0d

12 files changed

Lines changed: 75 additions & 12 deletions

File tree

src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { FeedbackBox } from './components/Feedback';
1818
import * as hooks from './hooks';
1919
import { ProblemTypeKeys } from '../../../../../data/constants/problem';
2020
import ExpandableTextArea from '../../../../../sharedComponents/ExpandableTextArea';
21-
import { answerRangeFormatRegex, numericRegex } from '../../../data/OLXParser';
21+
import { answerRangeFormatRegex } from '../../../data/OLXParser';
2222

2323
const AnswerOption = ({
2424
answer,
@@ -28,6 +28,7 @@ const AnswerOption = ({
2828
const dispatch = useDispatch();
2929

3030
const problemType = useSelector(selectors.problem.problemType);
31+
const isNumericInputValid = useSelector(selectors.problem.isNumericInputValid);
3132
const images = useSelector(selectors.app.images);
3233
const isLibrary = useSelector(selectors.app.isLibrary);
3334
const learningContextId = useSelector(selectors.app.learningContextId);
@@ -53,10 +54,6 @@ const AnswerOption = ({
5354
const cleanedValue = value.replace(/^\s+|\s+$/g, '');
5455
return !cleanedValue.length || answerRangeFormatRegex.test(cleanedValue);
5556
};
56-
const validateAnswerNumeric = (value) => {
57-
const cleanedValue = (value ?? '').trim();
58-
return !cleanedValue.length || numericRegex.test(cleanedValue);
59-
};
6057

6158
const getInputArea = () => {
6259
if ([ProblemTypeKeys.SINGLESELECT, ProblemTypeKeys.MULTISELECT].includes(problemType)) {
@@ -74,9 +71,8 @@ const AnswerOption = ({
7471
);
7572
}
7673
if (problemType !== ProblemTypeKeys.NUMERIC || !answer.isAnswerRange) {
77-
const isValidValue = validateAnswerNumeric(answer.title);
7874
return (
79-
<Form.Group isInvalid={!isValidValue}>
75+
<Form.Group isInvalid={!isNumericInputValid}>
8076
<Form.Control
8177
as="textarea"
8278
className="answer-option-textarea text-gray-500 small"
@@ -87,7 +83,7 @@ const AnswerOption = ({
8783
placeholder={intl.formatMessage(messages.answerTextboxPlaceholder)}
8884

8985
/>
90-
{!isValidValue && (
86+
{!isNumericInputValid && (
9187
<Form.Control.Feedback type="invalid">
9288
<FormattedMessage {...messages.answerNumericErrorText} />
9389
</Form.Control.Feedback>

src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/hooks.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { StrictDict } from '../../../../../utils';
55
// should be re-thought and cleaned up to avoid this pattern.
66
// eslint-disable-next-line import/no-self-import
77
import * as module from './hooks';
8-
import { actions } from '../../../../../data/redux';
8+
import { actions, thunkActions } from '../../../../../data/redux';
99
import { ProblemTypeKeys } from '../../../../../data/constants/problem';
1010
import { fetchEditorContent } from '../hooks';
1111

@@ -29,6 +29,17 @@ export const setAnswer = ({ answer, hasSingleAnswer, dispatch }) => (payload) =>
2929
dispatch(actions.problem.updateAnswer({ id: answer.id, hasSingleAnswer, ...payload }));
3030
};
3131

32+
export const validateInputBlock = ({
33+
title, dispatch,
34+
}) => {
35+
if (!title) {
36+
return;
37+
}
38+
dispatch(thunkActions.problem.validateBlockNumericInput({
39+
title,
40+
}));
41+
};
42+
3243
export const setAnswerTitle = ({
3344
answer,
3445
hasSingleAnswer,
@@ -43,6 +54,11 @@ export const setAnswerTitle = ({
4354
if (isDirty !== undefined) {
4455
dispatch(actions.problem.setDirty(isDirty));
4556
}
57+
58+
// For numeric problems, validate input on title change
59+
if (problemType === ProblemTypeKeys.NUMERIC) {
60+
validateInputBlock({ title, dispatch });
61+
}
4662
};
4763

4864
export const setSelectedFeedback = ({ answer, hasSingleAnswer, dispatch }) => (value) => {
@@ -106,5 +122,12 @@ export const useAnswerContainer = ({ answers, updateField }) => {
106122
};
107123

108124
export default {
109-
state, removeAnswer, setAnswer, setAnswerTitle, useFeedback, isSingleAnswerProblem, useAnswerContainer,
125+
state,
126+
removeAnswer,
127+
setAnswer,
128+
setAnswerTitle,
129+
useFeedback,
130+
isSingleAnswerProblem,
131+
useAnswerContainer,
132+
validateInputBlock,
110133
};

src/editors/containers/ProblemEditor/data/OLXParser.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ export const responseKeys = [
9696
* []
9797
*/
9898
export const answerRangeFormatRegex = /^[([]\s*-?(?:\d+(?:\.\d+)?|\d+\/\d+)\s*,\s*-?(?:\d+(?:\.\d+)?|\d+\/\d+)\s*[)\]]$/m;
99-
export const numericRegex = /^[+-]?(\d+(\.\d*)?|\.\d+)$/;
10099
export const stripNonTextTags = ({ input, tag }) => {
101100
const stripedTags = {};
102101
Object.entries(input).forEach(([key, value]) => {

src/editors/data/constants/requests.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,5 @@ export const RequestKeys = StrictDict({
3030
fetchAdvancedSettings: 'fetchAdvancedSettings',
3131
fetchVideoFeatures: 'fetchVideoFeatures',
3232
getHandlerUrl: 'getHandlerUrl',
33+
validateBlockNumericInput: 'validateBlockNumericInput',
3334
} as const);
-4.5 KB
Loading

src/editors/data/redux/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ export interface EditorState {
157157
rawOLX: string;
158158
rawMarkdown: string;
159159
problemType: null | ProblemType | AdvancedProblemType;
160+
isNumericInputValid: boolean;
160161
/**
161162
* Is the "markdown" editor currently active (as opposed to visual or advanced editors)
162163
* This is confusingly named, and different from `isMarkdownEditorEnabledForContext`

src/editors/data/redux/problem/reducers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const initialState: EditorState['problem'] = {
1313
rawMarkdown: '',
1414
isMarkdownEditorEnabled: false,
1515
problemType: null,
16+
isNumericInputValid: true,
1617
question: '',
1718
answers: [],
1819
correctAnswerCount: 0,

src/editors/data/redux/problem/selectors.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export const simpleSelectors = {
1818
defaultSettings: mkSimpleSelector(problemData => problemData.defaultSettings),
1919
completeState: mkSimpleSelector(problemData => problemData),
2020
isDirty: mkSimpleSelector(problemData => problemData.isDirty),
21+
isNumericInputValid: mkSimpleSelector(problemData => problemData.isNumericInputValid),
2122
};
2223

2324
export default simpleSelectors;

src/editors/data/redux/thunkActions/problem.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,19 @@ export const initializeProblem = (blockValue) => (dispatch, getState) => {
138138
}
139139
};
140140

141+
export const validateBlockNumericInput = ({ title, ...rest }) => (dispatch) => {
142+
dispatch(requests.validateNumericInput({
143+
title,
144+
...rest,
145+
onSuccess: (response) => {
146+
dispatch(actions.problem.updateField({ isNumericInputValid: response.data.is_valid }));
147+
},
148+
onFailure: () => {
149+
dispatch(actions.problem.updateField({ isNumericInputValid: false }));
150+
},
151+
}));
152+
};
153+
141154
export default {
142-
initializeProblem, switchEditor, switchToAdvancedEditor, fetchAdvancedSettings,
155+
initializeProblem, switchEditor, switchToAdvancedEditor, fetchAdvancedSettings, validateBlockNumericInput,
143156
};

src/editors/data/redux/thunkActions/requests.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,21 @@ export const uploadVideo = ({ data, ...rest }) => (dispatch, getState) => {
485485
}));
486486
};
487487

488+
export const validateNumericInput = ({ title, ...rest }) => (dispatch, getState) => {
489+
dispatch(module.networkRequest({
490+
requestKey: RequestKeys.validateBlockNumericInput,
491+
promise: api.validateBlockNumericInput({
492+
blockId: selectors.app.blockId(getState()),
493+
blockType: selectors.app.blockType(getState()),
494+
learningContextId: selectors.app.learningContextId(getState()),
495+
data: { formula: title },
496+
studioEndpointUrl: selectors.app.studioEndpointUrl(getState()),
497+
title: selectors.app.blockTitle(getState()),
498+
}),
499+
...rest,
500+
}));
501+
};
502+
488503
export default StrictDict({
489504
fetchBlock,
490505
fetchStudioView,
@@ -507,4 +522,5 @@ export default StrictDict({
507522
fetchVideoFeatures,
508523
uploadVideo,
509524
getHandlerlUrl,
525+
validateNumericInput,
510526
});

0 commit comments

Comments
 (0)