Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
Icon,
IconButton,
Form,
Spinner,
} from '@openedx/paragon';
import { FeedbackOutline, DeleteOutline } from '@openedx/paragon/icons';
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
Expand All @@ -19,6 +20,7 @@ import * as hooks from './hooks';
import { ProblemTypeKeys } from '../../../../../data/constants/problem';
import ExpandableTextArea from '../../../../../sharedComponents/ExpandableTextArea';
import { answerRangeFormatRegex } from '../../../data/OLXParser';
import { useValidateInputBlock } from '../../../data/apiHooks';

const AnswerOption = ({
answer,
Expand All @@ -32,7 +34,6 @@ const AnswerOption = ({
const isLibrary = useSelector(selectors.app.isLibrary);
const learningContextId = useSelector(selectors.app.learningContextId);
const blockId = useSelector(selectors.app.blockId);

const removeAnswer = hooks.removeAnswer({ answer, dispatch });
const setAnswer = hooks.setAnswer({ answer, hasSingleAnswer, dispatch });
const setAnswerTitle = hooks.setAnswerTitle({
Expand All @@ -44,6 +45,7 @@ const AnswerOption = ({
const setSelectedFeedback = hooks.setSelectedFeedback({ answer, hasSingleAnswer, dispatch });
const setUnselectedFeedback = hooks.setUnselectedFeedback({ answer, hasSingleAnswer, dispatch });
const { isFeedbackVisible, toggleFeedback } = hooks.useFeedback(answer);
const { data = { is_valid: true }, mutate, isPending } = useValidateInputBlock();

const staticRootUrl = isLibrary
? `${getConfig().STUDIO_BASE_URL}/library_assets/blocks/${blockId}/`
Expand Down Expand Up @@ -71,15 +73,29 @@ const AnswerOption = ({
}
if (problemType !== ProblemTypeKeys.NUMERIC || !answer.isAnswerRange) {
return (
<Form.Control
as="textarea"
className="answer-option-textarea text-gray-500 small"
autoResize
rows={1}
value={answer.title}
onChange={setAnswerTitle}
placeholder={intl.formatMessage(messages.answerTextboxPlaceholder)}
/>
<Form.Group isInvalid={!data?.is_valid ?? true}>
<Form.Control
as="textarea"
className="answer-option-textarea text-gray-500 small"
autoResize
rows={1}
value={answer.title}
onChange={(e) => {
setAnswerTitle(e);
mutate(e.target.value);
}}
placeholder={intl.formatMessage(messages.answerTextboxPlaceholder)}

/>
{(!data?.is_valid ?? true) && (
<Form.Control.Feedback type="invalid">
<FormattedMessage {...messages.answerNumericErrorText} />
</Form.Control.Feedback>
)}
{isPending && (
<Spinner animation="border" className="mie-3 mt-3" screenReaderText="loading" />
)}
</Form.Group>
);
}
// Return Answer Range View
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ const messages = defineMessages({
defaultMessage: 'Error: Invalid range format. Use brackets or parentheses with values separated by a comma.',
description: 'Error text describing wrong format of answer ranges',
},
answerNumericErrorText: {
id: 'authoring.answerwidget.answer.answerNumericErrorText',
defaultMessage: 'Error: This input type only supports numeric answers. Did you mean to make a Text input or Math expression input problem?',
description: 'Error message when user provides wrong format',
},
});

export default messages;
19 changes: 19 additions & 0 deletions src/editors/containers/ProblemEditor/data/apiHooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useMutation } from '@tanstack/react-query';
import { getConfig } from '@edx/frontend-platform';
import api from '@src/editors/data/services/cms/api';

const getApiBaseUrl = () => getConfig().STUDIO_BASE_URL;

export const useValidateInputBlock = () => useMutation({
mutationFn: async (title) => {
try {
const res = await api.validateBlockNumericInput({ studioEndpointUrl: `${getApiBaseUrl()}`, data: { formula: title } });
return res.data;
} catch (err: any) {
return {
is_valid: false,
error: err.response?.data?.error ?? 'Unknown error',
};
}
},
});
Binary file modified src/editors/data/images/numericalInput.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions src/editors/data/services/cms/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,13 @@ export const apiMethods = {
}) => get(
urls.handlerUrl({ studioEndpointUrl, blockId, handlerName }),
),
validateBlockNumericInput: ({
studioEndpointUrl,
data,
}) => post(
urls.validateNumericInputUrl({ studioEndpointUrl }),
data,
),
};

export default apiMethods;
4 changes: 4 additions & 0 deletions src/editors/data/services/cms/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,7 @@ export const courseVideos = (({ studioEndpointUrl, learningContextId }) => (
export const handlerUrl = (({ studioEndpointUrl, blockId, handlerName }) => (
`${studioEndpointUrl}/api/xblock/v2/xblocks/${blockId}/handler_url/${handlerName}/`
)) satisfies UrlFunction;

export const validateNumericInputUrl = (({ studioEndpointUrl }) => (
`${studioEndpointUrl}/api/contentstore/v2/validate/numerical-input/`
)) satisfies UrlFunction;