diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/TypeCard.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/TypeCard.test.jsx.snap index a110e60d1..251289f10 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/TypeCard.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/TypeCard.test.jsx.snap @@ -6,27 +6,11 @@ exports[`TypeCard snapshot snapshot: renders type setting card 1`] = ` title="Type" > - - + + + diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx index 604760210..5600f9e3a 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx @@ -10,6 +10,7 @@ import { EditorContainer } from '../../../EditorContainer'; import { selectors } from '../../../../data/redux'; import ReactStateSettingsParser from '../../data/ReactStateSettingsParser'; import ReactStateOLXParser from '../../data/ReactStateOLXParser'; +import { AdvanceProblemKeys } from '../../../../data/constants/problem'; export const EditProblemView = ({ problemType, @@ -23,6 +24,9 @@ export const EditProblemView = ({ olx: reactOLXParser.buildOLX(), }; }; + if (Object.values(AdvanceProblemKeys).includes(problemType)) { + return `hello raw editor with ${problemType}`; + } return ( diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.jsx index 8ecd629c4..e7f279fe7 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.jsx @@ -7,33 +7,38 @@ import { Button, ModalDialog, } from '@edx/paragon'; -import { FormattedMessage, injectIntl } from '@edx/frontend-platform/i18n'; +import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; +import messages from './messages'; +import hooks from '../hooks'; import { actions } from '../../../../../data/redux'; export const SelectTypeFooter = ({ - selected, onCancel, - // Redux - onSelect, + selected, + // redux + setProblemType, + updateField, + // injected, + intl, }) => ( -
+
@@ -47,14 +52,18 @@ SelectTypeFooter.defaultProps = { SelectTypeFooter.propTypes = { onCancel: PropTypes.func.isRequired, selected: PropTypes.string, - onSelect: PropTypes.func.isRequired, + setProblemType: PropTypes.func.isRequired, + updateField: PropTypes.func.isRequired, + // injected + intl: intlShape.isRequired, }; export const mapStateToProps = () => ({ }); export const mapDispatchToProps = { - initializeEditor: actions.problem.onSelect, + setProblemType: actions.problem.setProblemType, + updateField: actions.problem.updateField, }; export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(SelectTypeFooter)); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.test.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.test.jsx new file mode 100644 index 000000000..9d22bbf52 --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.test.jsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { shallow } from 'enzyme'; + +import { Button } from '@edx/paragon'; +import { formatMessage } from '../../../../../../testUtils'; +import * as module from './SelectTypeFooter'; +import hooks from '../hooks'; +import { actions } from '../../../../../data/redux'; + +jest.mock('../hooks', () => ({ + onSelect: jest.fn().mockName('onSelect'), +})); + +describe('SelectTypeFooter', () => { + const props = { + onCancel: jest.fn().mockName('onCancel'), + selected: null, + // redux + setProblemType: jest.fn().mockName('setProblemType'), + // inject + intl: { formatMessage }, + }; + + test('snapshot', () => { + expect(shallow()).toMatchSnapshot(); + }); + + describe('behavior', () => { + let el; + beforeEach(() => { + el = shallow(); + }); + test('close behavior is linked to modal onCancel', () => { + const expected = props.onCancel; + expect(el.find(Button).first().props().onClick) + .toEqual(expected); + }); + test('select behavior is linked to modal onSelect', () => { + const expected = hooks.onSelect(props.setProblemType, props.selected); + expect(el.find(Button).last().props().onClick) + .toEqual(expected); + }); + }); + + describe('mapStateToProps', () => { + test('is empty', () => { + expect(module.mapStateToProps()).toEqual({}); + }); + }); + describe('mapDispatchToProps', () => { + test('loads setProblemType from problem.setProblemType actions', () => { + expect(module.mapDispatchToProps.setProblemType).toEqual(actions.problem.setProblemType); + }); + }); +}); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/__snapshots__/SelectTypeFooter.test.jsx.snap b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/__snapshots__/SelectTypeFooter.test.jsx.snap new file mode 100644 index 000000000..760a9d35c --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/__snapshots__/SelectTypeFooter.test.jsx.snap @@ -0,0 +1,41 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SelectTypeFooter snapshot 1`] = ` +
+ + + + + + + +
+`; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/__snapshots__/index.test.jsx.snap b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/__snapshots__/index.test.jsx.snap new file mode 100644 index 000000000..d88ab2baf --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/__snapshots__/index.test.jsx.snap @@ -0,0 +1,29 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SelectTypeWrapper snapshot 1`] = ` +
+ + +

+ Select Problem type +

+
+ +
+
+
+ +

+ test child +

+
+ +
+`; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.jsx index 4e2b23aa1..b8c63786d 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.jsx @@ -8,9 +8,9 @@ import SelectTypeFooter from './SelectTypeFooter'; import * as hooks from '../../../../EditorContainer/hooks'; export const SelectTypeWrapper = ({ - selected, - onClose, children, + onClose, + selected, }) => { const handleCancelClicked = hooks.handleCancelClicked({ onClose }); @@ -28,7 +28,9 @@ export const SelectTypeWrapper = ({
- {children} + + {children} + ({ + handleCancelClicked: jest.fn().mockName('handleCancelClicked'), +})); + +describe('SelectTypeWrapper', () => { + const props = { + children: (

test child

), + onClose: jest.fn(), + selected: 'iMAsElecTedValUE', + }; + + test('snapshot', () => { + expect(shallow()).toMatchSnapshot(); + }); + + describe('behavior', () => { + let el; + beforeEach(() => { + el = shallow(); + }); + test('close behavior is linked to modal onClose', () => { + const expected = handleCancelClicked({ onClose: props.onClose }); + expect(el.find(IconButton).props().onClick) + .toEqual(expected); + }); + }); +}); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/messages.js b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/messages.js new file mode 100644 index 000000000..e4e34350f --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/messages.js @@ -0,0 +1,24 @@ +export const messages = { + cancelButtonLabel: { + id: 'authoring.problemeditor.selecttype.cancelButton.label', + defaultMessage: 'Cancel', + description: 'Label for cancel button.', + }, + cancelButtonAriaLabel: { + id: 'authoring.problemeditor.selecttype.cancelButton.ariaLabel', + defaultMessage: 'Cancel', + description: 'Screen reader label for cancel button.', + }, + selectButtonLabel: { + id: 'authoring.problemeditor.selecttype.selectButton.label', + defaultMessage: 'Select', + description: 'Label for select button.', + }, + selectButtonAriaLabel: { + id: 'authoring.problemeditor.selecttype.selectButton.ariaLabel', + defaultMessage: 'Select', + description: 'Screen reader label for select button.', + }, +}; + +export default messages; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/__snapshots__/index.test.jsx.snap b/src/editors/containers/ProblemEditor/components/SelectTypeModal/__snapshots__/index.test.jsx.snap new file mode 100644 index 000000000..c111eeb42 --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/__snapshots__/index.test.jsx.snap @@ -0,0 +1,24 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SelectTypeModal snapshot 1`] = ` + + + + + + + + + + +`; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.jsx new file mode 100644 index 000000000..2ecfa2568 --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.jsx @@ -0,0 +1,92 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import { + Form, + ActionRow, + IconButton, + Icon, + OverlayTrigger, + Tooltip, +} from '@edx/paragon'; +import { ArrowBack } from '@edx/paragon/icons'; +import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; +import { AdvanceProblems, ProblemTypeKeys } from '../../../../../data/constants/problem'; +import messages from './messages'; + +export const AdvanceTypeSelect = ({ + selected, + setSelected, + // injected + intl, +}) => { + const handleChange = e => { setSelected(e.target.value); }; + return ( +
+ + + setSelected(ProblemTypeKeys.SINGLESELECT)} /> + + + + + + + + {Object.entries(AdvanceProblems).map(([type, data]) => { + if (data.status !== '') { + return ( + + + {intl.formatMessage(messages.advanceProblemTypeLabel, { problemType: data.title })} + + + +
+ {intl.formatMessage(messages.supportStatusTooltipMessage, { supportStatus: data.status.replace(' ', '_') })} +
+ + )} + > +
+ {intl.formatMessage(messages.problemSupportStatus, { supportStatus: data.status })} +
+
+
+ ); + } + return ( + + + {intl.formatMessage(messages.advanceProblemTypeLabel, { problemType: data.title })} + + + + ); + })} +
+
+
+ ); +}; + +AdvanceTypeSelect.defaultProps = { + selected: null, +}; + +AdvanceTypeSelect.propTypes = { + selected: PropTypes.string, + setSelected: PropTypes.func.isRequired, + // injected + intl: intlShape.isRequired, +}; + +export default injectIntl(AdvanceTypeSelect); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.test.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.test.jsx new file mode 100644 index 000000000..d4152a42e --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.test.jsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { shallow } from 'enzyme'; + +import { formatMessage } from '../../../../../../testUtils'; +import * as module from './AdvanceTypeSelect'; + +describe('AdvanceTypeSelect', () => { + const props = { + intl: { formatMessage }, + selected: 'blankadvanced', + setSelected: jest.fn().mockName('setSelect'), + }; + describe('snapshots', () => { + test('snapshots: renders as expected with default props', () => { + expect( + shallow(), + ).toMatchSnapshot(); + }); + test('snapshots: renders as expected with problemType is circuitschematic', () => { + expect( + shallow(), + ).toMatchSnapshot(); + }); + test('snapshots: renders as expected with problemType is customgrader', () => { + expect( + shallow(), + ).toMatchSnapshot(); + }); + test('snapshots: renders as expected with problemType is drag_and_drop', () => { + expect( + shallow(), + ).toMatchSnapshot(); + }); + test('snapshots: renders as expected with problemType is formularesponse', () => { + expect( + shallow(), + ).toMatchSnapshot(); + }); + test('snapshots: renders as expected with problemType is imageresponse', () => { + expect( + shallow(), + ).toMatchSnapshot(); + }); + test('snapshots: renders as expected with problemType is jsinput_response', () => { + expect( + shallow(), + ).toMatchSnapshot(); + }); + test('snapshots: renders as expected with problemType is problem_with_hint', () => { + expect( + shallow(), + ).toMatchSnapshot(); + }); + }); +}); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.jsx index d894f22bf..b76b9b9b0 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.jsx @@ -1,28 +1,43 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { Hyperlink, Image } from '@edx/paragon'; +import { + FormattedMessage, + injectIntl, + intlShape, +} from '@edx/frontend-platform/i18n'; +import messages from './messages'; import { ProblemTypes } from '../../../../../data/constants/problem'; -const Preview = ({ +export const Preview = ({ problemType, + // injected + intl, }) => { if (problemType === null) { return null; } const data = ProblemTypes[problemType]; return ( -
-
-

{data.title}

+
+
+ {intl.formatMessage(messages.previewTitle, { previewTitle: data.title })}
-
- {data.preview} -
-
-

{data.description}

-
-
-

{data.helpLink}

+ {intl.formatMessage(messages.previewAltText, +
+ {intl.formatMessage(messages.previewDescription, { previewDescription: data.description })}
+ + +
); }; @@ -30,8 +45,11 @@ const Preview = ({ Preview.defaultProps = { problemType: null, }; + Preview.propTypes = { problemType: PropTypes.string, + // injected + intl: intlShape.isRequired, }; -export default Preview; +export default injectIntl(Preview); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.test.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.test.jsx new file mode 100644 index 000000000..74ad5d203 --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.test.jsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { shallow } from 'enzyme'; + +import { formatMessage } from '../../../../../../testUtils'; +import * as module from './Preview'; + +describe('Preview', () => { + const props = { + intl: { formatMessage }, + problemType: null, + }; + describe('snapshots', () => { + test('snapshots: renders as expected with default props', () => { + expect( + shallow(), + ).toMatchSnapshot(); + }); + test('snapshots: renders as expected with problemType is stringresponse', () => { + expect( + shallow(), + ).toMatchSnapshot(); + }); + test('snapshots: renders as expected with problemType is numericalresponse', () => { + expect( + shallow(), + ).toMatchSnapshot(); + }); + test('snapshots: renders as expected with problemType is optionresponse', () => { + expect( + shallow(), + ).toMatchSnapshot(); + }); + test('snapshots: renders as expected with problemType is choiceresponse', () => { + expect( + shallow(), + ).toMatchSnapshot(); + }); + test('snapshots: renders as expected with problemType is multiplechoiceresponse', () => { + expect( + shallow(), + ).toMatchSnapshot(); + }); + }); +}); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.jsx index 80b9de0e7..e2c8fea06 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.jsx @@ -1,31 +1,45 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Form } from '@edx/paragon'; -import { ProblemTypes } from '../../../../../data/constants/problem'; +import { Button, SelectableBox } from '@edx/paragon'; +import { FormattedMessage, injectIntl } from '@edx/frontend-platform/i18n'; +import { ProblemTypes, ProblemTypeKeys, AdvanceProblemKeys } from '../../../../../data/constants/problem'; +import messages from './messages'; -// TODO: problemtype -const ProblemTypeSelect = ({ - // redux +export const ProblemTypeSelect = ({ + selected, setSelected, }) => { const handleChange = e => setSelected(e.target.value); + const handleClick = () => setSelected(AdvanceProblemKeys.BLANK); + const settings = { 'aria-label': 'checkbox', type: 'radio' }; + return ( - - + - {ProblemTypes.SINGLESELECT.title} - {ProblemTypes.MULTISELECT.title} - {ProblemTypes.DROPDOWN.title} - {ProblemTypes.NUMERIC.title} - {ProblemTypes.TEXTINPUT.title} - - + {Object.values(ProblemTypeKeys).map((key) => ( + key !== 'advanced' + ? ( + + {ProblemTypes[key].title} + + ) + : null + ))} + + + ); }; ProblemTypeSelect.propTypes = { + selected: PropTypes.string.isRequired, setSelected: PropTypes.func.isRequired, }; -export default ProblemTypeSelect; +export default injectIntl(ProblemTypeSelect); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.test.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.test.jsx new file mode 100644 index 000000000..1cea66e9f --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.test.jsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import { ProblemTypeKeys } from '../../../../../data/constants/problem'; +import * as module from './ProblemTypeSelect'; + +describe('ProblemTypeSelect', () => { + const props = { + selected: null, + setSelected: jest.fn(), + }; + + describe('snapshot', () => { + test('SINGLESELECT', () => { + expect(shallow( + , + )).toMatchSnapshot(); + }); + test('MULTISELECT', () => { + expect(shallow( + , + )).toMatchSnapshot(); + }); + test('DROPDOWN', () => { + expect(shallow( + , + )).toMatchSnapshot(); + }); + test('NUMERIC', () => { + expect(shallow( + , + )).toMatchSnapshot(); + }); + test('TEXTINPUT', () => { + expect(shallow( + , + )).toMatchSnapshot(); + }); + }); +}); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/AdvanceTypeSelect.test.jsx.snap b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/AdvanceTypeSelect.test.jsx.snap new file mode 100644 index 000000000..040a7ca10 --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/AdvanceTypeSelect.test.jsx.snap @@ -0,0 +1,2241 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default props 1`] = ` +
+ + + + + + + + + + + + + Blank advance problem + + + + + + Circuit schematic builder + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Custom JavaScript display and grading + + + + + + Custom Python-evaluated input + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Provisional +
+
+
+ + + Drag and drop (deprecated version) + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Image mapped input + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Math expression input + + + + + + Problem with adaptive hint + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+
+
+
+`; + +exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problemType is circuitschematic 1`] = ` +
+ + + + + + + + + + + + + Blank advance problem + + + + + + Circuit schematic builder + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Custom JavaScript display and grading + + + + + + Custom Python-evaluated input + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Provisional +
+
+
+ + + Drag and drop (deprecated version) + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Image mapped input + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Math expression input + + + + + + Problem with adaptive hint + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+
+
+
+`; + +exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problemType is customgrader 1`] = ` +
+ + + + + + + + + + + + + Blank advance problem + + + + + + Circuit schematic builder + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Custom JavaScript display and grading + + + + + + Custom Python-evaluated input + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Provisional +
+
+
+ + + Drag and drop (deprecated version) + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Image mapped input + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Math expression input + + + + + + Problem with adaptive hint + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+
+
+
+`; + +exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problemType is drag_and_drop 1`] = ` +
+ + + + + + + + + + + + + Blank advance problem + + + + + + Circuit schematic builder + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Custom JavaScript display and grading + + + + + + Custom Python-evaluated input + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Provisional +
+
+
+ + + Drag and drop (deprecated version) + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Image mapped input + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Math expression input + + + + + + Problem with adaptive hint + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+
+
+
+`; + +exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problemType is formularesponse 1`] = ` +
+ + + + + + + + + + + + + Blank advance problem + + + + + + Circuit schematic builder + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Custom JavaScript display and grading + + + + + + Custom Python-evaluated input + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Provisional +
+
+
+ + + Drag and drop (deprecated version) + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Image mapped input + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Math expression input + + + + + + Problem with adaptive hint + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+
+
+
+`; + +exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problemType is imageresponse 1`] = ` +
+ + + + + + + + + + + + + Blank advance problem + + + + + + Circuit schematic builder + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Custom JavaScript display and grading + + + + + + Custom Python-evaluated input + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Provisional +
+
+
+ + + Drag and drop (deprecated version) + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Image mapped input + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Math expression input + + + + + + Problem with adaptive hint + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+
+
+
+`; + +exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problemType is jsinput_response 1`] = ` +
+ + + + + + + + + + + + + Blank advance problem + + + + + + Circuit schematic builder + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Custom JavaScript display and grading + + + + + + Custom Python-evaluated input + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Provisional +
+
+
+ + + Drag and drop (deprecated version) + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Image mapped input + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Math expression input + + + + + + Problem with adaptive hint + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+
+
+
+`; + +exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problemType is problem_with_hint 1`] = ` +
+ + + + + + + + + + + + + Blank advance problem + + + + + + Circuit schematic builder + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Custom JavaScript display and grading + + + + + + Custom Python-evaluated input + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Provisional +
+
+
+ + + Drag and drop (deprecated version) + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Image mapped input + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+ + + Math expression input + + + + + + Problem with adaptive hint + + + +
+ {supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + + + + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + } +
+ + } + placement="right" + > +
+ Not supported +
+
+
+
+
+
+`; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/Preview.test.jsx.snap b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/Preview.test.jsx.snap new file mode 100644 index 000000000..8dd2e23bf --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/Preview.test.jsx.snap @@ -0,0 +1,203 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Preview snapshots snapshots: renders as expected with default props 1`] = `""`; + +exports[`Preview snapshots snapshots: renders as expected with problemType is choiceresponse 1`] = ` +
+
+ Multi Select Problem +
+ A preview illustration of a {problemType, select,
+      multiplechoiceresponse {single select}
+      stringreponse {text input}
+      numericalresponse {numerical input}
+      optionresponse {dropdown}
+      choiceresponse {multiple select}
+      other {null}
+    } problem +
+ Specify one or more correct answers from a list of possible options. +
+ + + +
+`; + +exports[`Preview snapshots snapshots: renders as expected with problemType is multiplechoiceresponse 1`] = ` +
+
+ Single Select Problem +
+ A preview illustration of a {problemType, select,
+      multiplechoiceresponse {single select}
+      stringreponse {text input}
+      numericalresponse {numerical input}
+      optionresponse {dropdown}
+      choiceresponse {multiple select}
+      other {null}
+    } problem +
+ Specify one correct answer from a list of possible options +
+ + + +
+`; + +exports[`Preview snapshots snapshots: renders as expected with problemType is numericalresponse 1`] = ` +
+
+ Numeric Response Problem +
+ A preview illustration of a {problemType, select,
+      multiplechoiceresponse {single select}
+      stringreponse {text input}
+      numericalresponse {numerical input}
+      optionresponse {dropdown}
+      choiceresponse {multiple select}
+      other {null}
+    } problem +
+ Specify one or more correct numeric answers, submitted in a response field. +
+ + + +
+`; + +exports[`Preview snapshots snapshots: renders as expected with problemType is optionresponse 1`] = ` +
+
+ Dropdown Problem +
+ A preview illustration of a {problemType, select,
+      multiplechoiceresponse {single select}
+      stringreponse {text input}
+      numericalresponse {numerical input}
+      optionresponse {dropdown}
+      choiceresponse {multiple select}
+      other {null}
+    } problem +
+ Specify one correct answer from a list of possible options, selected in a dropdown menu. +
+ + + +
+`; + +exports[`Preview snapshots snapshots: renders as expected with problemType is stringresponse 1`] = ` +
+
+ Text Input Problem +
+ A preview illustration of a {problemType, select,
+      multiplechoiceresponse {single select}
+      stringreponse {text input}
+      numericalresponse {numerical input}
+      optionresponse {dropdown}
+      choiceresponse {multiple select}
+      other {null}
+    } problem +
+ Specify one or more correct text answers, including numbers and special characters, submitted in a response field. +
+ + + +
+`; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/ProblemTypeSelect.test.jsx.snap b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/ProblemTypeSelect.test.jsx.snap new file mode 100644 index 000000000..bf2f8389e --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/ProblemTypeSelect.test.jsx.snap @@ -0,0 +1,316 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ProblemTypeSelect snapshot DROPDOWN 1`] = ` + + + + Single Select Problem + + + Multi Select Problem + + + Dropdown Problem + + + Numeric Response Problem + + + Text Input Problem + + + + +`; + +exports[`ProblemTypeSelect snapshot MULTISELECT 1`] = ` + + + + Single Select Problem + + + Multi Select Problem + + + Dropdown Problem + + + Numeric Response Problem + + + Text Input Problem + + + + +`; + +exports[`ProblemTypeSelect snapshot NUMERIC 1`] = ` + + + + Single Select Problem + + + Multi Select Problem + + + Dropdown Problem + + + Numeric Response Problem + + + Text Input Problem + + + + +`; + +exports[`ProblemTypeSelect snapshot SINGLESELECT 1`] = ` + + + + Single Select Problem + + + Multi Select Problem + + + Dropdown Problem + + + Numeric Response Problem + + + Text Input Problem + + + + +`; + +exports[`ProblemTypeSelect snapshot TEXTINPUT 1`] = ` + + + + Single Select Problem + + + Multi Select Problem + + + Dropdown Problem + + + Numeric Response Problem + + + Text Input Problem + + + + +`; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/messages.js b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/messages.js new file mode 100644 index 000000000..dae097bb7 --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/messages.js @@ -0,0 +1,69 @@ +export const messages = { + advanceProblemButtonLabel: { + id: 'authoring.problemEditor.problemSelect.advanceButton.label', + defaultMessage: 'Advance problem types', + description: 'Button label for advance problem types option', + }, + advanceMenuTitle: { + id: 'authoring.problemEditor.advanceProblem.menu.title', + defaultMessage: 'Advanced problems', + description: 'Title for advanced problem menu', + }, + advanceProblemTypeLabel: { + id: 'authoring.problemEditor.advanceProblem.problemType.label', + defaultMessage: '{problemType}', + description: 'Label for advance problem type radio select', + }, + problemSupportStatus: { + id: 'authoring.problemEditor.advanceProblem.supportStatus', + defaultMessage: '{supportStatus}', + description: 'Text for advance problem type\'s support status', + }, + supportStatusTooltipMessage: { + id: 'authoring.problemEditor.advanceProblem.supportStatus.tooltipMessage', + defaultMessage: `{supportStatus, select, + Provisional {Provisionally supported tools might lack the robustness of functionality + that your courses require. edX does not have control over the quality of the software, + or of the content that can be provided using these tools. + \n \n + Test these tools thoroughly before using them in your course, especially in graded + sections. Complete documentstion might not be available for provisionally supported + tools, or documentation might be available from sources other than edX.} + Not_supported {Tools with no support are not maintained by edX, and might be deprecated + in the future. They are not recommened for use in courses due to non-compliance with one + or more of the base requirements, such as testing, accessibility, internationalization, + and documentation.} + other { } + }`, + description: 'Message for support status tooltip', + }, + previewTitle: { + id: 'authoring.problemEditor.preview.title', + defaultMessage: '{previewTitle}', + description: 'Title for the problem preview column', + }, + previewAltText: { + id: 'authoring.problemEditor.preview.altText', + defaultMessage: `A preview illustration of a {problemType, select, + multiplechoiceresponse {single select} + stringreponse {text input} + numericalresponse {numerical input} + optionresponse {dropdown} + choiceresponse {multiple select} + other {null} + } problem`, + description: 'Alt text for the illustration of the problem preview', + }, + previewDescription: { + id: 'authoring.problemEditor.preview.description', + defaultMessage: '{previewDescription}', + description: 'Description of the selected problem type', + }, + learnMoreButtonLabel: { + id: 'authoring.problemEditor.learnMoreButtonLabel.label', + defaultMessage: 'Learn More', + description: 'Label for Learn More button', + }, +}; + +export default messages; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.js b/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.js index 42d65c681..f1dace285 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.js +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.js @@ -1,10 +1,59 @@ +import { useEffect, useState } from 'react'; import { - useState, -} from 'react'; + AdvanceProblemKeys, AdvanceProblems, ProblemTypeKeys, ProblemTypes, +} from '../../../../data/constants/problem'; import { StrictDict } from '../../../../utils'; +import * as module from './hooks'; export const state = StrictDict({ selected: (val) => useState(val), }); -export default { state }; +export const selectHooks = () => { + const [selected, setSelected] = module.state.selected(ProblemTypeKeys.SINGLESELECT); + return { + selected, + setSelected, + }; +}; + +export const onSelect = (setProblemType, selected, updateField) => () => { + if (Object.values(AdvanceProblemKeys).includes(selected)) { + updateField({ rawOLX: AdvanceProblems[selected].template }); + } + setProblemType({ selected }); +}; + +export const useArrowNav = (selected, setSelected) => { + const detectKeyDown = (e) => { + const problemTypeValues = Object.values(ProblemTypeKeys); + switch (e.key) { + case 'ArrowUp': + if (problemTypeValues.includes(selected) && ProblemTypes[selected].prev) { + setSelected(ProblemTypes[selected].prev); + document.getElementById(ProblemTypes[selected].prev).focus(); + } + break; + case 'ArrowDown': + if (problemTypeValues.includes(selected) && ProblemTypes[selected].next) { + setSelected(ProblemTypes[selected].next); + document.getElementById(ProblemTypes[selected].next).focus(); + } + break; + default: + } + }; + useEffect(() => { + document.addEventListener('keydown', detectKeyDown, true); + return () => { + document.removeEventListener('keydown', detectKeyDown, true); + }; + }, [selected, setSelected]); +}; + +export default { + state, + selectHooks, + onSelect, + useArrowNav, +}; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.test.js b/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.test.js new file mode 100644 index 000000000..50829221e --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.test.js @@ -0,0 +1,168 @@ +/* eslint-disable prefer-destructuring */ +import React from 'react'; +import { MockUseState } from '../../../../../testUtils'; +import * as module from './hooks'; +import { AdvanceProblems, ProblemTypeKeys } from '../../../../data/constants/problem'; + +jest.mock('react', () => ({ + ...jest.requireActual('react'), + useState: (val) => ({ useState: val }), + useEffect: jest.fn(), +})); + +const state = new MockUseState(module); +const mockSetProblemType = jest.fn().mockName('setProblemType'); +const mockUpdateField = jest.fn().mockName('updateField'); +const mockSelected = 'vAl'; +const mockAdvancedSelected = 'blankadvanced'; +const mockSetSelected = jest.fn().mockName('setSelected'); + +let hook; + +describe('SelectTypeModal hooks', () => { + beforeEach(() => { + state.mock(); + }); + afterEach(() => { + state.restore(); + jest.clearAllMocks(); + }); + + describe('selectHooks', () => { + beforeEach(() => { + hook = module.selectHooks(); + }); + test('selected defaults to SINGLESELECT', () => { + expect(hook.selected).toEqual(ProblemTypeKeys.SINGLESELECT); + }); + test('setSelected sets state as expected', () => { + const expectedArg = 'neWvAl'; + state.mockVal(state.keys.selected, 'mOcKvAl'); + hook.setSelected(expectedArg); + expect(state.setState.selected).toHaveBeenCalledWith(expectedArg); + }); + }); + + describe('onSelect', () => { + test('updateField is called with selected templated if selected is an Advanced Problem', () => { + module.onSelect(mockSetProblemType, mockAdvancedSelected, mockUpdateField)(); + expect(mockUpdateField).toHaveBeenCalledWith({ + rawOLX: AdvanceProblems[mockAdvancedSelected].template, + }); + }); + test('setProblemType is called with selected', () => { + module.onSelect(mockSetProblemType, mockSelected, mockUpdateField)(); + expect(mockSetProblemType).toHaveBeenCalledWith({ selected: mockSelected }); + }); + }); + + describe('useArrowNav', () => { + document.body.innerHTML = ` +
+
+
+
+
+ `; + const mockKeyUp = new KeyboardEvent('keydown', { key: 'ArrowUp' }); + const mockKeyDown = new KeyboardEvent('keydown', { key: 'ArrowDown' }); + let cb; + let prereqs; + + describe('SINGLESELECT', () => { + beforeEach(() => { + module.useArrowNav(ProblemTypeKeys.SINGLESELECT, mockSetSelected); + [cb, prereqs] = React.useEffect.mock.calls[0]; + cb(); + }); + test('pressing up arrow sets MULTISELECT', () => { + expect(React.useEffect.mock.calls.length).toEqual(1); + expect(prereqs).toStrictEqual([ProblemTypeKeys.SINGLESELECT, mockSetSelected]); + document.dispatchEvent(mockKeyUp); + expect(mockSetSelected).toHaveBeenCalledWith(ProblemTypeKeys.TEXTINPUT); + }); + test('pressing down arrow sets MULTISELECT', () => { + expect(React.useEffect.mock.calls.length).toEqual(1); + expect(prereqs).toStrictEqual([ProblemTypeKeys.SINGLESELECT, mockSetSelected]); + document.dispatchEvent(mockKeyDown); + expect(mockSetSelected).toHaveBeenCalledWith(ProblemTypeKeys.MULTISELECT); + }); + }); + describe('MULTISELECT', () => { + beforeEach(() => { + module.useArrowNav(ProblemTypeKeys.MULTISELECT, mockSetSelected); + [cb, prereqs] = React.useEffect.mock.calls[0]; + cb(); + }); + test('pressing up arrow sets SINGLESELECT', () => { + expect(React.useEffect.mock.calls.length).toEqual(1); + expect(prereqs).toStrictEqual([ProblemTypeKeys.MULTISELECT, mockSetSelected]); + document.dispatchEvent(mockKeyUp); + expect(mockSetSelected).toHaveBeenCalledWith(ProblemTypeKeys.SINGLESELECT); + }); + test('pressing down arrow sets DROPDOWN', () => { + expect(React.useEffect.mock.calls.length).toEqual(1); + expect(prereqs).toStrictEqual([ProblemTypeKeys.MULTISELECT, mockSetSelected]); + document.dispatchEvent(mockKeyDown); + expect(mockSetSelected).toHaveBeenCalledWith(ProblemTypeKeys.DROPDOWN); + }); + }); + describe('DROPDOWN', () => { + beforeEach(() => { + module.useArrowNav(ProblemTypeKeys.DROPDOWN, mockSetSelected); + [cb, prereqs] = React.useEffect.mock.calls[0]; + cb(); + }); + test('pressing up arrow sets MULTISELECT', () => { + expect(React.useEffect.mock.calls.length).toEqual(1); + expect(prereqs).toStrictEqual([ProblemTypeKeys.DROPDOWN, mockSetSelected]); + document.dispatchEvent(mockKeyUp); + expect(mockSetSelected).toHaveBeenCalledWith(ProblemTypeKeys.MULTISELECT); + }); + test('pressing down arrow sets NUMERIC', () => { + expect(React.useEffect.mock.calls.length).toEqual(1); + expect(prereqs).toStrictEqual([ProblemTypeKeys.DROPDOWN, mockSetSelected]); + document.dispatchEvent(mockKeyDown); + expect(mockSetSelected).toHaveBeenCalledWith(ProblemTypeKeys.NUMERIC); + }); + }); + describe('NUMERIC', () => { + beforeEach(() => { + module.useArrowNav(ProblemTypeKeys.NUMERIC, mockSetSelected); + [cb, prereqs] = React.useEffect.mock.calls[0]; + cb(); + }); + test('pressing up arrow sets DROPDOWN', () => { + expect(React.useEffect.mock.calls.length).toEqual(1); + expect(prereqs).toStrictEqual([ProblemTypeKeys.NUMERIC, mockSetSelected]); + document.dispatchEvent(mockKeyUp); + expect(mockSetSelected).toHaveBeenCalledWith(ProblemTypeKeys.DROPDOWN); + }); + test('pressing down arrow sets TEXTINPUT', () => { + expect(React.useEffect.mock.calls.length).toEqual(1); + expect(prereqs).toStrictEqual([ProblemTypeKeys.NUMERIC, mockSetSelected]); + document.dispatchEvent(mockKeyDown); + expect(mockSetSelected).toHaveBeenCalledWith(ProblemTypeKeys.TEXTINPUT); + }); + }); + describe('TEXTINPUT', () => { + beforeEach(() => { + module.useArrowNav(ProblemTypeKeys.TEXTINPUT, mockSetSelected); + [cb, prereqs] = React.useEffect.mock.calls[0]; + cb(); + }); + test('pressing up arrow sets NUMERIC', () => { + expect(React.useEffect.mock.calls.length).toEqual(1); + expect(prereqs).toStrictEqual([ProblemTypeKeys.TEXTINPUT, mockSetSelected]); + document.dispatchEvent(mockKeyUp); + expect(mockSetSelected).toHaveBeenCalledWith(ProblemTypeKeys.NUMERIC); + }); + test('pressing down arrow sets SINGLESELECT', () => { + expect(React.useEffect.mock.calls.length).toEqual(1); + expect(prereqs).toStrictEqual([ProblemTypeKeys.TEXTINPUT, mockSetSelected]); + document.dispatchEvent(mockKeyDown); + expect(mockSetSelected).toHaveBeenCalledWith(ProblemTypeKeys.SINGLESELECT); + }); + }); + }); +}); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.jsx index 6d12dbad2..49d2ca7b7 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.jsx @@ -1,23 +1,40 @@ import React from 'react'; +import PropTypes from 'prop-types'; +import { Col, Row } from '@edx/paragon'; import ProblemTypeSelect from './content/ProblemTypeSelect'; import Preview from './content/Preview'; +import AdvanceTypeSelect from './content/AdvanceTypeSelect'; import SelectTypeWrapper from './SelectTypeWrapper'; -import * as hooks from './hooks'; +import hooks from './hooks'; +import { AdvanceProblemKeys } from '../../../../data/constants/problem'; -export const SelectTypeModal = () => { - const { selected, setSelected } = hooks.state.selected(null); +export const SelectTypeModal = ({ + onClose, +}) => { + const { selected, setSelected } = hooks.selectHooks(); + hooks.useArrowNav(selected, setSelected); return ( -
- - - - -
+ + + {(!Object.values(AdvanceProblemKeys).includes(selected)) ? ( + <> + + + + + + + + ) : } + + ); }; +SelectTypeModal.propTypes = { + onClose: PropTypes.func.isRequired, +}; + export default SelectTypeModal; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.test.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.test.jsx new file mode 100644 index 000000000..05ab1522d --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.test.jsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import * as module from '.'; + +jest.mock('./hooks', () => ({ + selectHooks: jest.fn(() => ({ + selected: 'mOcKsELEcted', + setSelected: jest.fn().mockName('setSelected'), + })), + useArrowNav: jest.fn().mockName('useArrowNav'), +})); + +describe('SelectTypeModal', () => { + const props = { + onClose: jest.fn(), + }; + + test('snapshot', () => { + expect(shallow()).toMatchSnapshot(); + }); +}); diff --git a/src/editors/containers/ProblemEditor/data/OLXParser.js b/src/editors/containers/ProblemEditor/data/OLXParser.js index 4fa43f000..18126a039 100644 --- a/src/editors/containers/ProblemEditor/data/OLXParser.js +++ b/src/editors/containers/ProblemEditor/data/OLXParser.js @@ -331,12 +331,12 @@ export class OLXParser { getProblemType() { const problemKeys = Object.keys(this.problem); const intersectedProblems = _.intersection(Object.values(ProblemTypeKeys), problemKeys); + + if (intersectedProblems.length === 0) { + return null; + } if (intersectedProblems.length > 1) { - const errorMessage = { - code: 500, - message: 'More than one problem type is not supported!', - }; - throw errorMessage; + return ProblemTypeKeys.ADVANCED; } const problemType = intersectedProblems[0]; return problemType; @@ -352,10 +352,6 @@ export class OLXParser { const problemType = this.getProblemType(); const hints = this.getHints(); const question = this.parseQuestions(problemType); - const errorMessage = { - code: 500, - message: 'The problem type is not supported!', - }; switch (problemType) { case ProblemTypeKeys.DROPDOWN: answersObject = this.parseMultipleChoiceAnswers(ProblemTypeKeys.DROPDOWN, 'optioninput', 'option'); @@ -372,8 +368,11 @@ export class OLXParser { case ProblemTypeKeys.SINGLESELECT: answersObject = this.parseMultipleChoiceAnswers(ProblemTypeKeys.SINGLESELECT, 'choicegroup', 'choice'); break; + case ProblemTypeKeys.ADVANCED: + break; default: - throw errorMessage; + // if problem is unset, return null + return {}; } if (_.has(answersObject, 'additionalStringAttributes')) { diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/__snapshots__/index.test.jsx.snap b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/__snapshots__/index.test.jsx.snap index 245b798b2..a03d9e42e 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/__snapshots__/index.test.jsx.snap +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/__snapshots__/index.test.jsx.snap @@ -55,10 +55,10 @@ exports[`TranscriptWidget component snapshots snapshot: renders ErrorAlert with />
- - + } placement="top" > @@ -78,7 +78,7 @@ exports[`TranscriptWidget component snapshots snapshot: renders ErrorAlert with } } /> - +
- - + } placement="top" > @@ -199,7 +199,7 @@ exports[`TranscriptWidget component snapshots snapshot: renders ErrorAlert with } } /> - +
- - + } placement="top" > @@ -316,7 +316,7 @@ exports[`TranscriptWidget component snapshots snapshots: renders as expected wit } } /> - +
- - + } placement="top" > @@ -490,7 +490,7 @@ exports[`TranscriptWidget component snapshots snapshots: renders as expected wit } } /> - +
- - + } placement="top" > @@ -607,7 +607,7 @@ exports[`TranscriptWidget component snapshots snapshots: renders as expected wit } } /> - +
- - + } placement="top" > @@ -120,7 +120,7 @@ exports[`VideoSourceWidget snapshots snapshots: renders as expected with default } } /> - + diff --git a/src/editors/data/constants/olxTemplates/circuitschematic.js b/src/editors/data/constants/olxTemplates/circuitschematic.js new file mode 100644 index 000000000..571b7c484 --- /dev/null +++ b/src/editors/data/constants/olxTemplates/circuitschematic.js @@ -0,0 +1,92 @@ +/* eslint-disable */ +// --- +// metadata: +// display_name: Circuit Schematic Builder +// markdown: !!null +// data: | +export const circuitSchematic = ` +

+ Circuit schematic problems allow students to create virtual circuits by + arranging elements such as voltage sources, capacitors, resistors, and + MOSFETs on an interactive grid. The system evaluates a DC, AC, or + transient analysis of the circuit. +

+

+ For more information, see + + Circuit Schematic Builder Problem in Building and Running an edX Course. +

+

+ When you add the problem, be sure to select Settings + to specify a Display Name and other values that apply. +

+

You can use the following example problems as models.

+ + +

Make a voltage divider that splits the provided voltage evenly.

+
+ +
+ + dc_value = "dc analysis not found" + for response in submission[0]: + if response[0] == 'dc': + for node in response[1:]: + dc_value = node['output'] + + if dc_value == .5: + correct = ['correct'] + else: + correct = ['incorrect'] + + +
+

Explanation

+

+ You can form a voltage divider that evenly divides the input + voltage with two identically valued resistors, with the sampled + voltage taken in between the two. +

+

+
+
+
+ + +

Make a high-pass filter.

+
+ +
+ + ac_values = None + for response in submission[0]: + if response[0] == 'ac': + for node in response[1:]: + ac_values = node['NodeA'] + print "the ac analysis value:", ac_values + if ac_values == None: + correct = ['incorrect'] + elif ac_values[0][1] < ac_values[1][1]: + correct = ['correct'] + else: + correct = ['incorrect'] + + +
+

Explanation

+

+ You can form a simple high-pass filter without any further + constraints by simply putting a resistor in series with a + capacitor. The actual values of the components do not really + matter in this problem. +

+

+
+
+
+
`; + +export default { circuitSchematic }; diff --git a/src/editors/data/constants/olxTemplates/customgrader.js b/src/editors/data/constants/olxTemplates/customgrader.js new file mode 100644 index 000000000..914fe3bb3 --- /dev/null +++ b/src/editors/data/constants/olxTemplates/customgrader.js @@ -0,0 +1,79 @@ +/* eslint-disable */ +// --- +// metadata: +// display_name: Custom Python-Evaluated Input +// markdown: !!null +// data: | +export const customGrader = ` +

+ In custom Python-evaluated input (also called "write-your-own-grader" + problems), the grader uses a Python script that you create and embed in + the problem to evaluate a learner's response or provide hints. These + problems can be any type. Numerical input and text input problems are + the most common write-your-own-grader problems. +

+

+ You can use script tag format or answer tag format to create these problems. +

+

+ You can create custom Python-evaluated input problems that provide + partial credit or that randomize variables in the Python code. You can + also add images to the solution by using an HTML "img" tag. Note that + the "img" tag must be between the "div" tags that are inside the + "solution" tags, and that learners do not see these images until they + click the "Show Answer" button. +

+

+ For more information, see + Write-Your-Own-Grader Problem in Building and Running an edX Course. +

+

+ When you add the problem, be sure to select Settings + to specify a Display Name and other values that apply. +

+

You can use the following example problem as a model.

+
+ + + +
+ + +
+

Explanation

+

Any set of integers on the line \(y = 10 - x\) satisfy these constraints.

+
+
+
+ + + +
+ + +
+

Explanation

+

Any set of integers on the line \(y = 20 - x\) satisfy these constraints.

+

To add an image to the solution, use an HTML "img" tag. Make sure to include alt text.

+ Description of image, with a primary goal of explaining its
+                relevance to the problem or concept being illustrated for someone
+                who is unable to see the image. +
+
+
+
`; + +export default { customGrader }; diff --git a/src/editors/data/constants/olxTemplates/drag_and_drop.js b/src/editors/data/constants/olxTemplates/drag_and_drop.js new file mode 100644 index 000000000..40d313652 --- /dev/null +++ b/src/editors/data/constants/olxTemplates/drag_and_drop.js @@ -0,0 +1,88 @@ +/* eslint-disable */ +// --- +// metadata: +// display_name: Drag and Drop (Deprecated Version) +// markdown: !!null +// showanswer: never +// data: | +export const dragAndDrop = ` +

In drag and drop problems, students respond to a question by dragging text or objects to a specific location on an image.

+

+ For more information, see + + Drag and Drop Problem (Deprecated) in Building and Running an edX Course. +

+

+ When you add the problem, be sure to select Settings + to specify a Display Name and other values that apply. +

+

You can use the following example problems as models.

+
+ +

Simple Drag and Drop

+

Drag each word in the scrollbar to the bucket that matches the number of letters in the word.

+ + + + + + + + + + + + + + + correct_answer = { + '1': [[70, 150], 121], + '6': [[190, 150], 121], + '8': [[190, 150], 121], + '2': [[310, 150], 121], + '9': [[310, 150], 121], + '11': [[310, 150], 121], + '4': [[420, 150], 121], + '7': [[420, 150], 121], + '3': [[550, 150], 121], + '5': [[550, 150], 121], + '10': [[550, 150], 121]} + if draganddrop.grade(submission[0], correct_answer): + correct = ['correct'] + else: + correct = ['incorrect'] + +
+ + +

Drag and Drop with Outline

+

Label the hydrogen atoms connected with the left carbon atom.

+ + + + + + + + + + + + + + + + correct_answer = [{ + 'draggables': ['1', '2'], + 'targets': ['t2', 't3', 't4' ], + 'rule':'anyof' + }] + if draganddrop.grade(submission[0], correct_answer): + correct = ['correct'] + else: + correct = ['incorrect'] + +
+
`; + +export default { dragAndDrop }; diff --git a/src/editors/data/constants/olxTemplates/formularesponse.js b/src/editors/data/constants/olxTemplates/formularesponse.js new file mode 100644 index 000000000..ea5d43987 --- /dev/null +++ b/src/editors/data/constants/olxTemplates/formularesponse.js @@ -0,0 +1,16 @@ +/* eslint-disable */ +// --- +// metadata: +// display_name: Math Expression Input +// markdown: !!null +// data: | +export const formulaResponse = ` + +

You can use this template as a guide to the OLX markup to use for math expression problems. Edit this component to replace the example with your own assessment.

+ + You can add an optional tip or note related to the prompt like this. Example: To test this example, the correct answer is R_1*R_2/R_3 + + +
+
`; +export default { formulaResponse }; diff --git a/src/editors/data/constants/olxTemplates/imageresponse.js b/src/editors/data/constants/olxTemplates/imageresponse.js new file mode 100644 index 000000000..b3b7acb6a --- /dev/null +++ b/src/editors/data/constants/olxTemplates/imageresponse.js @@ -0,0 +1,33 @@ +/* eslint-disable */ +// --- +// metadata: +// display_name: Image Mapped Input +// markdown: !!null +// data: | +export const imageResponse = ` +

+ In an image mapped input problem, also known as a "pointing on a picture" problem, students click inside a defined region in an image. You define this region by including coordinates in the body of the problem. You can define one rectangular region, + multiple rectangular regions, or one non-rectangular region. For more information, see + Image Mapped Input Problem + in + Building and Running an edx Course. +

+

When you add the problem, be sure to select + Settings + to specify a + Display Name + and other values that apply.

+

You can use the following example problem as a model.

+ +

What country is home to the Great Pyramid of Giza as well as the cities of Cairo and Memphis? Click the country on the map below.

+ + +
+

Explanation

+

Egypt is home to not only the Pyramids, Cairo, and Memphis, but also the Sphinx and the ancient Royal Library of Alexandria.

+
+
+
+
`; + +export default { imageResponse }; diff --git a/src/editors/data/constants/olxTemplates/jsinput_response.js b/src/editors/data/constants/olxTemplates/jsinput_response.js new file mode 100644 index 000000000..3af5a3761 --- /dev/null +++ b/src/editors/data/constants/olxTemplates/jsinput_response.js @@ -0,0 +1,81 @@ +/* eslint-disable */ +// --- +// metadata: +// display_name: Custom JavaScript Display and Grading +// markdown: !!null +// showanswer: never +// data: | +export const jsInputResponse = ` +

+ In these problems (also called custom JavaScript problems or JS Input + problems), you add a problem or tool that uses JavaScript in Studio. + Studio embeds the problem in an IFrame so that your learners can + interact with it in the LMS. You can grade your learners' work using + JavaScript and some basic Python, and the grading is integrated into the + edX grading system. +

+

+ The JS Input problem that you create must use HTML, JavaScript, and + cascading style sheets (CSS). You can use any application creation tool, + such as the Google Web Toolkit (GWT), to create your JS Input problem. +

+

+ For more information, see + + Custom JavaScript Problem in Building and Running an edX Course. +

+

+ JavaScript developers can also see + + Custom JavaScript Applications in the EdX Developer's Guide. +

+

+ When you add the problem, be sure to select Settings + to specify a Display Name and other values that apply. + Also, be sure to specify a title attribute on the jsinput tag; + this title is used for the title attribute on the generated IFrame. Generally, + the title attribute on the IFrame should match the title tag of the HTML hosted + within the IFrame, which is specified by the html_file attribute. +

+

You can use the following example problem as a model.

+ + + +

This is paragraph text displayed before the IFrame.

+ +
+
`; + +export default { jsInputResponse }; diff --git a/src/editors/data/constants/olxTemplates/problem_with_hint.js b/src/editors/data/constants/olxTemplates/problem_with_hint.js new file mode 100644 index 000000000..10be3531c --- /dev/null +++ b/src/editors/data/constants/olxTemplates/problem_with_hint.js @@ -0,0 +1,46 @@ +/* eslint-disable */ +// --- +// metadata: +// display_name: Problem with Adaptive Hint +// markdown: !!null +// data: | +export const problemWithHint = ` + +

Problem With Adaptive Hint

+

This problem demonstrates a question with hints, based on using the hintfn method.

+ + + + + + + +
+
`; + +export default { problemWithHint }; diff --git a/src/editors/data/constants/problem.js b/src/editors/data/constants/problem.js index 9d884afa4..cc491a9d8 100644 --- a/src/editors/data/constants/problem.js +++ b/src/editors/data/constants/problem.js @@ -1,46 +1,129 @@ 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 { circuitSchematic } from './olxTemplates/circuitschematic'; +import { customGrader } from './olxTemplates/customgrader'; +import { dragAndDrop } from './olxTemplates/drag_and_drop'; +import { formulaResponse } from './olxTemplates/formularesponse'; +import { imageResponse } from './olxTemplates/imageresponse'; +import { jsInputResponse } from './olxTemplates/jsinput_response'; +import { problemWithHint } from './olxTemplates/problem_with_hint'; export const ProblemTypeKeys = StrictDict({ - TEXTINPUT: 'stringresponse', - NUMERIC: 'numericalresponse', - DROPDOWN: 'optionresponse', - MULTISELECT: 'choiceresponse', SINGLESELECT: 'multiplechoiceresponse', + MULTISELECT: 'choiceresponse', + DROPDOWN: 'optionresponse', + NUMERIC: 'numericalresponse', + TEXTINPUT: 'stringresponse', + ADVANCED: 'advanced', }); export const ProblemTypes = StrictDict({ [ProblemTypeKeys.SINGLESELECT]: { title: 'Single Select Problem', - preview: ('
'), + preview: singleSelect, description: 'Specify one correct answer from a list of possible options', - helpLink: 'something.com', + helpLink: 'https://edx.readthedocs.io/projects/edx-partner-course-staff/en/latest/exercises_tools/multiple_choice.html', + prev: ProblemTypeKeys.TEXTINPUT, + next: ProblemTypeKeys.MULTISELECT, }, [ProblemTypeKeys.MULTISELECT]: { title: 'Multi Select Problem', - preview: ('
'), + preview: multiSelect, description: 'Specify one or more correct answers from a list of possible options.', - helpLink: 'something.com', + helpLink: 'https://edx.readthedocs.io/projects/edx-partner-course-staff/en/latest/exercises_tools/checkbox.html', + next: ProblemTypeKeys.DROPDOWN, + prev: ProblemTypeKeys.SINGLESELECT, }, [ProblemTypeKeys.DROPDOWN]: { title: 'Dropdown Problem', - preview: ('
'), + preview: dropdown, description: 'Specify one correct answer from a list of possible options, selected in a dropdown menu.', - helpLink: 'something.com', + helpLink: 'https://edx.readthedocs.io/projects/edx-partner-course-staff/en/latest/exercises_tools/dropdown.html', + next: ProblemTypeKeys.NUMERIC, + prev: ProblemTypeKeys.MULTISELECT, }, [ProblemTypeKeys.NUMERIC]: { title: 'Numeric Response Problem', - preview: ('
'), + preview: numericalInput, description: 'Specify one or more correct numeric answers, submitted in a response field.', - helpLink: 'something.com', + helpLink: 'https://edx.readthedocs.io/projects/edx-partner-course-staff/en/latest/exercises_tools/numerical_input.html', + next: ProblemTypeKeys.TEXTINPUT, + prev: ProblemTypeKeys.DROPDOWN, }, [ProblemTypeKeys.TEXTINPUT]: { title: 'Text Input Problem', - preview: ('
'), + preview: textInput, description: 'Specify one or more correct text answers, including numbers and special characters, submitted in a response field.', + helpLink: 'https://edx.readthedocs.io/projects/edx-partner-course-staff/en/latest/exercises_tools/text_input.html', + prev: ProblemTypeKeys.NUMERIC, + next: ProblemTypeKeys.SINGLESELECT, + }, + [ProblemTypeKeys.ADVANCED]: { + title: 'Advanced Problem', + preview: ('
'), + description: 'An Advanced Problem Type', helpLink: 'something.com', }, }); +export const AdvanceProblemKeys = StrictDict({ + BLANK: 'blankadvanced', + CIRCUITSCHEMATIC: 'circuitschematic', + JSINPUT: 'jsinputresponse', + CUSTOMGRADER: 'customgrader', + DRAGANDDROP: 'draganddrop', + IMAGE: 'imageresponse', + FORMULA: 'formularesponse', + PROBLEMWITHHINT: 'problemwithhint', +}); + +export const AdvanceProblems = StrictDict({ + [AdvanceProblemKeys.BLANK]: { + title: 'Blank advance problem', + status: '', + template: '', + }, + [AdvanceProblemKeys.CIRCUITSCHEMATIC]: { + title: 'Circuit schematic builder', + status: 'Not supported', + template: circuitSchematic, + }, + [AdvanceProblemKeys.JSINPUT]: { + title: 'Custom JavaScript display and grading', + status: '', + template: jsInputResponse, + }, + [AdvanceProblemKeys.CUSTOMGRADER]: { + title: 'Custom Python-evaluated input', + status: 'Provisional', + template: customGrader, + }, + [AdvanceProblemKeys.DRAGANDDROP]: { + title: 'Drag and drop (deprecated version)', + status: 'Not supported', + template: dragAndDrop, + }, + [AdvanceProblemKeys.IMAGE]: { + title: 'Image mapped input', + status: 'Not supported', + template: imageResponse, + }, + [AdvanceProblemKeys.FORMULA]: { + title: 'Math expression input', + status: '', + template: formulaResponse, + }, + [AdvanceProblemKeys.PROBLEMWITHHINT]: { + title: 'Problem with adaptive hint', + status: 'Not supported', + template: problemWithHint, + }, +}); + export const ShowAnswerTypesKeys = StrictDict({ ALWAYS: 'always', ANSWERED: 'answered', diff --git a/src/editors/data/constants/requests.js b/src/editors/data/constants/requests.js index 8efe58cb5..273ec758f 100644 --- a/src/editors/data/constants/requests.js +++ b/src/editors/data/constants/requests.js @@ -22,4 +22,6 @@ export const RequestKeys = StrictDict({ fetchCourseDetails: 'fetchCourseDetails', updateTranscriptLanguage: 'updateTranscriptLanguage', getTranscriptFile: 'getTranscriptFile', + uploadImage: 'uploadImage', + fetchAdvanceSettings: 'fetchAdvanceSettings', }); diff --git a/src/editors/data/images/dropdown.png b/src/editors/data/images/dropdown.png new file mode 100644 index 000000000..9fcf02949 Binary files /dev/null and b/src/editors/data/images/dropdown.png differ diff --git a/src/editors/data/images/multiSelect.png b/src/editors/data/images/multiSelect.png new file mode 100644 index 000000000..065716214 Binary files /dev/null and b/src/editors/data/images/multiSelect.png differ diff --git a/src/editors/data/images/numericalInput.png b/src/editors/data/images/numericalInput.png new file mode 100644 index 000000000..535631d3b Binary files /dev/null and b/src/editors/data/images/numericalInput.png differ diff --git a/src/editors/data/images/singleSelect.png b/src/editors/data/images/singleSelect.png new file mode 100644 index 000000000..33fe71831 Binary files /dev/null and b/src/editors/data/images/singleSelect.png differ diff --git a/src/editors/data/images/textInput.png b/src/editors/data/images/textInput.png new file mode 100644 index 000000000..39c3d9225 Binary files /dev/null and b/src/editors/data/images/textInput.png differ diff --git a/src/editors/data/redux/problem/reducers.js b/src/editors/data/redux/problem/reducers.js index cbd83f203..765c26ede 100644 --- a/src/editors/data/redux/problem/reducers.js +++ b/src/editors/data/redux/problem/reducers.js @@ -7,7 +7,7 @@ import { ProblemTypeKeys, ShowAnswerTypesKeys } from '../../constants/problem'; const nextAlphaId = (lastId) => String.fromCharCode(lastId.charCodeAt(0) + 1); const initialState = { rawOLX: '', - problemType: ProblemTypeKeys.SINGLESELECT, + problemType: null, question: '', answers: [], groupFeedbackList: [], @@ -124,9 +124,13 @@ const problem = createSlice({ }, ...payload, }), - onSelect: (state, { payload }) => ({ + setEnableTypeSelection: (state) => ({ ...state, - ...payload, + problemType: null, + }), + setProblemType: (state, { payload: { selected } }) => ({ + ...state, + problemType: selected, }), }, }); diff --git a/src/editors/data/redux/thunkActions/problem.js b/src/editors/data/redux/thunkActions/problem.js index 24a885b93..d1902d46f 100644 --- a/src/editors/data/redux/thunkActions/problem.js +++ b/src/editors/data/redux/thunkActions/problem.js @@ -1,22 +1,31 @@ import _ from 'lodash-es'; +import * as requests from './requests'; import { actions } from '..'; import { OLXParser } from '../../../containers/ProblemEditor/data/OLXParser'; import { parseSettings } from '../../../containers/ProblemEditor/data/SettingsParser'; export const initializeProblem = (blockValue) => (dispatch) => { const rawOLX = _.get(blockValue, 'data.data', {}); - try { - const olxParser = new OLXParser(rawOLX); - const { ...data } = olxParser.getParsedOLXData(); - let { settings } = olxParser.getParsedOLXData(); - settings = { ...settings, ...parseSettings(_.get(blockValue, 'data.metadata', {})) }; - if (!_.isEmpty(rawOLX) && !_.isEmpty(data)) { - dispatch(actions.problem.load({ ...data, rawOLX, settings })); - } - } catch (error) { - // eslint-disable-next-line - console.error(error); + const olxParser = new OLXParser(rawOLX); + + const parsedProblem = olxParser.getParsedOLXData(); + if (_.isEmpty(parsedProblem)) { + // if problem is blank, enable selection. + dispatch(actions.problem.setEnableTypeSelection()); } + const { settings, ...data } = parsedProblem; + const parsedSettings = { ...settings, ...parseSettings(_.get(blockValue, 'data.metadata', {})) }; + if (!_.isEmpty(rawOLX) && !_.isEmpty(data)) { + dispatch(actions.problem.load({ ...data, rawOLX, settings: parsedSettings })); + } + dispatch(requests.fetchAdvanceSettings({ + onSuccess: (response) => { + console.log(response); + if (response.data.allow_unsupported_xblocks.value) { + console.log(response.allow_unsupported_xblocks.value); + } + }, + })); }; export default { initializeProblem }; diff --git a/src/editors/data/redux/thunkActions/requests.js b/src/editors/data/redux/thunkActions/requests.js index 410869763..c21b06798 100644 --- a/src/editors/data/redux/thunkActions/requests.js +++ b/src/editors/data/redux/thunkActions/requests.js @@ -236,6 +236,17 @@ export const fetchCourseDetails = ({ ...rest }) => (dispatch, getState) => { })); }; +export const fetchAdvanceSettings = ({ ...rest }) => (dispatch, getState) => { + dispatch(module.networkRequest({ + requestKey: RequestKeys.fetchAdvanceSettings, + promise: api.fetchAdvanceSettings({ + studioEndpointUrl: selectors.app.studioEndpointUrl(getState()), + learningContextId: selectors.app.learningContextId(getState()), + }), + ...rest, + })); +}; + export default StrictDict({ fetchBlock, fetchStudioView, @@ -250,4 +261,5 @@ export default StrictDict({ updateTranscriptLanguage, fetchCourseDetails, getTranscriptFile, + fetchAdvanceSettings, }); diff --git a/src/editors/data/services/cms/api.js b/src/editors/data/services/cms/api.js index 8d6ca96cd..19d8afe53 100644 --- a/src/editors/data/services/cms/api.js +++ b/src/editors/data/services/cms/api.js @@ -21,6 +21,9 @@ export const apiMethods = { fetchCourseDetails: ({ studioEndpointUrl, learningContextId }) => get( urls.courseDetailsUrl({ studioEndpointUrl, learningContextId }), ), + fetchAdvanceSettings: ({ studioEndpointUrl, learningContextId }) => get( + urls.courseAdvanceSettings({ studioEndpointUrl, learningContextId }), + ), uploadAsset: ({ learningContextId, studioEndpointUrl, diff --git a/src/editors/data/services/cms/mockApi.js b/src/editors/data/services/cms/mockApi.js index f00fbccc1..9a1805baf 100644 --- a/src/editors/data/services/cms/mockApi.js +++ b/src/editors/data/services/cms/mockApi.js @@ -35,17 +35,7 @@ export const fetchBlockById = ({ blockId, studioEndpointUrl }) => { } else if (blockId === 'problem-block-id') { data = { data: ` - -

You can use this template as a guide to the simple editor markdown and OLX markup to use for dropdown problems. Edit this component to replace this template with your own assessment.

- - You can add an optional tip or note related to the prompt like this. - - - - - -
-
`, + `, display_name: 'Dropdown', metadata: { markdown: `You can use this template as a guide to the simple editor markdown and OLX markup to use for dropdown problems. Edit this component to replace this template with your own assessment. @@ -135,6 +125,10 @@ export const fetchCourseDetails = ({ studioEndpointUrl, learningContextId }) => export const allowThumbnailUpload = ({ studioEndpointUrl }) => mockPromise({ data: true, }); +// eslint-disable-next-line +export const fetchAdvanceSettings = ({ studioEndpointUrl, learningContextId }) => mockPromise({ + data: { allow_unsupported_xblocks: { value: true } }, +}); export const normalizeContent = ({ blockId, diff --git a/src/editors/data/services/cms/urls.js b/src/editors/data/services/cms/urls.js index 389a0b775..b6260fcc2 100644 --- a/src/editors/data/services/cms/urls.js +++ b/src/editors/data/services/cms/urls.js @@ -54,3 +54,7 @@ export const downloadVideoHandoutUrl = ({ studioEndpointUrl, handout }) => ( export const courseDetailsUrl = ({ studioEndpointUrl, learningContextId }) => ( `${studioEndpointUrl}/settings/details/${learningContextId}` ); + +export const courseAdvanceSettings = ({ studioEndpointUrl, learningContextId }) => ( + `${studioEndpointUrl}/api/contentstore/v0/advanced_settings/${learningContextId}` +); diff --git a/src/setupTest.js b/src/setupTest.js index b53d50d1e..cc8c8f7d3 100644 --- a/src/setupTest.js +++ b/src/setupTest.js @@ -111,7 +111,11 @@ jest.mock('@edx/paragon', () => jest.requireActual('testUtils').mockNestedCompon Label: 'Form.Label', Text: 'Form.Text', Row: 'Form.Row', + Radio: 'Radio', + RadioSet: 'RadioSet', }, + OverlayTrigger: 'OverlayTrigger', + Tooltip: 'Tooltip', FullscreenModal: 'FullscreenModal', Row: 'Row', Scrollable: 'Scrollable', @@ -122,9 +126,7 @@ jest.mock('@edx/paragon', () => jest.requireActual('testUtils').mockNestedCompon Spinner: 'Spinner', Stack: 'Stack', Toast: 'Toast', - Tooltip: 'ToolTip', Truncate: 'Truncate', - OverlayTrigger: 'OverLayTrigger', })); jest.mock('@edx/paragon/icons', () => ({