diff --git a/src/custom.d.ts b/src/custom.d.ts index 2b94311838..63c5b2cf57 100644 --- a/src/custom.d.ts +++ b/src/custom.d.ts @@ -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; diff --git a/src/editors/Editor.jsx b/src/editors/Editor.tsx similarity index 66% rename from src/editors/Editor.jsx rename to src/editors/Editor.tsx index 044c773f26..cc42647f56 100644 --- a/src/editors/Editor.jsx +++ b/src/editors/Editor.tsx @@ -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 = ({ learningContextId, blockType, blockId, lmsEndpointUrl, studioEndpointUrl, - onClose, - returnFunction, + onClose = null, + returnFunction = null, }) => { const dispatch = useDispatch(); hooks.initializeApp({ @@ -46,23 +54,5 @@ const Editor = ({ ); }; -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; diff --git a/src/editors/EditorComponent.ts b/src/editors/EditorComponent.ts new file mode 100644 index 0000000000..e670ff6f5d --- /dev/null +++ b/src/editors/EditorComponent.ts @@ -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; +} diff --git a/src/editors/containers/EditorContainer/messages.js b/src/editors/containers/EditorContainer/messages.ts similarity index 100% rename from src/editors/containers/EditorContainer/messages.js rename to src/editors/containers/EditorContainer/messages.ts diff --git a/src/editors/containers/ProblemEditor/__snapshots__/index.test.jsx.snap b/src/editors/containers/ProblemEditor/__snapshots__/index.test.tsx.snap similarity index 100% rename from src/editors/containers/ProblemEditor/__snapshots__/index.test.jsx.snap rename to src/editors/containers/ProblemEditor/__snapshots__/index.test.tsx.snap diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.jsx index 733f87a1a2..cd41940bb3 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.jsx @@ -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, -}) => ( -
- - - - - - - -
-); +}) => { + 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 ( +
+ + + + + + + +
+ ); +}; 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; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.test.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.test.jsx index c00cd02d48..d0795c36ba 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.test.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.test.jsx @@ -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); - }); - }); }); 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 index 6c9bc201a1..fb228f3205 100644 --- 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 @@ -17,6 +17,7 @@ exports[`SelectTypeWrapper snapshot 1`] = ` className="pgn__modal-close-container" > @@ -30,7 +31,7 @@ exports[`SelectTypeWrapper snapshot 1`] = ` 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 f747783f5a..e452967172 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.jsx @@ -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 (
@@ -51,5 +54,4 @@ SelectTypeWrapper.propTypes = { onClose: PropTypes.func, }; -export const SelectTypeWrapperInternal = SelectTypeWrapper; // For testing only -export default injectIntl(SelectTypeWrapper); +export default SelectTypeWrapper; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.test.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.test.jsx index ec2e3a51a6..8ac7456052 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.test.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.test.jsx @@ -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', () => ({ diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/messages.js b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/messages.ts similarity index 100% rename from src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/messages.js rename to src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/messages.ts 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 deleted file mode 100644 index 51f08eada8..0000000000 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/__snapshots__/index.test.jsx.snap +++ /dev/null @@ -1,26 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`SelectTypeModal snapshot 1`] = ` - - - - - - - - -`; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.test.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.test.tsx similarity index 68% rename from src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.test.jsx rename to src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.test.tsx index 978b35e7c5..09d0e40e39 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.test.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.test.tsx @@ -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().snapshot, ).toMatchSnapshot(); }); - test('snapshots: renders as expected with problemType is drag_and_drop', () => { - expect( - shallow().snapshot, - ).toMatchSnapshot(); - }); test('snapshots: renders as expected with problemType is formularesponse', () => { expect( shallow().snapshot, @@ -44,14 +34,14 @@ describe('AdvanceTypeSelect', () => { shallow().snapshot, ).toMatchSnapshot(); }); - test('snapshots: renders as expected with problemType is jsinput_response', () => { + test('snapshots: renders as expected with problemType is jsinputresponse', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); - test('snapshots: renders as expected with problemType is problem_with_hint', () => { + test('snapshots: renders as expected with problemType is problemwithhint', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); }); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.tsx similarity index 77% rename from src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.jsx rename to src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.tsx index d60061ddd1..2add431b27 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.tsx @@ -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; +} + +const AdvanceTypeSelect: React.FC = ({ selected, setSelected, - // injected - intl, }) => { + const intl = useIntl(); const handleChange = e => { setSelected(e.target.value); }; return ( - setSelected(ProblemTypeKeys.SINGLESELECT)} /> + setSelected(ProblemTypeKeys.SINGLESELECT)} + /> @@ -43,7 +56,7 @@ const AdvanceTypeSelect = ({ {Object.entries(AdvanceProblems).map(([type, data]) => { if (data.status !== '') { return ( - + {intl.formatMessage(messages.advanceProblemTypeLabel, { problemType: data.title })} @@ -51,7 +64,7 @@ const AdvanceTypeSelect = ({ +
{intl.formatMessage(messages.supportStatusTooltipMessage, { supportStatus: data.status.replace(' ', '_') })}
@@ -66,7 +79,7 @@ const AdvanceTypeSelect = ({ ); } return ( - + {intl.formatMessage(messages.advanceProblemTypeLabel, { problemType: data.title })} @@ -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; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.test.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.test.tsx similarity index 89% rename from src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.test.jsx rename to src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.test.tsx index 7482f843ef..e7d433c92d 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.test.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.test.tsx @@ -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(), }; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.tsx similarity index 69% rename from src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.jsx rename to src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.tsx index e58d731af2..a87f329042 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.tsx @@ -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 = ({ 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 ( @@ -45,10 +57,5 @@ const ProblemTypeSelect = ({ ); }; -ProblemTypeSelect.propTypes = { - selected: PropTypes.string.isRequired, - setSelected: PropTypes.func.isRequired, -}; -export const ProblemTypeSelectInternal = ProblemTypeSelect; // For testing only -export default injectIntl(ProblemTypeSelect); +export default ProblemTypeSelect; 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.tsx.snap similarity index 86% rename from src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/AdvanceTypeSelect.test.jsx.snap rename to src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/AdvanceTypeSelect.test.tsx.snap index 79a7990579..73f572377d 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/AdvanceTypeSelect.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/AdvanceTypeSelect.test.tsx.snap @@ -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" > @@ -36,6 +37,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default > +
@@ -91,6 +96,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default +
@@ -146,6 +155,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default +
@@ -190,6 +202,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with default +
@@ -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" > @@ -294,6 +311,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem > +
@@ -349,6 +370,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem +
@@ -404,6 +429,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem +
@@ -448,6 +476,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem +
@@ -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" > @@ -552,6 +585,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem > +
@@ -607,6 +644,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem +
@@ -662,6 +703,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem +
@@ -706,6 +750,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem -
- {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 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 -
-
-
- - - 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 - - - +
@@ -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" > @@ -1068,6 +859,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem > +
@@ -1123,6 +918,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem +
@@ -1178,6 +977,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem +
@@ -1222,6 +1024,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem +
@@ -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" > @@ -1326,6 +1133,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem > +
@@ -1381,6 +1192,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem +
@@ -1436,6 +1251,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem +
@@ -1480,6 +1298,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem +
@@ -1548,7 +1370,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem `; -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`] = ` @@ -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" > +
@@ -1639,6 +1466,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem +
@@ -1694,6 +1525,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem +
@@ -1738,6 +1572,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem +
@@ -1806,7 +1644,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem `; -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`] = ` @@ -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" > +
@@ -1897,6 +1740,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem +
@@ -1952,6 +1799,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem +
@@ -1996,6 +1846,7 @@ exports[`AdvanceTypeSelect snapshots snapshots: renders as expected with problem +
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.tsx.snap similarity index 93% rename from src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/ProblemTypeSelect.test.jsx.snap rename to src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/ProblemTypeSelect.test.tsx.snap index 157708524f..f21b392688 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/ProblemTypeSelect.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/__snapshots__/ProblemTypeSelect.test.tsx.snap @@ -11,18 +11,19 @@ exports[`ProblemTypeSelect snapshot DROPDOWN 1`] = ` > useState(val), -}); - -export const selectHooks = () => { - const [selected, setSelected] = module.state.selected(ProblemTypeKeys.SINGLESELECT); - return { - selected, - setSelected, - }; -}; - export const onSelect = ({ selected, updateField, diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.test.js b/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.test.js index e0582b5fb2..0c7f987f4c 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.test.js +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.test.js @@ -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(); }); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.test.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.test.jsx deleted file mode 100644 index 4574c1b799..0000000000 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.test.jsx +++ /dev/null @@ -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().snapshot).toMatchSnapshot(); - }); -}); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.test.tsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.test.tsx new file mode 100644 index 0000000000..9be7578c5f --- /dev/null +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.test.tsx @@ -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(); + + // 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' })); + }); +}); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.tsx similarity index 69% rename from src/editors/containers/ProblemEditor/components/SelectTypeModal/index.jsx rename to src/editors/containers/ProblemEditor/components/SelectTypeModal/index.tsx index 828dc4be05..fbb17fad43 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.tsx @@ -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 = ({ onClose, }) => { - const { selected, setSelected } = hooks.selectHooks(); + const [selected, setSelected] = React.useState(ProblemTypeKeys.SINGLESELECT); hooks.useArrowNav(selected, setSelected); return ( - {(!Object.values(AdvanceProblemKeys).includes(selected)) ? ( + {(!isAdvancedProblemType(selected)) ? ( @@ -29,8 +37,4 @@ const SelectTypeModal = ({ ); }; -SelectTypeModal.propTypes = { - onClose: PropTypes.func.isRequired, -}; - export default SelectTypeModal; diff --git a/src/editors/containers/ProblemEditor/index.test.jsx b/src/editors/containers/ProblemEditor/index.test.tsx similarity index 98% rename from src/editors/containers/ProblemEditor/index.test.jsx rename to src/editors/containers/ProblemEditor/index.test.tsx index f6866b56a6..ef31cdaac5 100644 --- a/src/editors/containers/ProblemEditor/index.test.jsx +++ b/src/editors/containers/ProblemEditor/index.test.tsx @@ -65,7 +65,6 @@ describe('ProblemEditor', () => { const wrapper = shallow(); @@ -75,7 +74,6 @@ describe('ProblemEditor', () => { const wrapper = shallow(); 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); diff --git a/src/editors/containers/ProblemEditor/index.jsx b/src/editors/containers/ProblemEditor/index.tsx similarity index 72% rename from src/editors/containers/ProblemEditor/index.jsx rename to src/editors/containers/ProblemEditor/index.tsx index 5f342ad1e4..293b7da6ac 100644 --- a/src/editors/containers/ProblemEditor/index.jsx +++ b/src/editors/containers/ProblemEditor/index.tsx @@ -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; +} + +const ProblemEditor: React.FC = ({ onClose, - returnFunction, + returnFunction = null, // Redux problemType, blockFinished, @@ -52,21 +64,6 @@ const ProblemEditor = ({ return (); }; -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); diff --git a/src/editors/data/constants/problem.js b/src/editors/data/constants/problem.ts similarity index 94% rename from src/editors/data/constants/problem.js rename to src/editors/data/constants/problem.ts index 5dcc8558cc..e0cb91d593 100644 --- a/src/editors/data/constants/problem.js +++ b/src/editors/data/constants/problem.ts @@ -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; diff --git a/src/editors/data/redux/index.js b/src/editors/data/redux/index.js index 84e5648bda..e231759a92 100644 --- a/src/editors/data/redux/index.js +++ b/src/editors/data/redux/index.js @@ -20,7 +20,7 @@ const modules = { const moduleProps = (propName) => Object.keys(modules).reduce( (obj, moduleKey) => ({ ...obj, [moduleKey]: modules[moduleKey][propName] }), - {}, + /** @type {Record} */({}), ); const rootReducer = combineReducers(moduleProps('reducer')); diff --git a/src/editors/setupEditorTest.js b/src/editors/setupEditorTest.js index 47df9429a2..88129bd9a9 100644 --- a/src/editors/setupEditorTest.js +++ b/src/editors/setupEditorTest.js @@ -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, }), diff --git a/src/editors/supportedEditors.js b/src/editors/supportedEditors.ts similarity index 98% rename from src/editors/supportedEditors.js rename to src/editors/supportedEditors.ts index 82d9c568a7..71d0bad737 100644 --- a/src/editors/supportedEditors.js +++ b/src/editors/supportedEditors.ts @@ -15,6 +15,6 @@ const supportedEditors = { [blockTypes.video_upload]: VideoUploadEditor, // ADDED_EDITORS GO BELOW [blockTypes.game]: GameEditor, -}; +} as const; export default supportedEditors; diff --git a/src/editors/utils/StrictDict.js b/src/editors/utils/StrictDict.ts similarity index 82% rename from src/editors/utils/StrictDict.js rename to src/editors/utils/StrictDict.ts index d73ac811da..c7b69c161a 100644 --- a/src/editors/utils/StrictDict.js +++ b/src/editors/utils/StrictDict.ts @@ -19,6 +19,6 @@ const strictGet = (target, name) => { return undefined; }; -const StrictDict = (dict) => new Proxy(dict, { get: strictGet }); +const StrictDict = >(dict: T) => new Proxy(dict, { get: strictGet }) as T; export default StrictDict; diff --git a/src/testUtils.tsx b/src/testUtils.tsx index 283e341f48..7481797621 100644 --- a/src/testUtils.tsx +++ b/src/testUtils.tsx @@ -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;