Skip to content

Commit f49c6a5

Browse files
refactor: replacing injectIntl with useIntl() (#458)
1 parent d71edbd commit f49c6a5

File tree

10 files changed

+192
-212
lines changed

10 files changed

+192
-212
lines changed

src/containers/ListView/SubmissionsTable.jsx

Lines changed: 84 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
TextFilter,
99
MultiSelectDropdownFilter,
1010
} from '@openedx/paragon';
11-
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
11+
import { useIntl } from '@edx/frontend-platform/i18n';
1212

1313
import { gradingStatuses, submissionFields } from 'data/services/lms/constants';
1414
import lmsMessages from 'data/services/lms/messages';
@@ -25,113 +25,108 @@ import messages from './messages';
2525
/**
2626
* <SubmissionsTable />
2727
*/
28-
export class SubmissionsTable extends React.Component {
29-
get gradeStatusOptions() {
30-
return Object.keys(gradingStatuses).map(statusKey => ({
31-
name: this.translate(lmsMessages[gradingStatuses[statusKey]]),
32-
value: gradingStatuses[statusKey],
33-
}));
34-
}
28+
export const SubmissionsTable = ({
29+
isIndividual,
30+
listData,
31+
loadSelectionForReview,
32+
}) => {
33+
const intl = useIntl();
3534

36-
get userLabel() {
37-
return this.translate(this.props.isIndividual ? messages.username : messages.teamName);
38-
}
35+
const translate = (...args) => intl.formatMessage(...args);
3936

40-
get userAccessor() {
41-
return this.props.isIndividual
42-
? submissionFields.username
43-
: submissionFields.teamName;
44-
}
37+
const gradeStatusOptions = Object.keys(gradingStatuses).map(statusKey => ({
38+
name: translate(lmsMessages[gradingStatuses[statusKey]]),
39+
value: gradingStatuses[statusKey],
40+
}));
4541

46-
get dateSubmittedLabel() {
47-
return this.translate(this.props.isIndividual
48-
? messages.learnerSubmissionDate
49-
: messages.teamSubmissionDate);
50-
}
42+
const userLabel = translate(isIndividual ? messages.username : messages.teamName);
43+
44+
const userAccessor = isIndividual
45+
? submissionFields.username
46+
: submissionFields.teamName;
5147

52-
formatDate = ({ value }) => {
48+
const dateSubmittedLabel = translate(isIndividual
49+
? messages.learnerSubmissionDate
50+
: messages.teamSubmissionDate);
51+
52+
const formatDate = ({ value }) => {
5353
const date = new Date(moment(value));
5454
return date.toLocaleString();
5555
};
5656

57-
formatGrade = ({ value: score }) => (
57+
const formatGrade = ({ value: score }) => (
5858
score === null ? '-' : `${score.pointsEarned}/${score.pointsPossible}`
5959
);
6060

61-
formatStatus = ({ value }) => (<StatusBadge status={value} />);
62-
63-
translate = (...args) => this.props.intl.formatMessage(...args);
61+
const formatStatus = ({ value }) => (<StatusBadge status={value} />);
6462

65-
handleViewAllResponsesClick = (data) => () => {
63+
const handleViewAllResponsesClick = (data) => () => {
6664
const getSubmissionUUID = (row) => row.original.submissionUUID;
67-
this.props.loadSelectionForReview(data.map(getSubmissionUUID));
65+
loadSelectionForReview(data.map(getSubmissionUUID));
6866
};
6967

70-
render() {
71-
if (!this.props.listData.length) {
72-
return null;
73-
}
74-
return (
75-
<div className="submissions-table">
76-
<DataTable
77-
data-testid="data-table"
78-
isFilterable
79-
FilterStatusComponent={FilterStatusComponent}
80-
numBreakoutFilters={2}
81-
defaultColumnValues={{ Filter: TextFilter }}
82-
isSelectable
83-
isSortable
84-
isPaginated
85-
itemCount={this.props.listData.length}
86-
initialState={{ pageSize: 10, pageIndex: 0 }}
87-
data={this.props.listData}
88-
tableActions={[
89-
<TableAction handleClick={this.handleViewAllResponsesClick} />,
90-
]}
91-
bulkActions={[
92-
<SelectedBulkAction handleClick={this.handleViewAllResponsesClick} />,
93-
]}
94-
columns={[
95-
{
96-
Header: this.userLabel,
97-
accessor: this.userAccessor,
98-
},
99-
{
100-
Header: this.dateSubmittedLabel,
101-
accessor: submissionFields.dateSubmitted,
102-
Cell: this.formatDate,
103-
disableFilters: true,
104-
},
105-
{
106-
Header: this.translate(messages.grade),
107-
accessor: submissionFields.score,
108-
Cell: this.formatGrade,
109-
disableFilters: true,
110-
},
111-
{
112-
Header: this.translate(messages.gradingStatus),
113-
accessor: submissionFields.gradingStatus,
114-
Cell: this.formatStatus,
115-
Filter: MultiSelectDropdownFilter,
116-
filter: 'includesValue',
117-
filterChoices: this.gradeStatusOptions,
118-
},
119-
]}
120-
>
121-
<DataTable.TableControlBar />
122-
<DataTable.Table />
123-
<DataTable.TableFooter />
124-
</DataTable>
125-
</div>
126-
);
68+
if (!listData.length) {
69+
return null;
12770
}
128-
}
71+
72+
return (
73+
<div className="submissions-table">
74+
<DataTable
75+
data-testid="data-table"
76+
isFilterable
77+
FilterStatusComponent={FilterStatusComponent}
78+
numBreakoutFilters={2}
79+
defaultColumnValues={{ Filter: TextFilter }}
80+
isSelectable
81+
isSortable
82+
isPaginated
83+
itemCount={listData.length}
84+
initialState={{ pageSize: 10, pageIndex: 0 }}
85+
data={listData}
86+
tableActions={[
87+
<TableAction handleClick={handleViewAllResponsesClick} />,
88+
]}
89+
bulkActions={[
90+
<SelectedBulkAction handleClick={handleViewAllResponsesClick} />,
91+
]}
92+
columns={[
93+
{
94+
Header: userLabel,
95+
accessor: userAccessor,
96+
},
97+
{
98+
Header: dateSubmittedLabel,
99+
accessor: submissionFields.dateSubmitted,
100+
Cell: formatDate,
101+
disableFilters: true,
102+
},
103+
{
104+
Header: translate(messages.grade),
105+
accessor: submissionFields.score,
106+
Cell: formatGrade,
107+
disableFilters: true,
108+
},
109+
{
110+
Header: translate(messages.gradingStatus),
111+
accessor: submissionFields.gradingStatus,
112+
Cell: formatStatus,
113+
Filter: MultiSelectDropdownFilter,
114+
filter: 'includesValue',
115+
filterChoices: gradeStatusOptions,
116+
},
117+
]}
118+
>
119+
<DataTable.TableControlBar />
120+
<DataTable.Table />
121+
<DataTable.TableFooter />
122+
</DataTable>
123+
</div>
124+
);
125+
};
129126
SubmissionsTable.defaultProps = {
130127
listData: [],
131128
};
132129
SubmissionsTable.propTypes = {
133-
// injected
134-
intl: intlShape.isRequired,
135130
// redux
136131
isIndividual: PropTypes.bool.isRequired,
137132
listData: PropTypes.arrayOf(PropTypes.shape({
@@ -155,4 +150,4 @@ export const mapDispatchToProps = {
155150
loadSelectionForReview: thunkActions.grading.loadSelectionForReview,
156151
};
157152

158-
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(SubmissionsTable));
153+
export default connect(mapStateToProps, mapDispatchToProps)(SubmissionsTable);

src/containers/ListView/SubmissionsTable.test.jsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { selectors, thunkActions } from 'data/redux';
1010
import { gradingStatuses as statuses, submissionFields } from 'data/services/lms/constants';
1111

1212
import StatusBadge from 'components/StatusBadge';
13-
import { formatMessage } from 'testUtils';
1413
import messages from './messages';
1514
import {
1615
SubmissionsTable,
@@ -117,7 +116,6 @@ describe('SubmissionsTable component', () => {
117116
};
118117
beforeEach(() => {
119118
props.loadSelectionForReview = jest.fn();
120-
props.intl = { formatMessage };
121119
});
122120
describe('render tests', () => {
123121
const mockMethod = (methodName) => {
@@ -272,13 +270,26 @@ describe('SubmissionsTable component', () => {
272270
});
273271
describe('handleViewAllResponsesClick', () => {
274272
it('calls loadSelectionForReview with submissionUUID from all rows if there are no selectedRows', () => {
273+
// Test the integration by simulating the function call directly
274+
// Since handleViewAllResponsesClick is internal to the functional component,
275+
// we test through the component behavior
275276
const data = [
276277
{ original: { submissionUUID: '123' } },
277278
{ original: { submissionUUID: '456' } },
278279
{ original: { submissionUUID: '789' } },
279280
];
280-
el.instance.children[0].props.tableActions[0].props.handleClick(data)();
281-
expect(el.shallowRenderer._instance.props.loadSelectionForReview).toHaveBeenCalledWith(['123', '456', '789']); // eslint-disable-line no-underscore-dangle
281+
282+
// Create a test instance that we can call the function on
283+
const testEl = shallow(<SubmissionsTable {...props} />);
284+
const tableProps = testEl.instance.findByTestId('data-table')[0].props;
285+
286+
// Get the handleClick function from the TableAction props
287+
const handleClickFunction = tableProps.tableActions[0].props.handleClick;
288+
289+
// Call the function as TableAction would call it
290+
handleClickFunction(data)();
291+
292+
expect(props.loadSelectionForReview).toHaveBeenCalledWith(['123', '456', '789']);
282293
});
283294
});
284295
});

src/containers/ReviewActions/components/SubmissionNavigation.jsx

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { connect } from 'react-redux';
44

55
import { Icon, IconButton } from '@openedx/paragon';
66
import { ChevronLeft, ChevronRight } from '@openedx/paragon/icons';
7-
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
7+
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
88

99
import { selectors, thunkActions } from 'data/redux';
1010
import messages from './messages';
@@ -13,50 +13,50 @@ import messages from './messages';
1313
* <SubmissionNavigation />
1414
*/
1515
export const SubmissionNavigation = ({
16-
intl,
1716
hasPrevSubmission,
1817
hasNextSubmission,
1918
loadPrev,
2019
loadNext,
2120
activeIndex,
2221
selectionLength,
2322
allowNavigation,
24-
}) => (
25-
<span className="submission-navigation">
26-
<IconButton
27-
className="ml-1"
28-
size="inline"
29-
disabled={!hasPrevSubmission || !allowNavigation}
30-
alt={intl.formatMessage(messages.loadPrevious)}
31-
src={ChevronLeft}
32-
iconAs={Icon}
33-
onClick={loadPrev}
34-
/>
35-
<span className="ml-1">
36-
<FormattedMessage
37-
{...messages.navigationLabel}
38-
values={{ current: activeIndex + 1, total: selectionLength }}
23+
}) => {
24+
const intl = useIntl();
25+
return (
26+
<span className="submission-navigation">
27+
<IconButton
28+
className="ml-1"
29+
size="inline"
30+
disabled={!hasPrevSubmission || !allowNavigation}
31+
alt={intl.formatMessage(messages.loadPrevious)}
32+
src={ChevronLeft}
33+
iconAs={Icon}
34+
onClick={loadPrev}
35+
/>
36+
<span className="ml-1">
37+
<FormattedMessage
38+
{...messages.navigationLabel}
39+
values={{ current: activeIndex + 1, total: selectionLength }}
40+
/>
41+
</span>
42+
<IconButton
43+
className="ml-1"
44+
size="inline"
45+
disabled={!hasNextSubmission || !allowNavigation}
46+
alt={intl.formatMessage(messages.loadNext)}
47+
src={ChevronRight}
48+
iconAs={Icon}
49+
onClick={loadNext}
3950
/>
4051
</span>
41-
<IconButton
42-
className="ml-1"
43-
size="inline"
44-
disabled={!hasNextSubmission || !allowNavigation}
45-
alt={intl.formatMessage(messages.loadNext)}
46-
src={ChevronRight}
47-
iconAs={Icon}
48-
onClick={loadNext}
49-
/>
50-
</span>
51-
);
52+
);
53+
};
5254
SubmissionNavigation.defaultProps = {
5355
hasPrevSubmission: false,
5456
hasNextSubmission: false,
5557
allowNavigation: false,
5658
};
5759
SubmissionNavigation.propTypes = {
58-
// injected
59-
intl: intlShape.isRequired,
6060
// redux
6161
allowNavigation: PropTypes.bool,
6262
activeIndex: PropTypes.number.isRequired,
@@ -80,4 +80,4 @@ export const mapDispatchToProps = {
8080
loadPrev: thunkActions.grading.loadPrev,
8181
};
8282

83-
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(SubmissionNavigation));
83+
export default connect(mapStateToProps, mapDispatchToProps)(SubmissionNavigation);

src/containers/ReviewActions/components/SubmissionNavigation.test.jsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import { shallow } from '@edx/react-unit-test-utils';
33

44
import { selectors, thunkActions } from 'data/redux';
55

6-
import { formatMessage } from 'testUtils';
7-
86
import {
97
SubmissionNavigation,
108
mapStateToProps,
@@ -28,7 +26,6 @@ jest.mock('data/redux/requests/selectors', () => ({
2826
describe('SubmissionNavigation component', () => {
2927
describe('component', () => {
3028
const props = {
31-
intl: { formatMessage },
3229
activeIndex: 4,
3330
selectionLength: 5,
3431
};

src/containers/ReviewModal/index.jsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react';
22
import { useDispatch } from 'react-redux';
33

44
import { FullscreenModal } from '@openedx/paragon';
5-
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
5+
import { useIntl } from '@edx/frontend-platform/i18n';
66

77
import LoadingMessage from 'components/LoadingMessage';
88
import DemoWarning from 'containers/DemoWarning';
@@ -18,7 +18,8 @@ import './ReviewModal.scss';
1818
/**
1919
* <ReviewModal />
2020
*/
21-
export const ReviewModal = ({ intl }) => {
21+
export const ReviewModal = () => {
22+
const intl = useIntl();
2223
const dispatch = useDispatch();
2324
const {
2425
isLoading,
@@ -48,9 +49,5 @@ export const ReviewModal = ({ intl }) => {
4849
</FullscreenModal>
4950
);
5051
};
51-
ReviewModal.propTypes = {
52-
// injected
53-
intl: intlShape.isRequired,
54-
};
5552

56-
export default injectIntl(ReviewModal);
53+
export default ReviewModal;

0 commit comments

Comments
 (0)