fix: propTypes warnings in Problem Editor, refactor some code to TS (#1280)

* fix: a11y - missing 'alt' text for Problem Editor IconButton
* fix: warning in <ProblemTypeSelect> component - missing key prop in list
* fix: warning: `problemType` required in `ProblemEditor`, but is `null`
* fix: warning: The prop `onClose` marked as required in `SelectTypeModal`
* fix: warning: prop `name` is marked as required in `ForwardRef(_c)`
* fix: warning: props `alt`, `id`, and `key` are required
* test: improve test coverage of SelectTypeModal, refactor some code
* test: improve test coverage
This commit is contained in:
Braden MacDonald
2024-09-18 10:01:56 -07:00
committed by GitHub
parent 82a3b7c986
commit b01090902a
32 changed files with 417 additions and 618 deletions

5
src/custom.d.ts vendored
View File

@@ -3,6 +3,11 @@ declare module '*.svg' {
export default content;
}
declare module '*.png' {
const content: string;
export default content;
}
declare module '*.json' {
const value: any;
export default value;

View File

@@ -1,21 +1,29 @@
import React from 'react';
import { useDispatch } from 'react-redux';
import PropTypes from 'prop-types';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import messages from './messages';
import * as hooks from './hooks';
import supportedEditors from './supportedEditors';
import type { EditorComponent } from './EditorComponent';
const Editor = ({
export interface Props extends EditorComponent {
blockType: string;
blockId: string | null;
learningContextId: string | null;
lmsEndpointUrl: string | null;
studioEndpointUrl: string | null;
}
const Editor: React.FC<Props> = ({
learningContextId,
blockType,
blockId,
lmsEndpointUrl,
studioEndpointUrl,
onClose,
returnFunction,
onClose = null,
returnFunction = null,
}) => {
const dispatch = useDispatch();
hooks.initializeApp({
@@ -46,23 +54,5 @@ const Editor = ({
</div>
);
};
Editor.defaultProps = {
blockId: null,
learningContextId: null,
lmsEndpointUrl: null,
onClose: null,
returnFunction: null,
studioEndpointUrl: null,
};
Editor.propTypes = {
blockId: PropTypes.string,
blockType: PropTypes.string.isRequired,
learningContextId: PropTypes.string,
lmsEndpointUrl: PropTypes.string,
onClose: PropTypes.func,
returnFunction: PropTypes.func,
studioEndpointUrl: PropTypes.string,
};
export default Editor;

View File

@@ -0,0 +1,6 @@
/** Shared interface that all Editor components (like ProblemEditor) adhere to */
export interface EditorComponent {
onClose: (() => void) | null;
// TODO: get a better type for the 'result' here
returnFunction?: (() => (result: any) => void) | null;
}

View File

@@ -1,13 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
import {
ActionRow,
Button,
ModalDialog,
} from '@openedx/paragon';
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import messages from './messages';
import * as hooks from '../hooks';
@@ -16,68 +15,49 @@ import { actions, selectors } from '../../../../../data/redux';
const SelectTypeFooter = ({
onCancel,
selected,
// redux
defaultSettings,
updateField,
setBlockTitle,
// injected,
intl,
}) => (
<div className="editor-footer fixed-bottom">
<ModalDialog.Footer className="border-top-0">
<ActionRow>
<ActionRow.Spacer />
<Button
aria-label={intl.formatMessage(messages.cancelButtonAriaLabel)}
variant="tertiary"
onClick={onCancel}
>
<FormattedMessage {...messages.cancelButtonLabel} />
</Button>
<Button
aria-label={intl.formatMessage(messages.selectButtonAriaLabel)}
onClick={hooks.onSelect({
selected,
updateField,
setBlockTitle,
defaultSettings,
})}
disabled={!selected}
>
<FormattedMessage {...messages.selectButtonLabel} />
</Button>
</ActionRow>
</ModalDialog.Footer>
</div>
);
}) => {
const intl = useIntl();
const defaultSettings = useSelector(selectors.problem.defaultSettings);
const dispatch = useDispatch();
const updateField = React.useCallback((data) => dispatch(actions.problem.updateField(data)), [dispatch]);
const setBlockTitle = React.useCallback((title) => dispatch(actions.app.setBlockTitle(title)), [dispatch]);
return (
<div className="editor-footer fixed-bottom">
<ModalDialog.Footer className="border-top-0">
<ActionRow>
<ActionRow.Spacer />
<Button
aria-label={intl.formatMessage(messages.cancelButtonAriaLabel)}
variant="tertiary"
onClick={onCancel}
>
<FormattedMessage {...messages.cancelButtonLabel} />
</Button>
<Button
aria-label={intl.formatMessage(messages.selectButtonAriaLabel)}
onClick={hooks.onSelect({
selected,
updateField,
setBlockTitle,
defaultSettings,
})}
disabled={!selected}
>
<FormattedMessage {...messages.selectButtonLabel} />
</Button>
</ActionRow>
</ModalDialog.Footer>
</div>
);
};
SelectTypeFooter.defaultProps = {
selected: null,
};
SelectTypeFooter.propTypes = {
defaultSettings: PropTypes.shape({
maxAttempts: PropTypes.number,
rerandomize: PropTypes.string,
showResetButton: PropTypes.bool,
showanswer: PropTypes.string,
}).isRequired,
onCancel: PropTypes.func.isRequired,
selected: PropTypes.string,
updateField: PropTypes.func.isRequired,
setBlockTitle: PropTypes.func.isRequired,
// injected
intl: intlShape.isRequired,
};
export const mapStateToProps = (state) => ({
defaultSettings: selectors.problem.defaultSettings(state),
});
export const mapDispatchToProps = {
updateField: actions.problem.updateField,
setBlockTitle: actions.app.setBlockTitle,
};
export const SelectTypeFooterInternal = SelectTypeFooter; // For testing only
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(SelectTypeFooter));
export default SelectTypeFooter;

View File

@@ -1,14 +1,10 @@
import 'CourseAuthoring/editors/setupEditorTest';
import React from 'react';
import { shallow } from '@edx/react-unit-test-utils';
import { Button } from '@openedx/paragon';
import { formatMessage } from '../../../../../testUtils';
import * as module from './SelectTypeFooter';
import SelectTypeFooter from './SelectTypeFooter';
import * as hooks from '../hooks';
import { actions } from '../../../../../data/redux';
const SelectTypeFooter = module.SelectTypeFooterInternal;
jest.mock('../hooks', () => ({
onSelect: jest.fn().mockName('onSelect'),
@@ -46,15 +42,4 @@ describe('SelectTypeFooter', () => {
.toEqual(expected);
});
});
describe('mapStateToProps', () => {
test('is empty', () => {
expect(module.mapDispatchToProps.defaultSettings).toEqual(actions.problem.defaultSettings);
});
});
describe('mapDispatchToProps', () => {
test('loads updateField from problem.updateField actions', () => {
expect(module.mapDispatchToProps.updateField).toEqual(actions.problem.updateField);
});
});
});

View File

@@ -17,6 +17,7 @@ exports[`SelectTypeWrapper snapshot 1`] = `
className="pgn__modal-close-container"
>
<IconButton
alt="Exit the editor"
iconAs="Icon"
src={[MockFunction icons.Close]}
/>
@@ -30,7 +31,7 @@ exports[`SelectTypeWrapper snapshot 1`] = `
test child
</h1>
</ModalDialog.Body>
<injectIntl(ShimmedIntlComponent)
<SelectTypeFooter
selected="iMAsElecTedValUE"
/>
</div>

View File

@@ -1,11 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
import { Icon, ModalDialog, IconButton } from '@openedx/paragon';
import { Close } from '@openedx/paragon/icons';
import SelectTypeFooter from './SelectTypeFooter';
import * as hooks from '../../../../EditorContainer/hooks';
import ecMessages from '../../../../EditorContainer/messages';
import messages from './messages';
const SelectTypeWrapper = ({
@@ -14,6 +15,7 @@ const SelectTypeWrapper = ({
selected,
}) => {
const handleCancel = hooks.handleCancel({ onClose });
const intl = useIntl();
return (
<div
@@ -27,6 +29,7 @@ const SelectTypeWrapper = ({
src={Close}
iconAs={Icon}
onClick={handleCancel}
alt={intl.formatMessage(ecMessages.exitButtonAlt)}
/>
</div>
</ModalDialog.Title>
@@ -51,5 +54,4 @@ SelectTypeWrapper.propTypes = {
onClose: PropTypes.func,
};
export const SelectTypeWrapperInternal = SelectTypeWrapper; // For testing only
export default injectIntl(SelectTypeWrapper);
export default SelectTypeWrapper;

View File

@@ -2,7 +2,7 @@ import 'CourseAuthoring/editors/setupEditorTest';
import React from 'react';
import { shallow } from '@edx/react-unit-test-utils';
import { IconButton } from '@openedx/paragon';
import { SelectTypeWrapperInternal as SelectTypeWrapper } from '.';
import SelectTypeWrapper from '.';
import { handleCancel } from '../../../../EditorContainer/hooks';
jest.mock('../../../../EditorContainer/hooks', () => ({

View File

@@ -1,26 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SelectTypeModal snapshot 1`] = `
<injectIntl(ShimmedIntlComponent)
onClose={[MockFunction]}
selected="mOcKsELEcted"
>
<Row
className="justify-content-center"
>
<Stack
className="flex-wrap mb-6"
direction="horizontal"
gap={4}
>
<injectIntl(ShimmedIntlComponent)
selected="mOcKsELEcted"
setSelected={[MockFunction setSelected]}
/>
<injectIntl(ShimmedIntlComponent)
problemType="mOcKsELEcted"
/>
</Stack>
</Row>
</injectIntl(ShimmedIntlComponent)>
`;

View File

@@ -1,16 +1,11 @@
import 'CourseAuthoring/editors/setupEditorTest';
import React from 'react';
import { shallow } from '@edx/react-unit-test-utils';
import { formatMessage } from '../../../../../testUtils';
import * as module from './AdvanceTypeSelect';
const AdvanceTypeSelect = module.AdvanceTypeSelectInternal;
import AdvanceTypeSelect from './AdvanceTypeSelect';
describe('AdvanceTypeSelect', () => {
const props = {
intl: { formatMessage },
selected: 'blankadvanced',
selected: 'blankadvanced' as const,
setSelected: jest.fn().mockName('setSelect'),
};
describe('snapshots', () => {
@@ -29,11 +24,6 @@ describe('AdvanceTypeSelect', () => {
shallow(<AdvanceTypeSelect {...props} selected="customgrader" />).snapshot,
).toMatchSnapshot();
});
test('snapshots: renders as expected with problemType is drag_and_drop', () => {
expect(
shallow(<AdvanceTypeSelect {...props} selected="drag_and_drop" />).snapshot,
).toMatchSnapshot();
});
test('snapshots: renders as expected with problemType is formularesponse', () => {
expect(
shallow(<AdvanceTypeSelect {...props} selected="formularesponse" />).snapshot,
@@ -44,14 +34,14 @@ describe('AdvanceTypeSelect', () => {
shallow(<AdvanceTypeSelect {...props} selected="imageresponse" />).snapshot,
).toMatchSnapshot();
});
test('snapshots: renders as expected with problemType is jsinput_response', () => {
test('snapshots: renders as expected with problemType is jsinputresponse', () => {
expect(
shallow(<AdvanceTypeSelect {...props} selected="jsinput_response" />).snapshot,
shallow(<AdvanceTypeSelect {...props} selected="jsinputresponse" />).snapshot,
).toMatchSnapshot();
});
test('snapshots: renders as expected with problemType is problem_with_hint', () => {
test('snapshots: renders as expected with problemType is problemwithhint', () => {
expect(
shallow(<AdvanceTypeSelect {...props} selected="problem_with_hint" />).snapshot,
shallow(<AdvanceTypeSelect {...props} selected="problemwithhint" />).snapshot,
).toMatchSnapshot();
});
});

View File

@@ -1,5 +1,4 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
Form,
@@ -12,22 +11,36 @@ import {
Col,
} from '@openedx/paragon';
import { ArrowBack } from '@openedx/paragon/icons';
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { AdvanceProblems, ProblemTypeKeys } from '../../../../../data/constants/problem';
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
import {
AdvancedProblemType,
AdvanceProblems,
ProblemType,
ProblemTypeKeys,
} from '../../../../../data/constants/problem';
import messages from './messages';
const AdvanceTypeSelect = ({
interface Props {
selected: AdvancedProblemType;
setSelected: React.Dispatch<ProblemType | AdvancedProblemType>;
}
const AdvanceTypeSelect: React.FC<Props> = ({
selected,
setSelected,
// injected
intl,
}) => {
const intl = useIntl();
const handleChange = e => { setSelected(e.target.value); };
return (
<Col xs={12} md={8} className="justify-content-center">
<Form.Group className="border rounded text-primary-500 p-0">
<ActionRow className="border-primary-100 border-bottom py-3 pl-2.5 pr-4">
<IconButton src={ArrowBack} iconAs={Icon} onClick={() => setSelected(ProblemTypeKeys.SINGLESELECT)} />
<IconButton
src={ArrowBack}
iconAs={Icon}
alt={intl.formatMessage(messages.advanceMenuGoBack)}
onClick={() => setSelected(ProblemTypeKeys.SINGLESELECT)}
/>
<ActionRow.Spacer />
<Form.Label className="h4">
<FormattedMessage {...messages.advanceMenuTitle} />
@@ -43,7 +56,7 @@ const AdvanceTypeSelect = ({
{Object.entries(AdvanceProblems).map(([type, data]) => {
if (data.status !== '') {
return (
<ActionRow className="border-primary-100 border-bottom m-0 py-3 w-100">
<ActionRow className="border-primary-100 border-bottom m-0 py-3 w-100" key={type}>
<Form.Radio id={type} value={type}>
{intl.formatMessage(messages.advanceProblemTypeLabel, { problemType: data.title })}
</Form.Radio>
@@ -51,7 +64,7 @@ const AdvanceTypeSelect = ({
<OverlayTrigger
placement="right"
overlay={(
<Tooltip>
<Tooltip id={`tooltip-adv-${type}`}>
<div className="text-left">
{intl.formatMessage(messages.supportStatusTooltipMessage, { supportStatus: data.status.replace(' ', '_') })}
</div>
@@ -66,7 +79,7 @@ const AdvanceTypeSelect = ({
);
}
return (
<ActionRow className="border-primary-100 border-bottom m-0 py-3 w-100">
<ActionRow className="border-primary-100 border-bottom m-0 py-3 w-100" key={type}>
<Form.Radio id={type} value={type}>
{intl.formatMessage(messages.advanceProblemTypeLabel, { problemType: data.title })}
</Form.Radio>
@@ -86,16 +99,4 @@ const AdvanceTypeSelect = ({
);
};
AdvanceTypeSelect.defaultProps = {
selected: null,
};
AdvanceTypeSelect.propTypes = {
selected: PropTypes.string,
setSelected: PropTypes.func.isRequired,
// injected
intl: intlShape.isRequired,
};
export const AdvanceTypeSelectInternal = AdvanceTypeSelect; // For testing only
export default injectIntl(AdvanceTypeSelect);
export default AdvanceTypeSelect;

View File

@@ -1,12 +1,11 @@
import 'CourseAuthoring/editors/setupEditorTest';
import React from 'react';
import { shallow } from '@edx/react-unit-test-utils';
import { ProblemTypeKeys } from '../../../../../data/constants/problem';
import { ProblemTypeSelectInternal as ProblemTypeSelect } from './ProblemTypeSelect';
import ProblemTypeSelect from './ProblemTypeSelect';
describe('ProblemTypeSelect', () => {
const props = {
selected: null,
setSelected: jest.fn(),
};

View File

@@ -1,24 +1,35 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Container } from '@openedx/paragon';
import { FormattedMessage, injectIntl } from '@edx/frontend-platform/i18n';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
// SelectableBox in paragon has a bug where you can't change selection. So we override it
import SelectableBox from '../../../../../sharedComponents/SelectableBox';
import { ProblemTypes, ProblemTypeKeys, AdvanceProblemKeys } from '../../../../../data/constants/problem';
import {
ProblemTypes,
ProblemTypeKeys,
AdvanceProblemKeys,
AdvancedProblemType,
ProblemType,
} from '../../../../../data/constants/problem';
import messages from './messages';
const ProblemTypeSelect = ({
interface Props {
selected: ProblemType;
setSelected: (selected: ProblemType | AdvancedProblemType) => void;
}
const ProblemTypeSelect: React.FC<Props> = ({
selected,
setSelected,
}) => {
const handleChange = e => setSelected(e.target.value);
const handleClick = () => setSelected(AdvanceProblemKeys.BLANK);
const settings = { 'aria-label': 'checkbox', type: 'radio' };
const settings = { type: 'radio' };
return (
<Container style={{ width: '494px', height: '400px' }}>
<SelectableBox.Set
name="problem-type"
columns={1}
onChange={handleChange}
type={settings.type}
@@ -30,6 +41,7 @@ const ProblemTypeSelect = ({
<SelectableBox
className="border border-light-400 text-primary-500 shadow-none"
id={key}
key={key}
value={key}
{...settings}
>
@@ -45,10 +57,5 @@ const ProblemTypeSelect = ({
</Container>
);
};
ProblemTypeSelect.propTypes = {
selected: PropTypes.string.isRequired,
setSelected: PropTypes.func.isRequired,
};
export const ProblemTypeSelectInternal = ProblemTypeSelect; // For testing only
export default injectIntl(ProblemTypeSelect);
export default ProblemTypeSelect;

View File

@@ -13,6 +13,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default
className="border-primary-100 border-bottom py-3 pl-2.5 pr-4"
>
<IconButton
alt="Go back"
iconAs="Icon"
onClick={[Function]}
/>
@@ -36,6 +37,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default
>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="blankadvanced"
>
<Radio
id="blankadvanced"
@@ -47,6 +49,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="circuitschematic"
>
<Radio
id="circuitschematic"
@@ -57,7 +60,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-circuitschematic"
>
<div
className="text-left"
>
@@ -91,6 +96,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="jsinputresponse"
>
<Radio
id="jsinputresponse"
@@ -102,6 +108,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="customgrader"
>
<Radio
id="customgrader"
@@ -112,7 +119,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-customgrader"
>
<div
className="text-left"
>
@@ -146,6 +155,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="imageresponse"
>
<Radio
id="imageresponse"
@@ -156,7 +166,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-imageresponse"
>
<div
className="text-left"
>
@@ -190,6 +202,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="formularesponse"
>
<Radio
id="formularesponse"
@@ -201,6 +214,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="problemwithhint"
>
<Radio
id="problemwithhint"
@@ -211,7 +225,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-problemwithhint"
>
<div
className="text-left"
>
@@ -271,6 +287,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
className="border-primary-100 border-bottom py-3 pl-2.5 pr-4"
>
<IconButton
alt="Go back"
iconAs="Icon"
onClick={[Function]}
/>
@@ -294,6 +311,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="blankadvanced"
>
<Radio
id="blankadvanced"
@@ -305,6 +323,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="circuitschematic"
>
<Radio
id="circuitschematic"
@@ -315,7 +334,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-circuitschematic"
>
<div
className="text-left"
>
@@ -349,6 +370,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="jsinputresponse"
>
<Radio
id="jsinputresponse"
@@ -360,6 +382,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="customgrader"
>
<Radio
id="customgrader"
@@ -370,7 +393,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-customgrader"
>
<div
className="text-left"
>
@@ -404,6 +429,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="imageresponse"
>
<Radio
id="imageresponse"
@@ -414,7 +440,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-imageresponse"
>
<div
className="text-left"
>
@@ -448,6 +476,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="formularesponse"
>
<Radio
id="formularesponse"
@@ -459,6 +488,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="problemwithhint"
>
<Radio
id="problemwithhint"
@@ -469,7 +499,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-problemwithhint"
>
<div
className="text-left"
>
@@ -529,6 +561,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
className="border-primary-100 border-bottom py-3 pl-2.5 pr-4"
>
<IconButton
alt="Go back"
iconAs="Icon"
onClick={[Function]}
/>
@@ -552,6 +585,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="blankadvanced"
>
<Radio
id="blankadvanced"
@@ -563,6 +597,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="circuitschematic"
>
<Radio
id="circuitschematic"
@@ -573,7 +608,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-circuitschematic"
>
<div
className="text-left"
>
@@ -607,6 +644,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="jsinputresponse"
>
<Radio
id="jsinputresponse"
@@ -618,6 +656,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="customgrader"
>
<Radio
id="customgrader"
@@ -628,7 +667,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-customgrader"
>
<div
className="text-left"
>
@@ -662,6 +703,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="imageresponse"
>
<Radio
id="imageresponse"
@@ -672,7 +714,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-imageresponse"
>
<div
className="text-left"
>
@@ -706,6 +750,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="formularesponse"
>
<Radio
id="formularesponse"
@@ -717,6 +762,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="problemwithhint"
>
<Radio
id="problemwithhint"
@@ -727,265 +773,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<div
className="text-left"
>
{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 { }
}
</div>
</Tooltip>
}
placement="right"
>
<div
className="text-gray-500"
>
Not supported
</div>
</OverlayTrigger>
</ActionRow>
</RadioSet>
</Form.Group>
<Hyperlink
destination="https://edx.readthedocs.io/projects/edx-partner-course-staff/en/latest/exercises_tools/create_exercises_and_tools.html#advanced"
target="_blank"
>
<FormattedMessage
defaultMessage="Learn more about advanced problem types"
description="Label for Learn more about advanced problem types button"
id="authoring.problemEditor.advanceProblem.learnMoreButtonLabel.label"
/>
</Hyperlink>
</Col>
`;
exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problemType is drag_and_drop 1`] = `
<Col
className="justify-content-center"
md={8}
xs={12}
>
<Form.Group
className="border rounded text-primary-500 p-0"
>
<ActionRow
className="border-primary-100 border-bottom py-3 pl-2.5 pr-4"
>
<IconButton
iconAs="Icon"
onClick={[Function]}
/>
<ActionRow.Spacer />
<Form.Label
className="h4"
>
<FormattedMessage
defaultMessage="Advanced problems"
description="Title for advanced problem menu"
id="authoring.problemEditor.advanceProblem.menu.title"
/>
</Form.Label>
<ActionRow.Spacer />
</ActionRow>
<RadioSet
className="px-4"
name="advanceTypes"
onChange={[Function]}
value="drag_and_drop"
>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
>
<Radio
id="blankadvanced"
value="blankadvanced"
>
Blank problem
</Radio>
<ActionRow.Spacer />
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
>
<Radio
id="circuitschematic"
value="circuitschematic"
>
Circuit schematic builder
</Radio>
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<div
className="text-left"
>
{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 { }
}
</div>
</Tooltip>
}
placement="right"
>
<div
className="text-gray-500"
>
Not supported
</div>
</OverlayTrigger>
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
>
<Radio
id="jsinputresponse"
value="jsinputresponse"
>
Custom JavaScript display and grading
</Radio>
<ActionRow.Spacer />
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
>
<Radio
id="customgrader"
value="customgrader"
>
Custom Python-evaluated input
</Radio>
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<div
className="text-left"
>
{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 { }
}
</div>
</Tooltip>
}
placement="right"
>
<div
className="text-gray-500"
>
Provisional
</div>
</OverlayTrigger>
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
>
<Radio
id="imageresponse"
value="imageresponse"
>
Image mapped input
</Radio>
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<div
className="text-left"
>
{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 { }
}
</div>
</Tooltip>
}
placement="right"
>
<div
className="text-gray-500"
>
Not supported
</div>
</OverlayTrigger>
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
>
<Radio
id="formularesponse"
value="formularesponse"
>
Math expression input
</Radio>
<ActionRow.Spacer />
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
>
<Radio
id="problemwithhint"
value="problemwithhint"
>
Problem with adaptive hint
</Radio>
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-problemwithhint"
>
<div
className="text-left"
>
@@ -1045,6 +835,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
className="border-primary-100 border-bottom py-3 pl-2.5 pr-4"
>
<IconButton
alt="Go back"
iconAs="Icon"
onClick={[Function]}
/>
@@ -1068,6 +859,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="blankadvanced"
>
<Radio
id="blankadvanced"
@@ -1079,6 +871,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="circuitschematic"
>
<Radio
id="circuitschematic"
@@ -1089,7 +882,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-circuitschematic"
>
<div
className="text-left"
>
@@ -1123,6 +918,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="jsinputresponse"
>
<Radio
id="jsinputresponse"
@@ -1134,6 +930,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="customgrader"
>
<Radio
id="customgrader"
@@ -1144,7 +941,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-customgrader"
>
<div
className="text-left"
>
@@ -1178,6 +977,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="imageresponse"
>
<Radio
id="imageresponse"
@@ -1188,7 +988,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-imageresponse"
>
<div
className="text-left"
>
@@ -1222,6 +1024,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="formularesponse"
>
<Radio
id="formularesponse"
@@ -1233,6 +1036,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="problemwithhint"
>
<Radio
id="problemwithhint"
@@ -1243,7 +1047,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-problemwithhint"
>
<div
className="text-left"
>
@@ -1303,6 +1109,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
className="border-primary-100 border-bottom py-3 pl-2.5 pr-4"
>
<IconButton
alt="Go back"
iconAs="Icon"
onClick={[Function]}
/>
@@ -1326,6 +1133,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="blankadvanced"
>
<Radio
id="blankadvanced"
@@ -1337,6 +1145,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="circuitschematic"
>
<Radio
id="circuitschematic"
@@ -1347,7 +1156,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-circuitschematic"
>
<div
className="text-left"
>
@@ -1381,6 +1192,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="jsinputresponse"
>
<Radio
id="jsinputresponse"
@@ -1392,6 +1204,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="customgrader"
>
<Radio
id="customgrader"
@@ -1402,7 +1215,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-customgrader"
>
<div
className="text-left"
>
@@ -1436,6 +1251,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="imageresponse"
>
<Radio
id="imageresponse"
@@ -1446,7 +1262,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-imageresponse"
>
<div
className="text-left"
>
@@ -1480,6 +1298,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="formularesponse"
>
<Radio
id="formularesponse"
@@ -1491,6 +1310,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="problemwithhint"
>
<Radio
id="problemwithhint"
@@ -1501,7 +1321,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-problemwithhint"
>
<div
className="text-left"
>
@@ -1548,7 +1370,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</Col>
`;
exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problemType is jsinput_response 1`] = `
exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problemType is jsinputresponse 1`] = `
<Col
className="justify-content-center"
md={8}
@@ -1561,6 +1383,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
className="border-primary-100 border-bottom py-3 pl-2.5 pr-4"
>
<IconButton
alt="Go back"
iconAs="Icon"
onClick={[Function]}
/>
@@ -1580,10 +1403,11 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
className="px-4"
name="advanceTypes"
onChange={[Function]}
value="jsinput_response"
value="jsinputresponse"
>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="blankadvanced"
>
<Radio
id="blankadvanced"
@@ -1595,6 +1419,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="circuitschematic"
>
<Radio
id="circuitschematic"
@@ -1605,7 +1430,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-circuitschematic"
>
<div
className="text-left"
>
@@ -1639,6 +1466,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="jsinputresponse"
>
<Radio
id="jsinputresponse"
@@ -1650,6 +1478,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="customgrader"
>
<Radio
id="customgrader"
@@ -1660,7 +1489,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-customgrader"
>
<div
className="text-left"
>
@@ -1694,6 +1525,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="imageresponse"
>
<Radio
id="imageresponse"
@@ -1704,7 +1536,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-imageresponse"
>
<div
className="text-left"
>
@@ -1738,6 +1572,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="formularesponse"
>
<Radio
id="formularesponse"
@@ -1749,6 +1584,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="problemwithhint"
>
<Radio
id="problemwithhint"
@@ -1759,7 +1595,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-problemwithhint"
>
<div
className="text-left"
>
@@ -1806,7 +1644,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</Col>
`;
exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problemType is problem_with_hint 1`] = `
exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problemType is problemwithhint 1`] = `
<Col
className="justify-content-center"
md={8}
@@ -1819,6 +1657,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
className="border-primary-100 border-bottom py-3 pl-2.5 pr-4"
>
<IconButton
alt="Go back"
iconAs="Icon"
onClick={[Function]}
/>
@@ -1838,10 +1677,11 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
className="px-4"
name="advanceTypes"
onChange={[Function]}
value="problem_with_hint"
value="problemwithhint"
>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="blankadvanced"
>
<Radio
id="blankadvanced"
@@ -1853,6 +1693,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="circuitschematic"
>
<Radio
id="circuitschematic"
@@ -1863,7 +1704,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-circuitschematic"
>
<div
className="text-left"
>
@@ -1897,6 +1740,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="jsinputresponse"
>
<Radio
id="jsinputresponse"
@@ -1908,6 +1752,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="customgrader"
>
<Radio
id="customgrader"
@@ -1918,7 +1763,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-customgrader"
>
<div
className="text-left"
>
@@ -1952,6 +1799,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="imageresponse"
>
<Radio
id="imageresponse"
@@ -1962,7 +1810,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-imageresponse"
>
<div
className="text-left"
>
@@ -1996,6 +1846,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="formularesponse"
>
<Radio
id="formularesponse"
@@ -2007,6 +1858,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
</ActionRow>
<ActionRow
className="border-primary-100 border-bottom m-0 py-3 w-100"
key="problemwithhint"
>
<Radio
id="problemwithhint"
@@ -2017,7 +1869,9 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem
<ActionRow.Spacer />
<OverlayTrigger
overlay={
<Tooltip>
<Tooltip
id="tooltip-adv-problemwithhint"
>
<div
className="text-left"
>

View File

@@ -11,18 +11,19 @@ exports[`ProblemTypeSelect snapshot DROPDOWN 1`] = `
>
<ForwardRef
columns={1}
name="problem-type"
onChange={[Function]}
type="radio"
value="optionresponse"
>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="multiplechoiceresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="multiplechoiceresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -31,13 +32,13 @@ exports[`ProblemTypeSelect snapshot DROPDOWN 1`] = `
Single select
</ForwardRef>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="choiceresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="choiceresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -46,13 +47,13 @@ exports[`ProblemTypeSelect snapshot DROPDOWN 1`] = `
Multi-select
</ForwardRef>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="optionresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="optionresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -61,13 +62,13 @@ exports[`ProblemTypeSelect snapshot DROPDOWN 1`] = `
Dropdown
</ForwardRef>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="numericalresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="numericalresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -76,13 +77,13 @@ exports[`ProblemTypeSelect snapshot DROPDOWN 1`] = `
Numerical input
</ForwardRef>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="stringresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="stringresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -116,18 +117,19 @@ exports[`ProblemTypeSelect snapshot MULTISELECT 1`] = `
>
<ForwardRef
columns={1}
name="problem-type"
onChange={[Function]}
type="radio"
value="choiceresponse"
>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="multiplechoiceresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="multiplechoiceresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -136,13 +138,13 @@ exports[`ProblemTypeSelect snapshot MULTISELECT 1`] = `
Single select
</ForwardRef>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="choiceresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="choiceresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -151,13 +153,13 @@ exports[`ProblemTypeSelect snapshot MULTISELECT 1`] = `
Multi-select
</ForwardRef>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="optionresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="optionresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -166,13 +168,13 @@ exports[`ProblemTypeSelect snapshot MULTISELECT 1`] = `
Dropdown
</ForwardRef>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="numericalresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="numericalresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -181,13 +183,13 @@ exports[`ProblemTypeSelect snapshot MULTISELECT 1`] = `
Numerical input
</ForwardRef>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="stringresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="stringresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -221,18 +223,19 @@ exports[`ProblemTypeSelect snapshot NUMERIC 1`] = `
>
<ForwardRef
columns={1}
name="problem-type"
onChange={[Function]}
type="radio"
value="numericalresponse"
>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="multiplechoiceresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="multiplechoiceresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -241,13 +244,13 @@ exports[`ProblemTypeSelect snapshot NUMERIC 1`] = `
Single select
</ForwardRef>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="choiceresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="choiceresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -256,13 +259,13 @@ exports[`ProblemTypeSelect snapshot NUMERIC 1`] = `
Multi-select
</ForwardRef>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="optionresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="optionresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -271,13 +274,13 @@ exports[`ProblemTypeSelect snapshot NUMERIC 1`] = `
Dropdown
</ForwardRef>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="numericalresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="numericalresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -286,13 +289,13 @@ exports[`ProblemTypeSelect snapshot NUMERIC 1`] = `
Numerical input
</ForwardRef>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="stringresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="stringresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -326,18 +329,19 @@ exports[`ProblemTypeSelect snapshot SINGLESELECT 1`] = `
>
<ForwardRef
columns={1}
name="problem-type"
onChange={[Function]}
type="radio"
value="multiplechoiceresponse"
>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="multiplechoiceresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="multiplechoiceresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -346,13 +350,13 @@ exports[`ProblemTypeSelect snapshot SINGLESELECT 1`] = `
Single select
</ForwardRef>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="choiceresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="choiceresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -361,13 +365,13 @@ exports[`ProblemTypeSelect snapshot SINGLESELECT 1`] = `
Multi-select
</ForwardRef>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="optionresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="optionresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -376,13 +380,13 @@ exports[`ProblemTypeSelect snapshot SINGLESELECT 1`] = `
Dropdown
</ForwardRef>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="numericalresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="numericalresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -391,13 +395,13 @@ exports[`ProblemTypeSelect snapshot SINGLESELECT 1`] = `
Numerical input
</ForwardRef>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="stringresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="stringresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -431,18 +435,19 @@ exports[`ProblemTypeSelect snapshot TEXTINPUT 1`] = `
>
<ForwardRef
columns={1}
name="problem-type"
onChange={[Function]}
type="radio"
value="stringresponse"
>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="multiplechoiceresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="multiplechoiceresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -451,13 +456,13 @@ exports[`ProblemTypeSelect snapshot TEXTINPUT 1`] = `
Single select
</ForwardRef>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="choiceresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="choiceresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -466,13 +471,13 @@ exports[`ProblemTypeSelect snapshot TEXTINPUT 1`] = `
Multi-select
</ForwardRef>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="optionresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="optionresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -481,13 +486,13 @@ exports[`ProblemTypeSelect snapshot TEXTINPUT 1`] = `
Dropdown
</ForwardRef>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="numericalresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="numericalresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"
@@ -496,13 +501,13 @@ exports[`ProblemTypeSelect snapshot TEXTINPUT 1`] = `
Numerical input
</ForwardRef>
<ForwardRef
aria-label="checkbox"
checked={false}
className="border border-light-400 text-primary-500 shadow-none"
id="stringresponse"
inputHidden={true}
isIndeterminate={false}
isInvalid={false}
key="stringresponse"
onClick={[Function]}
onFocus={[Function]}
type="radio"

View File

@@ -12,6 +12,11 @@ const messages = defineMessages({
defaultMessage: 'Advanced problems',
description: 'Title for advanced problem menu',
},
advanceMenuGoBack: {
id: 'authoring.problemEditor.advanceProblem.menu.goBack',
defaultMessage: 'Go back',
description: 'Return to the previous menu that shows basic problem types',
},
advanceProblemTypeLabel: {
id: 'authoring.problemEditor.advanceProblem.problemType.label',
defaultMessage: '{problemType}',

View File

@@ -1,28 +1,10 @@
import { useEffect, useState } from 'react';
import { useEffect } from 'react';
import {
AdvanceProblemKeys, AdvanceProblems, ProblemTypeKeys, ProblemTypes,
} from '../../../../data/constants/problem';
import { StrictDict, snakeCaseKeys } from '../../../../utils';
// This 'module' self-import hack enables mocking during tests.
// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested
// should be re-thought and cleaned up to avoid this pattern.
// eslint-disable-next-line import/no-self-import
import * as module from './hooks';
import { snakeCaseKeys } from '../../../../utils';
import { getDataFromOlx } from '../../../../data/redux/thunkActions/problem';
export const state = StrictDict({
// eslint-disable-next-line react-hooks/rules-of-hooks
selected: (val) => useState(val),
});
export const selectHooks = () => {
const [selected, setSelected] = module.state.selected(ProblemTypeKeys.SINGLESELECT);
return {
selected,
setSelected,
};
};
export const onSelect = ({
selected,
updateField,

View File

@@ -1,11 +1,6 @@
/* eslint-disable prefer-destructuring */
import React from 'react';
import { MockUseState } from '../../../../testUtils';
// This 'module' self-import hack enables mocking during tests.
// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested
// should be re-thought and cleaned up to avoid this pattern.
// eslint-disable-next-line import/no-self-import
import * as module from './hooks';
import * as hooks from './hooks';
import { AdvanceProblems, ProblemTypeKeys, ProblemTypes } from '../../../../data/constants/problem';
import { getDataFromOlx } from '../../../../data/redux/thunkActions/problem';
@@ -15,7 +10,6 @@ jest.mock('react', () => ({
useEffect: jest.fn(),
}));
const state = new MockUseState(module);
const mockUpdateField = jest.fn().mockName('updateField');
const mockSelected = 'multiplechoiceresponse';
const mockAdvancedSelected = 'circuitschematic';
@@ -28,35 +22,14 @@ const mockDefaultSettings = {
showanswer: 'always',
};
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({
hooks.onSelect({
selected: mockAdvancedSelected,
updateField: mockUpdateField,
setBlockTitle: mocksetBlockTitle,
@@ -68,7 +41,7 @@ describe('SelectTypeModal hooks', () => {
expect(mocksetBlockTitle).toHaveBeenCalledWith(AdvanceProblems[mockAdvancedSelected].title);
});
test('updateField is called with selected on visual propblems', () => {
module.onSelect({
hooks.onSelect({
selected: mockSelected,
updateField: mockUpdateField,
setBlockTitle: mocksetBlockTitle,
@@ -106,7 +79,7 @@ describe('SelectTypeModal hooks', () => {
describe('SINGLESELECT', () => {
beforeEach(() => {
module.useArrowNav(ProblemTypeKeys.SINGLESELECT, mockSetSelected);
hooks.useArrowNav(ProblemTypeKeys.SINGLESELECT, mockSetSelected);
[cb, prereqs] = React.useEffect.mock.calls[0];
cb();
});
@@ -125,7 +98,7 @@ describe('SelectTypeModal hooks', () => {
});
describe('MULTISELECT', () => {
beforeEach(() => {
module.useArrowNav(ProblemTypeKeys.MULTISELECT, mockSetSelected);
hooks.useArrowNav(ProblemTypeKeys.MULTISELECT, mockSetSelected);
[cb, prereqs] = React.useEffect.mock.calls[0];
cb();
});
@@ -144,7 +117,7 @@ describe('SelectTypeModal hooks', () => {
});
describe('DROPDOWN', () => {
beforeEach(() => {
module.useArrowNav(ProblemTypeKeys.DROPDOWN, mockSetSelected);
hooks.useArrowNav(ProblemTypeKeys.DROPDOWN, mockSetSelected);
[cb, prereqs] = React.useEffect.mock.calls[0];
cb();
});
@@ -163,7 +136,7 @@ describe('SelectTypeModal hooks', () => {
});
describe('NUMERIC', () => {
beforeEach(() => {
module.useArrowNav(ProblemTypeKeys.NUMERIC, mockSetSelected);
hooks.useArrowNav(ProblemTypeKeys.NUMERIC, mockSetSelected);
[cb, prereqs] = React.useEffect.mock.calls[0];
cb();
});
@@ -182,7 +155,7 @@ describe('SelectTypeModal hooks', () => {
});
describe('TEXTINPUT', () => {
beforeEach(() => {
module.useArrowNav(ProblemTypeKeys.TEXTINPUT, mockSetSelected);
hooks.useArrowNav(ProblemTypeKeys.TEXTINPUT, mockSetSelected);
[cb, prereqs] = React.useEffect.mock.calls[0];
cb();
});

View File

@@ -1,22 +0,0 @@
import 'CourseAuthoring/editors/setupEditorTest';
import React from 'react';
import { shallow } from '@edx/react-unit-test-utils';
import SelectTypeModal 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(<SelectTypeModal {...props} />).snapshot).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,47 @@
import { Provider } from 'react-redux';
import {
fireEvent,
render,
screen,
initializeMocks,
} from '../../../../../testUtils';
import editorStore from '../../../../data/store';
import * as hooks from './hooks';
import SelectTypeModal from '.';
describe('SelectTypeModal', () => {
beforeEach(() => {
initializeMocks();
});
test('it can select a basic problem type', async () => {
const mockClose = jest.fn();
const mockSelect = jest.fn();
jest.spyOn(hooks, 'onSelect').mockImplementation(mockSelect);
// This is a new-style test, unlike most of the old snapshot-based editor tests.
render(<Provider store={editorStore}><SelectTypeModal onClose={mockClose} /></Provider>);
// First we see the menu of problem types:
expect(await screen.findByRole('button', { name: 'Numerical input' })).toBeInTheDocument();
// And the "Advanced" types are not yet listed:
expect(screen.queryByRole('radio', { name: 'Custom JavaScript display and grading' })).not.toBeInTheDocument();
// Before we select a problem type, try viewing the advanced types and then going back:
const advancedButton = await screen.findByRole('button', { name: 'Advanced problem types' });
fireEvent.click(advancedButton);
// Now we see the advanced types:
await screen.findByRole('radio', { name: 'Custom JavaScript display and grading' });
expect(screen.queryByRole('button', { name: 'Numerical input' })).not.toBeInTheDocument();
const goBackButton = screen.getByRole('button', { name: 'Go back' });
fireEvent.click(goBackButton);
const numericalInputBtn = await screen.findByRole('button', { name: 'Numerical input' });
fireEvent.click(numericalInputBtn);
// Now we save our selection:
const selectBtn = screen.getByRole('button', { name: 'Select' });
fireEvent.click(selectBtn);
expect(mockSelect).toHaveBeenLastCalledWith(expect.objectContaining({ selected: 'numericalresponse' }));
});
});

View File

@@ -1,5 +1,4 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Row, Stack } from '@openedx/paragon';
import ProblemTypeSelect from './content/ProblemTypeSelect';
@@ -7,18 +6,27 @@ import Preview from './content/Preview';
import AdvanceTypeSelect from './content/AdvanceTypeSelect';
import SelectTypeWrapper from './SelectTypeWrapper';
import * as hooks from './hooks';
import { AdvanceProblemKeys } from '../../../../data/constants/problem';
import {
AdvancedProblemType,
isAdvancedProblemType,
ProblemType,
ProblemTypeKeys,
} from '../../../../data/constants/problem';
const SelectTypeModal = ({
interface Props {
onClose: (() => void) | null;
}
const SelectTypeModal: React.FC<Props> = ({
onClose,
}) => {
const { selected, setSelected } = hooks.selectHooks();
const [selected, setSelected] = React.useState<ProblemType | AdvancedProblemType>(ProblemTypeKeys.SINGLESELECT);
hooks.useArrowNav(selected, setSelected);
return (
<SelectTypeWrapper onClose={onClose} selected={selected}>
<Row className="justify-content-center">
{(!Object.values(AdvanceProblemKeys).includes(selected)) ? (
{(!isAdvancedProblemType(selected)) ? (
<Stack direction="horizontal" gap={4} className="flex-wrap mb-6">
<ProblemTypeSelect selected={selected} setSelected={setSelected} />
<Preview problemType={selected} />
@@ -29,8 +37,4 @@ const SelectTypeModal = ({
);
};
SelectTypeModal.propTypes = {
onClose: PropTypes.func.isRequired,
};
export default SelectTypeModal;

View File

@@ -65,7 +65,6 @@ describe('ProblemEditor', () => {
const wrapper = shallow(<ProblemEditor
{...props}
blockFinished
studioViewFinished
advancedSettingsFinished
blockFailed
/>);
@@ -75,7 +74,6 @@ describe('ProblemEditor', () => {
const wrapper = shallow(<ProblemEditor
{...props}
blockFinished
studioViewFinished
advancedSettingsFinished
/>);
expect(wrapper.instance.findByType('SelectTypeModal')).toHaveLength(1);
@@ -85,7 +83,6 @@ describe('ProblemEditor', () => {
{...props}
problemType="multiplechoiceresponse"
blockFinished
studioViewFinished
advancedSettingsFinished
/>);
expect(wrapper.instance.findByType('EditProblemView')).toHaveLength(1);

View File

@@ -1,17 +1,29 @@
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { Spinner } from '@openedx/paragon';
import { injectIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import SelectTypeModal from './components/SelectTypeModal';
import EditProblemView from './components/EditProblemView';
import { selectors, thunkActions } from '../../data/redux';
import { RequestKeys } from '../../data/constants/requests';
import messages from './messages';
import { ProblemType } from '../../data/constants/problem';
import type { EditorComponent } from '../../EditorComponent';
const ProblemEditor = ({
export interface Props extends EditorComponent {
// redux
advancedSettingsFinished: boolean;
blockFinished: boolean;
blockFailed: boolean;
/** null if this is a new problem */
problemType: ProblemType | null;
initializeProblemEditor: (blockValue: any) => void;
blockValue: Record<string, any>;
}
const ProblemEditor: React.FC<Props> = ({
onClose,
returnFunction,
returnFunction = null,
// Redux
problemType,
blockFinished,
@@ -52,21 +64,6 @@ const ProblemEditor = ({
return (<EditProblemView {...{ onClose, returnFunction }} />);
};
ProblemEditor.defaultProps = {
returnFunction: null,
};
ProblemEditor.propTypes = {
onClose: PropTypes.func.isRequired,
returnFunction: PropTypes.func,
// redux
advancedSettingsFinished: PropTypes.bool.isRequired,
blockFinished: PropTypes.bool.isRequired,
blockFailed: PropTypes.bool.isRequired,
problemType: PropTypes.string.isRequired,
initializeProblemEditor: PropTypes.func.isRequired,
blockValue: PropTypes.objectOf(PropTypes.shape({})).isRequired,
};
export const mapStateToProps = (state) => ({
blockFinished: selectors.requests.isFinished(state, { requestKey: RequestKeys.fetchBlock }),
blockFailed: selectors.requests.isFailed(state, { requestKey: RequestKeys.fetchBlock }),
@@ -80,4 +77,4 @@ export const mapDispatchToProps = {
};
export const ProblemEditorInternal = ProblemEditor; // For testing only
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(ProblemEditor));
export default connect(mapStateToProps, mapDispatchToProps)(ProblemEditor);

View File

@@ -14,7 +14,8 @@ export const ProblemTypeKeys = StrictDict({
NUMERIC: 'numericalresponse',
TEXTINPUT: 'stringresponse',
ADVANCED: 'advanced',
});
} as const);
export type ProblemType = typeof ProblemTypeKeys[keyof typeof ProblemTypeKeys];
export const ProblemTypes = StrictDict({
[ProblemTypeKeys.SINGLESELECT]: {
@@ -84,7 +85,12 @@ export const AdvanceProblemKeys = StrictDict({
IMAGE: 'imageresponse',
FORMULA: 'formularesponse',
PROBLEMWITHHINT: 'problemwithhint',
});
} as const);
export type AdvancedProblemType = typeof AdvanceProblemKeys[keyof typeof AdvanceProblemKeys];
export function isAdvancedProblemType(pt: ProblemType | AdvancedProblemType): pt is AdvancedProblemType {
return Object.values(AdvanceProblemKeys).includes(pt as any);
}
export const AdvanceProblems = StrictDict({
[AdvanceProblemKeys.BLANK]: {
@@ -122,7 +128,7 @@ export const AdvanceProblems = StrictDict({
status: 'Not supported',
template: advancedOlxTemplates.problemWithHint,
},
});
} as const);
export const ShowAnswerTypesKeys = StrictDict({
ALWAYS: 'always',
@@ -137,7 +143,7 @@ export const ShowAnswerTypesKeys = StrictDict({
AFTER_ALL_ATTEMPTS: 'after_all_attempts',
AFTER_ALL_ATTEMPTS_OR_CORRECT: 'after_all_attempts_or_correct',
ATTEMPTED_NO_PAST_DUE: 'attempted_no_past_due',
});
} as const);
export const ShowAnswerTypes = StrictDict({
[ShowAnswerTypesKeys.ALWAYS]: {
@@ -188,14 +194,14 @@ export const ShowAnswerTypes = StrictDict({
id: 'authoring.problemeditor.settings.showanswertype.attempted_no_past_due',
defaultMessage: 'Attempted',
},
});
} as const);
export const RandomizationTypesKeys = StrictDict({
NEVER: 'never',
ALWAYS: 'always',
ONRESET: 'onreset',
PERSTUDENT: 'per_student',
});
} as const);
export const RandomizationTypes = StrictDict({
[RandomizationTypesKeys.ALWAYS]: {
@@ -214,9 +220,9 @@ export const RandomizationTypes = StrictDict({
id: 'authoring.problemeditor.settings.RandomizationTypes.perstudent',
defaultMessage: 'Per Student',
},
});
} as const);
export const RichTextProblems = [ProblemTypeKeys.SINGLESELECT, ProblemTypeKeys.MULTISELECT];
export const RichTextProblems = [ProblemTypeKeys.SINGLESELECT, ProblemTypeKeys.MULTISELECT] as const;
export const settingsOlxAttributes = [
'@_display_name',
@@ -226,4 +232,4 @@ export const settingsOlxAttributes = [
'@_show_reset_button',
'@_submission_wait_seconds',
'@_attempts_before_showanswer_button',
];
] as const;

View File

@@ -20,7 +20,7 @@ const modules = {
const moduleProps = (propName) => Object.keys(modules).reduce(
(obj, moduleKey) => ({ ...obj, [moduleKey]: modules[moduleKey][propName] }),
{},
/** @type {Record<string, any>} */({}),
);
const rootReducer = combineReducers(moduleProps('reducer'));

View File

@@ -1,5 +1,15 @@
// These additional mocks and setup are required for some tests in src/editors/
// and are imported on an as-needed basis.
// /////////////////////////////////////////////////////////////////////////////
// TODO: tests using this 'setupEditorTest', shallow rendering, and snapshots
// should be replaced with modern tests that use src/testUtils.ts and
// @testing-library/react. See
// src/editors/containers/ProblemEditor/components/SelectTypeModal/index.test.tsx
// for an example of an editor test in the new format.
// /////////////////////////////////////////////////////////////////////////////
import { formatMessage as mockFormatMessage } from './testUtils';
// eslint-disable-next-line import/no-extraneous-dependencies
import 'jest-canvas-mock';
@@ -8,7 +18,7 @@ jest.mock('@edx/frontend-platform/i18n', () => {
const PropTypes = jest.requireActual('prop-types');
return {
...i18n,
useIntl: () => ({ formatMessage: (m) => m.defaultMessage }),
useIntl: () => ({ formatMessage: mockFormatMessage }),
intlShape: PropTypes.shape({
formatMessage: PropTypes.func,
}),

View File

@@ -15,6 +15,6 @@ const supportedEditors = {
[blockTypes.video_upload]: VideoUploadEditor,
// ADDED_EDITORS GO BELOW
[blockTypes.game]: GameEditor,
};
} as const;
export default supportedEditors;

View File

@@ -19,6 +19,6 @@ const strictGet = (target, name) => {
return undefined;
};
const StrictDict = (dict) => new Proxy(dict, { get: strictGet });
const StrictDict = <T extends Record<string, any>>(dict: T) => new Proxy(dict, { get: strictGet }) as T;
export default StrictDict;

View File

@@ -7,6 +7,7 @@
import React from 'react';
import { AxiosError } from 'axios';
import { jest } from '@jest/globals';
import type { Store } from 'redux';
import { initializeMockApp } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { IntlProvider } from '@edx/frontend-platform/i18n';
@@ -25,7 +26,7 @@ import { ToastContext, type ToastContextData } from './generic/toast-context';
import initializeReduxStore from './store';
/** @deprecated Use React Query and/or regular React Context instead of redux */
let reduxStore;
let reduxStore: Store;
let queryClient;
let axiosMock: MockAdapter;