* feat: add external links override support * fix: add note, fix 404 url and fix unrelated typo * test: fix
401 lines
16 KiB
TypeScript
401 lines
16 KiB
TypeScript
import { getExternalLinkUrl } from '@edx/frontend-platform';
|
|
import { StrictDict } from '../../utils';
|
|
import singleSelect from '../images/singleSelect.png';
|
|
import multiSelect from '../images/multiSelect.png';
|
|
import dropdown from '../images/dropdown.png';
|
|
import numericalInput from '../images/numericalInput.png';
|
|
import textInput from '../images/textInput.png';
|
|
import advancedOlxTemplates from './advancedOlxTemplates';
|
|
import basicProblemTemplates from './basicProblemTemplates';
|
|
import problemMessages from '../../containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/messages';
|
|
|
|
export const ProblemTypeKeys = StrictDict({
|
|
SINGLESELECT: 'multiplechoiceresponse',
|
|
MULTISELECT: 'choiceresponse',
|
|
DROPDOWN: 'optionresponse',
|
|
NUMERIC: 'numericalresponse',
|
|
TEXTINPUT: 'stringresponse',
|
|
ADVANCED: 'advanced',
|
|
} as const);
|
|
export type ProblemType = typeof ProblemTypeKeys[keyof typeof ProblemTypeKeys];
|
|
|
|
/**
|
|
* Get problem types with internationalized strings.
|
|
* @param {Function} formatMessage - The intl.formatMessage function
|
|
* @returns {Object} ProblemTypes object with localized strings
|
|
*
|
|
* Usage in React components:
|
|
*
|
|
* import { useIntl } from '@edx/frontend-platform/i18n';
|
|
* import { getProblemTypes } from '../path/to/problem';
|
|
*
|
|
* const MyComponent = () => {
|
|
* const intl = useIntl();
|
|
* const localizedProblemTypes = getProblemTypes(intl.formatMessage);
|
|
*
|
|
* return <div>{localizedProblemTypes[ProblemTypeKeys.SINGLESELECT].title}</div>;
|
|
* };
|
|
*/
|
|
export const getProblemTypes = (formatMessage) => ({
|
|
[ProblemTypeKeys.SINGLESELECT]: {
|
|
title: formatMessage(problemMessages.singleSelectTitle),
|
|
preview: singleSelect,
|
|
previewDescription: formatMessage(problemMessages.singleSelectDescription),
|
|
description: formatMessage(problemMessages.singleSelectInstruction),
|
|
helpLink: getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/concepts/exercise_tools/about_multi_select.html'),
|
|
prev: ProblemTypeKeys.TEXTINPUT,
|
|
next: ProblemTypeKeys.MULTISELECT,
|
|
template: basicProblemTemplates.singleSelect.olx,
|
|
markdownTemplate: basicProblemTemplates.singleSelect.markdown,
|
|
},
|
|
[ProblemTypeKeys.MULTISELECT]: {
|
|
title: formatMessage(problemMessages.multiSelectTitle),
|
|
preview: multiSelect,
|
|
previewDescription: formatMessage(problemMessages.multiSelectDescription),
|
|
description: formatMessage(problemMessages.multiSelectInstruction),
|
|
helpLink: getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_multi_select.html'),
|
|
next: ProblemTypeKeys.DROPDOWN,
|
|
prev: ProblemTypeKeys.SINGLESELECT,
|
|
template: basicProblemTemplates.multiSelect.olx,
|
|
markdownTemplate: basicProblemTemplates.multiSelect.markdown,
|
|
},
|
|
[ProblemTypeKeys.DROPDOWN]: {
|
|
title: formatMessage(problemMessages.dropdownTitle),
|
|
preview: dropdown,
|
|
previewDescription: formatMessage(problemMessages.dropdownDescription),
|
|
description: formatMessage(problemMessages.dropdownInstruction),
|
|
helpLink: getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_dropdown.html'),
|
|
next: ProblemTypeKeys.NUMERIC,
|
|
prev: ProblemTypeKeys.MULTISELECT,
|
|
template: basicProblemTemplates.dropdown.olx,
|
|
markdownTemplate: basicProblemTemplates.dropdown.markdown,
|
|
},
|
|
[ProblemTypeKeys.NUMERIC]: {
|
|
title: formatMessage(problemMessages.numericalInputTitle),
|
|
preview: numericalInput,
|
|
previewDescription: formatMessage(problemMessages.numericalInputDescription),
|
|
description: formatMessage(problemMessages.numericalInputInstruction),
|
|
helpLink: getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/manage_numerical_input_problem.html'),
|
|
next: ProblemTypeKeys.TEXTINPUT,
|
|
prev: ProblemTypeKeys.DROPDOWN,
|
|
template: basicProblemTemplates.numeric.olx,
|
|
markdownTemplate: basicProblemTemplates.numeric.markdown,
|
|
},
|
|
[ProblemTypeKeys.TEXTINPUT]: {
|
|
title: formatMessage(problemMessages.textInputTitle),
|
|
preview: textInput,
|
|
previewDescription: formatMessage(problemMessages.textInputDescription),
|
|
description: formatMessage(problemMessages.textInputInstruction),
|
|
helpLink: getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_text_input.html'),
|
|
prev: ProblemTypeKeys.NUMERIC,
|
|
next: ProblemTypeKeys.SINGLESELECT,
|
|
template: basicProblemTemplates.textInput.olx,
|
|
markdownTemplate: basicProblemTemplates.textInput.markdown,
|
|
},
|
|
[ProblemTypeKeys.ADVANCED]: {
|
|
title: formatMessage(problemMessages.advancedProblemTitle),
|
|
preview: ('<div />'),
|
|
description: formatMessage(problemMessages.advancedProblemDescription),
|
|
helpLink: 'something.com',
|
|
},
|
|
});
|
|
|
|
export const ProblemTypes = StrictDict({
|
|
[ProblemTypeKeys.SINGLESELECT]: {
|
|
title: 'Single select',
|
|
preview: singleSelect,
|
|
previewDescription: 'Learners must select the correct answer from a list of possible options.',
|
|
description: 'Enter your single select answers below and select which choices are correct. Learners must choose one correct answer.',
|
|
helpLink: getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/concepts/exercise_tools/about_multi_select.html'),
|
|
prev: ProblemTypeKeys.TEXTINPUT,
|
|
next: ProblemTypeKeys.MULTISELECT,
|
|
template: basicProblemTemplates.singleSelect.olx,
|
|
markdownTemplate: basicProblemTemplates.singleSelect.markdown,
|
|
},
|
|
[ProblemTypeKeys.MULTISELECT]: {
|
|
title: 'Multi-select',
|
|
preview: multiSelect,
|
|
previewDescription: 'Learners must select all correct answers from a list of possible options.',
|
|
description: 'Enter your multi select answers below and select which choices are correct. Learners must choose all correct answers.',
|
|
helpLink: getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_multi_select.html'),
|
|
next: ProblemTypeKeys.DROPDOWN,
|
|
prev: ProblemTypeKeys.SINGLESELECT,
|
|
template: basicProblemTemplates.multiSelect.olx,
|
|
markdownTemplate: basicProblemTemplates.multiSelect.markdown,
|
|
},
|
|
[ProblemTypeKeys.DROPDOWN]: {
|
|
title: 'Dropdown',
|
|
preview: dropdown,
|
|
previewDescription: 'Learners must select the correct answer from a list of possible options',
|
|
description: 'Enter your dropdown answers below and select which choice is correct. Learners must select one correct answer.',
|
|
helpLink: getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_dropdown.html'),
|
|
next: ProblemTypeKeys.NUMERIC,
|
|
prev: ProblemTypeKeys.MULTISELECT,
|
|
template: basicProblemTemplates.dropdown.olx,
|
|
markdownTemplate: basicProblemTemplates.dropdown.markdown,
|
|
},
|
|
[ProblemTypeKeys.NUMERIC]: {
|
|
title: 'Numerical input',
|
|
preview: numericalInput,
|
|
previewDescription: 'Specify one or more correct numeric answers, submitted in a response field.',
|
|
description: 'Enter correct numerical input answers below. Learners must enter one correct answer.',
|
|
helpLink: getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/manage_numerical_input_problem.html'),
|
|
next: ProblemTypeKeys.TEXTINPUT,
|
|
prev: ProblemTypeKeys.DROPDOWN,
|
|
template: basicProblemTemplates.numeric.olx,
|
|
markdownTemplate: basicProblemTemplates.numeric.markdown,
|
|
},
|
|
[ProblemTypeKeys.TEXTINPUT]: {
|
|
title: 'Text input',
|
|
preview: textInput,
|
|
previewDescription: 'Specify one or more correct text answers, including numbers and special characters, submitted in a response field.',
|
|
description: 'Enter your text input answers below and select which choices are correct. Learners must enter one correct answer.',
|
|
helpLink: getExternalLinkUrl('https://docs.openedx.org/en/latest/educators/how-tos/course_development/exercise_tools/add_text_input.html'),
|
|
prev: ProblemTypeKeys.NUMERIC,
|
|
next: ProblemTypeKeys.SINGLESELECT,
|
|
template: basicProblemTemplates.textInput.olx,
|
|
markdownTemplate: basicProblemTemplates.textInput.markdown,
|
|
},
|
|
[ProblemTypeKeys.ADVANCED]: {
|
|
title: 'Advanced Problem',
|
|
preview: ('<div />'),
|
|
description: 'An Advanced Problem Type',
|
|
helpLink: 'something.com',
|
|
},
|
|
});
|
|
|
|
export const AdvanceProblemKeys = StrictDict({
|
|
BLANK: 'blankadvanced',
|
|
CIRCUITSCHEMATIC: 'circuitschematic',
|
|
JSINPUT: 'jsinputresponse',
|
|
CUSTOMGRADER: 'customgrader',
|
|
IMAGE: 'imageresponse',
|
|
FORMULA: 'formularesponse',
|
|
PROBLEMWITHHINT: 'problemwithhint',
|
|
} as const);
|
|
export type AdvancedProblemType = typeof AdvanceProblemKeys[keyof typeof AdvanceProblemKeys];
|
|
|
|
export function isAdvancedProblemType(pt: ProblemType | AdvancedProblemType): pt is AdvancedProblemType {
|
|
return Object.values(AdvanceProblemKeys).includes(pt as any);
|
|
}
|
|
|
|
/**
|
|
* Get advanced problem types with internationalized strings.
|
|
* @param {Function} formatMessage - The intl.formatMessage function
|
|
* @returns {Object} AdvanceProblems object with localized strings
|
|
*
|
|
* Usage in React components:
|
|
*
|
|
* import { useIntl } from '@edx/frontend-platform/i18n';
|
|
* import { getAdvanceProblems } from '../path/to/problem';
|
|
*
|
|
* const MyComponent = () => {
|
|
* const intl = useIntl();
|
|
* const localizedAdvanceProblems = getAdvanceProblems(intl.formatMessage);
|
|
*
|
|
* return <div>{localizedAdvanceProblems[AdvanceProblemKeys.BLANK].title}</div>;
|
|
* };
|
|
*/
|
|
export const getAdvanceProblems = (formatMessage) => ({
|
|
[AdvanceProblemKeys.BLANK]: {
|
|
title: formatMessage(problemMessages.blankProblemTitle),
|
|
status: '',
|
|
template: '<problem></problem>',
|
|
},
|
|
[AdvanceProblemKeys.CIRCUITSCHEMATIC]: {
|
|
title: formatMessage(problemMessages.circuitSchematicTitle),
|
|
status: 'Not supported',
|
|
template: advancedOlxTemplates.circuitSchematic,
|
|
},
|
|
[AdvanceProblemKeys.JSINPUT]: {
|
|
title: formatMessage(problemMessages.customJavaScriptTitle),
|
|
status: '',
|
|
template: advancedOlxTemplates.jsInputResponse,
|
|
},
|
|
[AdvanceProblemKeys.CUSTOMGRADER]: {
|
|
title: formatMessage(problemMessages.customPythonTitle),
|
|
status: 'Provisional',
|
|
template: advancedOlxTemplates.customGrader,
|
|
},
|
|
[AdvanceProblemKeys.IMAGE]: {
|
|
title: formatMessage(problemMessages.imageMappedTitle),
|
|
status: 'Not supported',
|
|
template: advancedOlxTemplates.imageResponse,
|
|
},
|
|
[AdvanceProblemKeys.FORMULA]: {
|
|
title: formatMessage(problemMessages.mathExpressionTitle),
|
|
status: '',
|
|
template: advancedOlxTemplates.formulaResponse,
|
|
},
|
|
[AdvanceProblemKeys.PROBLEMWITHHINT]: {
|
|
title: formatMessage(problemMessages.problemWithHintTitle),
|
|
status: 'Not supported',
|
|
template: advancedOlxTemplates.problemWithHint,
|
|
},
|
|
});
|
|
|
|
export const AdvanceProblems = StrictDict({
|
|
[AdvanceProblemKeys.BLANK]: {
|
|
title: 'Blank problem',
|
|
status: '',
|
|
template: '<problem></problem>',
|
|
},
|
|
[AdvanceProblemKeys.CIRCUITSCHEMATIC]: {
|
|
title: 'Circuit schematic builder',
|
|
status: 'Not supported',
|
|
template: advancedOlxTemplates.circuitSchematic,
|
|
},
|
|
[AdvanceProblemKeys.JSINPUT]: {
|
|
title: 'Custom JavaScript display and grading',
|
|
status: '',
|
|
template: advancedOlxTemplates.jsInputResponse,
|
|
},
|
|
[AdvanceProblemKeys.CUSTOMGRADER]: {
|
|
title: 'Custom Python-evaluated input',
|
|
status: 'Provisional',
|
|
template: advancedOlxTemplates.customGrader,
|
|
},
|
|
[AdvanceProblemKeys.IMAGE]: {
|
|
title: 'Image mapped input',
|
|
status: 'Not supported',
|
|
template: advancedOlxTemplates.imageResponse,
|
|
},
|
|
[AdvanceProblemKeys.FORMULA]: {
|
|
title: 'Math expression input',
|
|
status: '',
|
|
template: advancedOlxTemplates.formulaResponse,
|
|
},
|
|
[AdvanceProblemKeys.PROBLEMWITHHINT]: {
|
|
title: 'Problem with adaptive hint',
|
|
status: 'Not supported',
|
|
template: advancedOlxTemplates.problemWithHint,
|
|
},
|
|
} as const);
|
|
|
|
export const ShowAnswerTypesKeys = StrictDict({
|
|
ALWAYS: 'always',
|
|
ANSWERED: 'answered',
|
|
ATTEMPTED: 'attempted',
|
|
CLOSED: 'closed',
|
|
FINISHED: 'finished',
|
|
CORRECT_OR_PAST_DUE: 'correct_or_past_due',
|
|
PAST_DUE: 'past_due',
|
|
NEVER: 'never',
|
|
AFTER_SOME_NUMBER_OF_ATTEMPTS: 'after_attempts',
|
|
AFTER_ALL_ATTEMPTS: 'after_all_attempts',
|
|
AFTER_ALL_ATTEMPTS_OR_CORRECT: 'after_all_attempts_or_correct',
|
|
ATTEMPTED_NO_PAST_DUE: 'attempted_no_past_due',
|
|
} as const);
|
|
|
|
export const ShowAnswerTypes = StrictDict({
|
|
[ShowAnswerTypesKeys.ALWAYS]: {
|
|
id: 'authoring.problemeditor.settings.showanswertype.always',
|
|
defaultMessage: 'Always',
|
|
},
|
|
[ShowAnswerTypesKeys.ANSWERED]: {
|
|
id: 'authoring.problemeditor.settings.showanswertype.answered',
|
|
defaultMessage: 'Answered',
|
|
},
|
|
[ShowAnswerTypesKeys.ATTEMPTED]: {
|
|
id: 'authoring.problemeditor.settings.showanswertype.attempted',
|
|
defaultMessage: 'Attempted or Past Due',
|
|
},
|
|
[ShowAnswerTypesKeys.CLOSED]: {
|
|
id: 'authoring.problemeditor.settings.showanswertype.closed',
|
|
defaultMessage: 'Closed',
|
|
},
|
|
[ShowAnswerTypesKeys.FINISHED]: {
|
|
id: 'authoring.problemeditor.settings.showanswertype.finished',
|
|
defaultMessage: 'Finished',
|
|
},
|
|
[ShowAnswerTypesKeys.CORRECT_OR_PAST_DUE]: {
|
|
id: 'authoring.problemeditor.settings.showanswertype.correct_or_past_due',
|
|
defaultMessage: 'Correct or Past Due',
|
|
},
|
|
[ShowAnswerTypesKeys.PAST_DUE]: {
|
|
id: 'authoring.problemeditor.settings.showanswertype.past_due',
|
|
defaultMessage: 'Past Due',
|
|
},
|
|
[ShowAnswerTypesKeys.NEVER]: {
|
|
id: 'authoring.problemeditor.settings.showanswertype.never',
|
|
defaultMessage: 'Never',
|
|
},
|
|
[ShowAnswerTypesKeys.AFTER_SOME_NUMBER_OF_ATTEMPTS]: {
|
|
id: 'authoring.problemeditor.settings.showanswertype.after_attempts',
|
|
defaultMessage: 'After Some Number of Attempts',
|
|
},
|
|
[ShowAnswerTypesKeys.AFTER_ALL_ATTEMPTS]: {
|
|
id: 'authoring.problemeditor.settings.showanswertype.after_all_attempts',
|
|
defaultMessage: 'After All Attempts',
|
|
},
|
|
[ShowAnswerTypesKeys.AFTER_ALL_ATTEMPTS_OR_CORRECT]: {
|
|
id: 'authoring.problemeditor.settings.showanswertype.after_all_attempts_or_correct',
|
|
defaultMessage: 'After All Attempts or Correct',
|
|
},
|
|
[ShowAnswerTypesKeys.ATTEMPTED_NO_PAST_DUE]: {
|
|
id: 'authoring.problemeditor.settings.showanswertype.attempted_no_past_due',
|
|
defaultMessage: 'Attempted',
|
|
},
|
|
} as const);
|
|
|
|
export const RandomizationTypesKeys = StrictDict({
|
|
NEVER: 'never',
|
|
ALWAYS: 'always',
|
|
ONRESET: 'onreset',
|
|
PERSTUDENT: 'per_student',
|
|
} as const);
|
|
|
|
export const RandomizationTypes = StrictDict({
|
|
[RandomizationTypesKeys.ALWAYS]: {
|
|
id: 'authoring.problemeditor.settings.RandomizationTypes.always',
|
|
defaultMessage: 'Always',
|
|
},
|
|
[RandomizationTypesKeys.NEVER]: {
|
|
id: 'authoring.problemeditor.settings.RandomizationTypes.never',
|
|
defaultMessage: 'Never',
|
|
},
|
|
[RandomizationTypesKeys.ONRESET]: {
|
|
id: 'authoring.problemeditor.settings.RandomizationTypes.onreset',
|
|
defaultMessage: 'On Reset',
|
|
},
|
|
[RandomizationTypesKeys.PERSTUDENT]: {
|
|
id: 'authoring.problemeditor.settings.RandomizationTypes.perstudent',
|
|
defaultMessage: 'Per Student',
|
|
},
|
|
} as const);
|
|
|
|
export const RichTextProblems = [ProblemTypeKeys.SINGLESELECT, ProblemTypeKeys.MULTISELECT] as const;
|
|
|
|
export const settingsOlxAttributes = [
|
|
'@_display_name',
|
|
'@_weight',
|
|
'@_max_attempts',
|
|
'@_showanswer',
|
|
'@_show_reset_button',
|
|
'@_submission_wait_seconds',
|
|
'@_attempts_before_showanswer_button',
|
|
] as const;
|
|
|
|
export const ignoredOlxAttributes = [
|
|
'@_markdown',
|
|
'@_url_name',
|
|
'@_x-is-pointer-node',
|
|
'@_markdown_edited',
|
|
'@_copied_from_block',
|
|
'@_copied_from_version',
|
|
] as const;
|
|
|
|
// Useful for the block creation workflow.
|
|
export const problemTitles = new Set([...Object.values(ProblemTypes).map((problem) => problem.title),
|
|
...Object.values(AdvanceProblems).map((problem) => problem.title)]);
|
|
|
|
/**
|
|
* Get problem titles with internationalization support
|
|
* @param {Function} formatMessage - The intl.formatMessage function
|
|
* @returns {Set<string>} Set of localized problem titles
|
|
*/
|
|
export const getProblemTitles = (formatMessage) => new Set([
|
|
...Object.values(getProblemTypes(formatMessage)).map((problem) => problem.title),
|
|
...Object.values(getAdvanceProblems(formatMessage)).map((problem) => problem.title),
|
|
]);
|