-
Notifications
You must be signed in to change notification settings - Fork 29
/
index.jsx
214 lines (192 loc) · 6.04 KB
/
index.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
import React from 'react';
import PropTypes from 'prop-types';
import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
import { Alert, Pagination, Table } from '@openedx/paragon';
import { Error } from '@openedx/paragon/icons';
import 'font-awesome/css/font-awesome.css';
import TableLoadingSkeleton from './TableLoadingSkeleton';
import TableLoadingOverlay from '../TableLoadingOverlay';
import { updateUrl } from '../../utils';
import { withLocation, withNavigate } from '../../hoc';
class TableComponent extends React.Component {
componentDidMount() {
// Get initial data
this.props.paginateTable();
}
componentDidUpdate(prevProps) {
const { location } = this.props;
// Handle the case where the query params have changed. This is used when sorting & paging, but
// also when the back button is used. We need to determine if this is a pagination or sorting
// request as we handle these as slightly different actions in the action handlers.
if (location.search !== prevProps.location.search) {
const prevQueryParams = new URLSearchParams(prevProps.location.search);
const prevPage = prevQueryParams.get('page');
const prevOrdering = prevQueryParams.get('ordering');
const currentQueryParams = new URLSearchParams(location.search);
const page = currentQueryParams.get('page');
const ordering = currentQueryParams.get('ordering');
if (ordering !== prevOrdering) {
this.props.sortTable(ordering);
} else if (page !== prevPage) {
this.props.paginateTable(parseInt(page, 10));
}
}
}
componentWillUnmount() {
this.props.clearTable();
}
renderTableContent() {
const {
className,
currentPage,
pageCount,
tableSortable,
data,
ordering,
formatData,
id,
loading,
enterpriseId,
defaultSortIndex,
defaultSortType,
navigate,
location,
} = this.props;
const sortByColumn = (column, direction) => {
updateUrl(navigate, location.pathname, {
page: 1,
ordering: direction === 'desc' ? `-${column.key}` : column.key,
});
sendEnterpriseTrackEvent(enterpriseId, 'edx.ui.enterprise.admin_portal.table.sorted', {
tableId: id,
column: column.label,
direction,
});
};
const columnConfig = this.props.columns.map(column => ({
...column,
onSort: column.columnSortable ? (direction) => sortByColumn(column, direction) : null,
}));
let sortDirection;
let sortColumn;
if (tableSortable) {
sortDirection = defaultSortType || (ordering && ordering.indexOf('-') !== -1 ? 'desc' : 'asc');
sortColumn = (ordering && ordering.replace('-', '')) || columnConfig[defaultSortIndex].key;
}
return (
<div className={className}>
<div className="row">
<div className="col">
{loading && <TableLoadingOverlay />}
<div className="table-responsive">
<Table
id={id}
className="table-sm table-striped"
columns={columnConfig}
data={formatData(data)}
tableSortable={tableSortable}
defaultSortedColumn={sortColumn}
defaultSortDirection={sortDirection}
/>
</div>
</div>
</div>
<div className="row mt-2">
<div className="col d-flex justify-content-center">
<Pagination
paginationLabel={`${id}-pagination`}
pageCount={pageCount}
currentPage={currentPage}
onPageSelect={(page) => {
updateUrl(this.props.navigate, this.props.location.pathname, { page });
sendEnterpriseTrackEvent(enterpriseId, 'edx.ui.enterprise.admin_portal.table.paginated', {
tableId: id,
page,
});
}}
/>
</div>
</div>
</div>
);
}
renderLoadingMessage() {
return <TableLoadingSkeleton />;
}
renderErrorMessage() {
return (
<Alert
variant="danger"
icon={Error}
>
<Alert.Heading>Unable to load data</Alert.Heading>
<p>Try refreshing your screen {this.props.error.message}</p>
</Alert>
);
}
renderEmptyDataMessage() {
return (
<Alert
variant="warning"
icon={Error}
>
There are no results.
</Alert>
);
}
render() {
const {
data,
loading,
error,
} = this.props;
return (
<>
{error && this.renderErrorMessage()}
{loading && !data && this.renderLoadingMessage()}
{!loading && !error && data && data.length === 0
&& this.renderEmptyDataMessage()}
{data && data.length > 0 && this.renderTableContent()}
</>
);
}
}
TableComponent.propTypes = {
// Props expected from consumer
id: PropTypes.string.isRequired,
className: PropTypes.string,
columns: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
formatData: PropTypes.func.isRequired,
tableSortable: PropTypes.bool,
defaultSortIndex: PropTypes.number,
defaultSortType: PropTypes.string,
// Props expected from TableContainer / redux store
enterpriseId: PropTypes.string.isRequired,
data: PropTypes.arrayOf(PropTypes.shape({})),
currentPage: PropTypes.number,
pageCount: PropTypes.number,
ordering: PropTypes.string,
loading: PropTypes.bool,
error: PropTypes.instanceOf(Error),
paginateTable: PropTypes.func.isRequired,
sortTable: PropTypes.func.isRequired,
clearTable: PropTypes.func.isRequired,
location: PropTypes.shape({
pathname: PropTypes.string,
search: PropTypes.string,
}).isRequired,
navigate: PropTypes.func,
};
TableComponent.defaultProps = {
className: null,
defaultSortIndex: 0,
defaultSortType: undefined,
tableSortable: false,
data: undefined,
ordering: undefined,
currentPage: undefined,
pageCount: undefined,
error: null,
loading: false,
};
export default withLocation(withNavigate(TableComponent));