Skip to content

Commit b010909

Browse files
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
1 parent 82a3b7c commit b010909

32 files changed

+417
-618
lines changed

src/custom.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ declare module '*.svg' {
33
export default content;
44
}
55

6+
declare module '*.png' {
7+
const content: string;
8+
export default content;
9+
}
10+
611
declare module '*.json' {
712
const value: any;
813
export default value;

src/editors/Editor.jsx renamed to src/editors/Editor.tsx

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
11
import React from 'react';
22
import { useDispatch } from 'react-redux';
3-
import PropTypes from 'prop-types';
43
import { FormattedMessage } from '@edx/frontend-platform/i18n';
54

65
import messages from './messages';
76
import * as hooks from './hooks';
87

98
import supportedEditors from './supportedEditors';
9+
import type { EditorComponent } from './EditorComponent';
1010

11-
const Editor = ({
11+
export interface Props extends EditorComponent {
12+
blockType: string;
13+
blockId: string | null;
14+
learningContextId: string | null;
15+
lmsEndpointUrl: string | null;
16+
studioEndpointUrl: string | null;
17+
}
18+
19+
const Editor: React.FC<Props> = ({
1220
learningContextId,
1321
blockType,
1422
blockId,
1523
lmsEndpointUrl,
1624
studioEndpointUrl,
17-
onClose,
18-
returnFunction,
25+
onClose = null,
26+
returnFunction = null,
1927
}) => {
2028
const dispatch = useDispatch();
2129
hooks.initializeApp({
@@ -46,23 +54,5 @@ const Editor = ({
4654
</div>
4755
);
4856
};
49-
Editor.defaultProps = {
50-
blockId: null,
51-
learningContextId: null,
52-
lmsEndpointUrl: null,
53-
onClose: null,
54-
returnFunction: null,
55-
studioEndpointUrl: null,
56-
};
57-
58-
Editor.propTypes = {
59-
blockId: PropTypes.string,
60-
blockType: PropTypes.string.isRequired,
61-
learningContextId: PropTypes.string,
62-
lmsEndpointUrl: PropTypes.string,
63-
onClose: PropTypes.func,
64-
returnFunction: PropTypes.func,
65-
studioEndpointUrl: PropTypes.string,
66-
};
6757

6858
export default Editor;

src/editors/EditorComponent.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/** Shared interface that all Editor components (like ProblemEditor) adhere to */
2+
export interface EditorComponent {
3+
onClose: (() => void) | null;
4+
// TODO: get a better type for the 'result' here
5+
returnFunction?: (() => (result: any) => void) | null;
6+
}
Lines changed: 38 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
3-
import { connect } from 'react-redux';
4-
3+
import { useDispatch, useSelector } from 'react-redux';
4+
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
55
import {
66
ActionRow,
77
Button,
88
ModalDialog,
99
} from '@openedx/paragon';
10-
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
1110
import messages from './messages';
1211
import * as hooks from '../hooks';
1312

@@ -16,68 +15,49 @@ import { actions, selectors } from '../../../../../data/redux';
1615
const SelectTypeFooter = ({
1716
onCancel,
1817
selected,
19-
// redux
20-
defaultSettings,
21-
updateField,
22-
setBlockTitle,
23-
// injected,
24-
intl,
25-
}) => (
26-
<div className="editor-footer fixed-bottom">
27-
<ModalDialog.Footer className="border-top-0">
28-
<ActionRow>
29-
<ActionRow.Spacer />
30-
<Button
31-
aria-label={intl.formatMessage(messages.cancelButtonAriaLabel)}
32-
variant="tertiary"
33-
onClick={onCancel}
34-
>
35-
<FormattedMessage {...messages.cancelButtonLabel} />
36-
</Button>
37-
<Button
38-
aria-label={intl.formatMessage(messages.selectButtonAriaLabel)}
39-
onClick={hooks.onSelect({
40-
selected,
41-
updateField,
42-
setBlockTitle,
43-
defaultSettings,
44-
})}
45-
disabled={!selected}
46-
>
47-
<FormattedMessage {...messages.selectButtonLabel} />
48-
</Button>
49-
</ActionRow>
50-
</ModalDialog.Footer>
51-
</div>
52-
);
18+
}) => {
19+
const intl = useIntl();
20+
const defaultSettings = useSelector(selectors.problem.defaultSettings);
21+
const dispatch = useDispatch();
22+
const updateField = React.useCallback((data) => dispatch(actions.problem.updateField(data)), [dispatch]);
23+
const setBlockTitle = React.useCallback((title) => dispatch(actions.app.setBlockTitle(title)), [dispatch]);
24+
return (
25+
<div className="editor-footer fixed-bottom">
26+
<ModalDialog.Footer className="border-top-0">
27+
<ActionRow>
28+
<ActionRow.Spacer />
29+
<Button
30+
aria-label={intl.formatMessage(messages.cancelButtonAriaLabel)}
31+
variant="tertiary"
32+
onClick={onCancel}
33+
>
34+
<FormattedMessage {...messages.cancelButtonLabel} />
35+
</Button>
36+
<Button
37+
aria-label={intl.formatMessage(messages.selectButtonAriaLabel)}
38+
onClick={hooks.onSelect({
39+
selected,
40+
updateField,
41+
setBlockTitle,
42+
defaultSettings,
43+
})}
44+
disabled={!selected}
45+
>
46+
<FormattedMessage {...messages.selectButtonLabel} />
47+
</Button>
48+
</ActionRow>
49+
</ModalDialog.Footer>
50+
</div>
51+
);
52+
};
5353

5454
SelectTypeFooter.defaultProps = {
5555
selected: null,
5656
};
5757

5858
SelectTypeFooter.propTypes = {
59-
defaultSettings: PropTypes.shape({
60-
maxAttempts: PropTypes.number,
61-
rerandomize: PropTypes.string,
62-
showResetButton: PropTypes.bool,
63-
showanswer: PropTypes.string,
64-
}).isRequired,
6559
onCancel: PropTypes.func.isRequired,
6660
selected: PropTypes.string,
67-
updateField: PropTypes.func.isRequired,
68-
setBlockTitle: PropTypes.func.isRequired,
69-
// injected
70-
intl: intlShape.isRequired,
71-
};
72-
73-
export const mapStateToProps = (state) => ({
74-
defaultSettings: selectors.problem.defaultSettings(state),
75-
});
76-
77-
export const mapDispatchToProps = {
78-
updateField: actions.problem.updateField,
79-
setBlockTitle: actions.app.setBlockTitle,
8061
};
8162

82-
export const SelectTypeFooterInternal = SelectTypeFooter; // For testing only
83-
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(SelectTypeFooter));
63+
export default SelectTypeFooter;

src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.test.jsx

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
import 'CourseAuthoring/editors/setupEditorTest';
2-
import React from 'react';
32
import { shallow } from '@edx/react-unit-test-utils';
43

54
import { Button } from '@openedx/paragon';
65
import { formatMessage } from '../../../../../testUtils';
7-
import * as module from './SelectTypeFooter';
6+
import SelectTypeFooter from './SelectTypeFooter';
87
import * as hooks from '../hooks';
9-
import { actions } from '../../../../../data/redux';
10-
11-
const SelectTypeFooter = module.SelectTypeFooterInternal;
128

139
jest.mock('../hooks', () => ({
1410
onSelect: jest.fn().mockName('onSelect'),
@@ -46,15 +42,4 @@ describe('SelectTypeFooter', () => {
4642
.toEqual(expected);
4743
});
4844
});
49-
50-
describe('mapStateToProps', () => {
51-
test('is empty', () => {
52-
expect(module.mapDispatchToProps.defaultSettings).toEqual(actions.problem.defaultSettings);
53-
});
54-
});
55-
describe('mapDispatchToProps', () => {
56-
test('loads updateField from problem.updateField actions', () => {
57-
expect(module.mapDispatchToProps.updateField).toEqual(actions.problem.updateField);
58-
});
59-
});
6045
});

src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/__snapshots__/index.test.jsx.snap

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ exports[`SelectTypeWrapper snapshot 1`] = `
1717
className="pgn__modal-close-container"
1818
>
1919
<IconButton
20+
alt="Exit the editor"
2021
iconAs="Icon"
2122
src={[MockFunction icons.Close]}
2223
/>
@@ -30,7 +31,7 @@ exports[`SelectTypeWrapper snapshot 1`] = `
3031
test child
3132
</h1>
3233
</ModalDialog.Body>
33-
<injectIntl(ShimmedIntlComponent)
34+
<SelectTypeFooter
3435
selected="iMAsElecTedValUE"
3536
/>
3637
</div>

src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.jsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
3-
import { injectIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
3+
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
44
import { Icon, ModalDialog, IconButton } from '@openedx/paragon';
55
import { Close } from '@openedx/paragon/icons';
66
import SelectTypeFooter from './SelectTypeFooter';
77

88
import * as hooks from '../../../../EditorContainer/hooks';
9+
import ecMessages from '../../../../EditorContainer/messages';
910
import messages from './messages';
1011

1112
const SelectTypeWrapper = ({
@@ -14,6 +15,7 @@ const SelectTypeWrapper = ({
1415
selected,
1516
}) => {
1617
const handleCancel = hooks.handleCancel({ onClose });
18+
const intl = useIntl();
1719

1820
return (
1921
<div
@@ -27,6 +29,7 @@ const SelectTypeWrapper = ({
2729
src={Close}
2830
iconAs={Icon}
2931
onClick={handleCancel}
32+
alt={intl.formatMessage(ecMessages.exitButtonAlt)}
3033
/>
3134
</div>
3235
</ModalDialog.Title>
@@ -51,5 +54,4 @@ SelectTypeWrapper.propTypes = {
5154
onClose: PropTypes.func,
5255
};
5356

54-
export const SelectTypeWrapperInternal = SelectTypeWrapper; // For testing only
55-
export default injectIntl(SelectTypeWrapper);
57+
export default SelectTypeWrapper;

src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.test.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import 'CourseAuthoring/editors/setupEditorTest';
22
import React from 'react';
33
import { shallow } from '@edx/react-unit-test-utils';
44
import { IconButton } from '@openedx/paragon';
5-
import { SelectTypeWrapperInternal as SelectTypeWrapper } from '.';
5+
import SelectTypeWrapper from '.';
66
import { handleCancel } from '../../../../EditorContainer/hooks';
77

88
jest.mock('../../../../EditorContainer/hooks', () => ({

0 commit comments

Comments
 (0)