Skip to content

Commit 519feab

Browse files
authored
fix: bug in datatable with multi select (#1394)
* fix: bug in datatable with multi select * fix: in progress file * fix: changes to SelectionStatusComponent
1 parent b842e83 commit 519feab

File tree

9 files changed

+246
-155
lines changed

9 files changed

+246
-155
lines changed

src/components/PeopleManagement/AddMembersModal/AddMembersModalContent.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import AddMembersModalSummary from './AddMembersModalSummary';
1212
import InviteSummaryCount from '../../learner-credit-management/invite-modal/InviteSummaryCount';
1313
import FileUpload from '../../learner-credit-management/invite-modal/FileUpload';
1414
import { EMAIL_ADDRESSES_INPUT_VALUE_DEBOUNCE_DELAY, isInviteEmailAddressesInputValueValid } from '../../learner-credit-management/cards/data';
15-
import EnterpriseCustomerUserDatatable from '../EnterpriseCustomerUserDatatable';
15+
import EnterpriseCustomerUserDataTable from '../EnterpriseCustomerUserDataTable';
1616
import { useEnterpriseLearners } from '../../learner-credit-management/data';
1717
import { HELP_CENTER_URL } from '../constants';
1818

@@ -133,7 +133,7 @@ const AddMembersModalContent = ({
133133
<hr className="my-4" />
134134
</Col>
135135
</Row>
136-
<EnterpriseCustomerUserDatatable
136+
<EnterpriseCustomerUserDataTable
137137
onHandleAddMembersBulkAction={handleAddMembersBulkAction}
138138
onHandleRemoveMembersBulkAction={handleRemoveMembersBulkAction}
139139
learnerEmails={learnerEmails}

src/components/PeopleManagement/CreateGroupModalContent.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import InviteModalSummary from '../learner-credit-management/invite-modal/Invite
1212
import InviteSummaryCount from '../learner-credit-management/invite-modal/InviteSummaryCount';
1313
import FileUpload from '../learner-credit-management/invite-modal/FileUpload';
1414
import { EMAIL_ADDRESSES_INPUT_VALUE_DEBOUNCE_DELAY, isInviteEmailAddressesInputValueValid } from '../learner-credit-management/cards/data';
15-
import { MAX_LENGTH_GROUP_NAME, HELP_CENTER_URL } from './constants';
16-
import EnterpriseCustomerUserDatatable from './EnterpriseCustomerUserDatatable';
15+
import { HELP_CENTER_URL, MAX_LENGTH_GROUP_NAME } from './constants';
16+
import EnterpriseCustomerUserDataTable from './EnterpriseCustomerUserDataTable';
1717
import { useEnterpriseLearners } from '../learner-credit-management/data';
1818

1919
const CreateGroupModalContent = ({
@@ -162,7 +162,7 @@ const CreateGroupModalContent = ({
162162
<hr className="my-4" />
163163
</Col>
164164
</Row>
165-
<EnterpriseCustomerUserDatatable
165+
<EnterpriseCustomerUserDataTable
166166
onHandleAddMembersBulkAction={handleAddMembersBulkAction}
167167
onHandleRemoveMembersBulkAction={handleRemoveMembersBulkAction}
168168
learnerEmails={learnerEmails}
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
import {
2+
createContext, useCallback, useContext, useEffect, useMemo, useState,
3+
} from 'react';
4+
import { connect } from 'react-redux';
5+
import PropTypes from 'prop-types';
6+
import {
7+
CheckboxControl, DataTable, DataTableContext, Icon, TextFilter,
8+
} from '@openedx/paragon';
9+
import { Check } from '@openedx/paragon/icons';
10+
11+
import {
12+
GROUP_MEMBERS_TABLE_PAGE_SIZE, GROUP_MEMBERS_TABLE_DEFAULT_PAGE,
13+
} from './constants';
14+
import MemberDetailsCell from './MemberDetailsCell';
15+
import AddMembersBulkAction from './GroupDetailPage/AddMembersBulkAction';
16+
import RemoveMembersBulkAction from './RemoveMembersBulkAction';
17+
import MemberJoinedDateCell from './MemberJoinedDateCell';
18+
import { useEnterpriseMembersTableData } from './data/hooks';
19+
20+
export const BaseSelectWithContext = ({ row, enterpriseGroupLearners }) => {
21+
const {
22+
indeterminate,
23+
checked,
24+
...toggleRowSelectedProps
25+
} = row.getToggleRowSelectedProps();
26+
const isAddedMember = enterpriseGroupLearners.find(learner => learner.lmsUserId === Number(row.id));
27+
return (
28+
<div>
29+
<CheckboxControl
30+
{...toggleRowSelectedProps}
31+
title="Toggle row selected"
32+
checked={checked}
33+
disabled={isAddedMember}
34+
style={{ cursor: isAddedMember ? null : 'pointer' }}
35+
/>
36+
</div>
37+
);
38+
};
39+
const FilterStatus = (rest) => <DataTable.FilterStatus showFilteredFields={false} {...rest} />;
40+
41+
const EnterpriseCustomerUserDataTableContext = createContext();
42+
43+
const useCheckboxControlProps = (props) => {
44+
const updatedProps = useMemo(
45+
() => {
46+
const { indeterminate, ...rest } = props;
47+
return { isIndeterminate: indeterminate, ...rest };
48+
},
49+
[props],
50+
);
51+
return updatedProps;
52+
};
53+
54+
const addSelectedRowAction = (row, itemCount) => ({
55+
type: 'ADD ROW', row, itemCount,
56+
});
57+
58+
const deleteSelectedRowAction = (rowId) => ({
59+
type: 'DELETE ROW', rowId,
60+
});
61+
62+
const CustomSelectColumnCell = ({ row }) => {
63+
const { enterpriseGroupLearners } = useContext(EnterpriseCustomerUserDataTableContext);
64+
const [isAddedMember, setIsAddedMember] = useState(false);
65+
const {
66+
itemCount,
67+
controlledTableSelections: [, dispatch],
68+
} = useContext(DataTableContext);
69+
70+
const toggleSelected = useCallback(
71+
() => {
72+
if (row.isSelected) {
73+
dispatch(deleteSelectedRowAction(row.id));
74+
} else {
75+
dispatch(addSelectedRowAction(row, itemCount));
76+
}
77+
},
78+
[itemCount, row, dispatch],
79+
);
80+
81+
useEffect(() => {
82+
setIsAddedMember(!!enterpriseGroupLearners.find(learner => learner.lmsUserId === Number(row.id)));
83+
}, [enterpriseGroupLearners, row.id]);
84+
85+
const checkboxControlProps = useCheckboxControlProps(
86+
row.getToggleRowSelectedProps(),
87+
);
88+
89+
if (isAddedMember) {
90+
return (<Icon src={Check} />);
91+
}
92+
93+
return (
94+
<div className="pgn__data-table__controlled-select">
95+
<CheckboxControl
96+
{...checkboxControlProps}
97+
onChange={toggleSelected}
98+
disabled={isAddedMember}
99+
style={{ cursor: isAddedMember ? null : 'pointer' }}
100+
/>
101+
</div>
102+
);
103+
};
104+
105+
CustomSelectColumnCell.propTypes = {
106+
row: PropTypes.shape({
107+
getToggleRowSelectedProps: PropTypes.func.isRequired,
108+
id: PropTypes.string,
109+
isSelected: PropTypes.bool,
110+
}).isRequired,
111+
};
112+
113+
const EnterpriseCustomerUserDataTable = ({
114+
enterpriseId,
115+
learnerEmails,
116+
onHandleAddMembersBulkAction,
117+
onHandleRemoveMembersBulkAction,
118+
enterpriseGroupLearners,
119+
}) => {
120+
const {
121+
isLoading,
122+
enterpriseMembersTableData,
123+
fetchEnterpriseMembersTableData,
124+
} = useEnterpriseMembersTableData({ enterpriseId });
125+
126+
const selectColumn = {
127+
id: 'selection',
128+
Header: DataTable.ControlledSelectHeader,
129+
Cell: CustomSelectColumnCell,
130+
disableSortBy: true,
131+
};
132+
133+
const contextValue = useMemo(() => ({
134+
enterpriseGroupLearners,
135+
}), [enterpriseGroupLearners]);
136+
137+
return (
138+
<EnterpriseCustomerUserDataTableContext.Provider value={contextValue}>
139+
<DataTable
140+
bulkActions={[
141+
<AddMembersBulkAction
142+
onHandleAddMembersBulkAction={onHandleAddMembersBulkAction}
143+
enterpriseId={enterpriseId}
144+
enterpriseGroupLearners={enterpriseGroupLearners}
145+
/>,
146+
<RemoveMembersBulkAction
147+
enterpriseId={enterpriseId}
148+
learnerEmails={learnerEmails}
149+
onHandleRemoveMembersBulkAction={onHandleRemoveMembersBulkAction}
150+
/>,
151+
]}
152+
columns={[
153+
{
154+
Header: 'Member details',
155+
accessor: 'name',
156+
Cell: MemberDetailsCell,
157+
},
158+
{
159+
Header: 'Joined organization',
160+
accessor: 'joinedOrg',
161+
Cell: MemberJoinedDateCell,
162+
disableFilters: true,
163+
},
164+
]}
165+
initialState={{
166+
pageIndex: GROUP_MEMBERS_TABLE_DEFAULT_PAGE,
167+
pageSize: GROUP_MEMBERS_TABLE_PAGE_SIZE,
168+
sortBy: [
169+
{ id: 'name', desc: true },
170+
],
171+
filters: [],
172+
}}
173+
data={enterpriseMembersTableData.results}
174+
defaultColumnValues={{ Filter: TextFilter }}
175+
FilterStatusComponent={FilterStatus}
176+
fetchData={fetchEnterpriseMembersTableData}
177+
isFilterable
178+
isLoading={isLoading}
179+
isPaginated
180+
isSelectable
181+
itemCount={enterpriseMembersTableData.itemCount}
182+
manualFilters
183+
manualPagination
184+
isSortable
185+
manualSortBy
186+
initialTableOptions={{
187+
getRowId: row => row.enterpriseCustomerUser.userId.toString(),
188+
}}
189+
pageCount={enterpriseMembersTableData.pageCount}
190+
manualSelectColumn={selectColumn}
191+
SelectionStatusComponent={DataTable.ControlledSelectionStatus}
192+
/>
193+
</EnterpriseCustomerUserDataTableContext.Provider>
194+
);
195+
};
196+
197+
EnterpriseCustomerUserDataTable.defaultProps = {
198+
enterpriseGroupLearners: [],
199+
};
200+
201+
EnterpriseCustomerUserDataTable.propTypes = {
202+
enterpriseId: PropTypes.string.isRequired,
203+
learnerEmails: PropTypes.arrayOf(PropTypes.string).isRequired,
204+
onHandleRemoveMembersBulkAction: PropTypes.func.isRequired,
205+
onHandleAddMembersBulkAction: PropTypes.func.isRequired,
206+
enterpriseGroupLearners: PropTypes.arrayOf(PropTypes.shape({})),
207+
};
208+
209+
BaseSelectWithContext.propTypes = {
210+
row: PropTypes.shape({
211+
getToggleRowSelectedProps: PropTypes.func.isRequired,
212+
id: PropTypes.string,
213+
}).isRequired,
214+
enterpriseGroupLearners: PropTypes.arrayOf(PropTypes.shape({})),
215+
};
216+
217+
const mapStateToProps = state => ({
218+
enterpriseId: state.portalConfiguration.enterpriseId,
219+
});
220+
221+
export default connect(mapStateToProps)(EnterpriseCustomerUserDataTable);

0 commit comments

Comments
 (0)