Skip to content

Commit 62d0a56

Browse files
authored
Fix : ensure table selections respect applied filters (#116)
Signed-off-by: Ayoub LABIDI <[email protected]>
1 parent 5013324 commit 62d0a56

File tree

5 files changed

+55
-29
lines changed

5 files changed

+55
-29
lines changed

src/pages/common/table-selection.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ export interface TableSelectionProps {
2020
onSelectionChanged: (selectedItems: string[]) => void;
2121
}
2222

23+
const rowSelection = {
24+
...defaultRowSelection,
25+
headerCheckbox: false,
26+
};
27+
2328
const TableSelection: FunctionComponent<TableSelectionProps> = (props) => {
2429
const [selectedRowsLength, setSelectedRowsLength] = useState(0);
2530
const gridRef = useRef<AgGridReact>(null);
@@ -82,7 +87,7 @@ const TableSelection: FunctionComponent<TableSelectionProps> = (props) => {
8287
rowData={rowData}
8388
columnDefs={columnDefs}
8489
defaultColDef={defaultColDef}
85-
rowSelection={defaultRowSelection}
90+
rowSelection={rowSelection}
8691
getRowId={getRowId}
8792
onSelectionChanged={handleEquipmentSelectionChanged}
8893
onGridReady={onGridReady}

src/pages/groups/groups-table.tsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ import { useIntl } from 'react-intl';
1010
import { GroupAdd } from '@mui/icons-material';
1111
import { GridButton, GridButtonDelete, GridTable, GridTableRef } from '../../components/Grid';
1212
import { GroupInfos, UserAdminSrv, UserInfos } from '../../services';
13-
import { ColDef, GetRowIdParams, RowClickedEvent, SelectionChangedEvent, TextFilterParams } from 'ag-grid-community';
13+
import { ColDef, GetRowIdParams, RowClickedEvent, TextFilterParams } from 'ag-grid-community';
1414
import { useSnackMessage } from '@gridsuite/commons-ui';
1515
import DeleteConfirmationDialog from '../common/delete-confirmation-dialog';
1616
import { defaultColDef, defaultRowSelection } from '../common/table-config';
1717
import MultiChipCellRenderer from '../common/multi-chip-cell-renderer';
18+
import { useTableSelection } from '../../utils/hooks';
1819

1920
export interface GroupsTableProps {
2021
gridRef: RefObject<GridTableRef<GroupInfos>>;
@@ -26,18 +27,13 @@ const GroupsTable: FunctionComponent<GroupsTableProps> = (props) => {
2627
const intl = useIntl();
2728
const { snackError } = useSnackMessage();
2829

29-
const [rowsSelection, setRowsSelection] = useState<GroupInfos[]>([]);
30+
const { rowsSelection, onSelectionChanged, onFilterChanged } = useTableSelection<GroupInfos>();
3031
const [showDeletionDialog, setShowDeletionDialog] = useState(false);
3132

3233
function getRowId(params: GetRowIdParams<GroupInfos>): string {
3334
return params.data.name;
3435
}
3536

36-
const onSelectionChanged = useCallback(
37-
(event: SelectionChangedEvent<GroupInfos, {}>) => setRowsSelection(event.api.getSelectedRows() ?? []),
38-
[setRowsSelection]
39-
);
40-
4137
const onAddButton = useCallback(() => props.setOpenAddGroupDialog(true), [props]);
4238

4339
const deleteGroups = useCallback((): Promise<void> | undefined => {
@@ -114,6 +110,7 @@ const GroupsTable: FunctionComponent<GroupsTableProps> = (props) => {
114110
rowSelection={defaultRowSelection}
115111
onRowClicked={props.onRowClicked}
116112
onSelectionChanged={onSelectionChanged}
113+
onFilterChanged={onFilterChanged}
117114
>
118115
<GridButton
119116
labelId="groups.table.toolbar.add.label"

src/pages/profiles/profiles-table.tsx

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,12 @@ import { useIntl } from 'react-intl';
1010
import { ManageAccounts } from '@mui/icons-material';
1111
import { GridButton, GridButtonDelete, GridTable, GridTableRef } from '../../components/Grid';
1212
import { UserAdminSrv, UserProfile } from '../../services';
13-
import {
14-
ColDef,
15-
GetRowIdParams,
16-
ITooltipParams,
17-
RowClickedEvent,
18-
SelectionChangedEvent,
19-
TextFilterParams,
20-
} from 'ag-grid-community';
13+
import { ColDef, GetRowIdParams, ITooltipParams, RowClickedEvent, TextFilterParams } from 'ag-grid-community';
2114
import { useSnackMessage } from '@gridsuite/commons-ui';
2215
import DeleteConfirmationDialog from '../common/delete-confirmation-dialog';
2316
import { defaultColDef, defaultRowSelection } from '../common/table-config';
2417
import ValidityCellRenderer from './validity-cell-renderer';
18+
import { useTableSelection } from '../../utils/hooks';
2519

2620
export interface ProfilesTableProps {
2721
gridRef: RefObject<GridTableRef<UserProfile>>;
@@ -33,18 +27,13 @@ const ProfilesTable: FunctionComponent<ProfilesTableProps> = (props) => {
3327
const intl = useIntl();
3428
const { snackError } = useSnackMessage();
3529

36-
const [rowsSelection, setRowsSelection] = useState<UserProfile[]>([]);
30+
const { rowsSelection, onSelectionChanged, onFilterChanged } = useTableSelection<UserProfile>();
3731
const [showDeletionDialog, setShowDeletionDialog] = useState(false);
3832

3933
function getRowId(params: GetRowIdParams<UserProfile>): string {
4034
return params.data.id ?? '';
4135
}
4236

43-
const onSelectionChanged = useCallback(
44-
(event: SelectionChangedEvent<UserProfile, {}>) => setRowsSelection(event.api.getSelectedRows() ?? []),
45-
[setRowsSelection]
46-
);
47-
4837
const onAddButton = useCallback(() => props.setOpenAddProfileDialog(true), [props]);
4938

5039
const deleteProfiles = useCallback(() => {
@@ -134,6 +123,7 @@ const ProfilesTable: FunctionComponent<ProfilesTableProps> = (props) => {
134123
rowSelection={defaultRowSelection}
135124
onRowClicked={props.onRowClicked}
136125
onSelectionChanged={onSelectionChanged}
126+
onFilterChanged={onFilterChanged}
137127
>
138128
<GridButton
139129
labelId="profiles.table.toolbar.add.label"

src/pages/users/users-table.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ import {
1515
GetRowIdParams,
1616
ICheckboxCellRendererParams,
1717
RowClickedEvent,
18-
SelectionChangedEvent,
1918
TextFilterParams,
2019
} from 'ag-grid-community';
2120
import { useSnackMessage } from '@gridsuite/commons-ui';
2221
import DeleteConfirmationDialog from '../common/delete-confirmation-dialog';
2322
import { defaultColDef, defaultRowSelection } from '../common/table-config';
2423
import MultiChipCellRenderer from '../common/multi-chip-cell-renderer';
24+
import { useTableSelection } from '../../utils/hooks';
2525

2626
export interface UsersTableProps {
2727
gridRef: RefObject<GridTableRef<UserInfos>>;
@@ -33,18 +33,13 @@ const UsersTable: FunctionComponent<UsersTableProps> = (props) => {
3333
const intl = useIntl();
3434
const { snackError } = useSnackMessage();
3535

36-
const [rowsSelection, setRowsSelection] = useState<UserInfos[]>([]);
36+
const { rowsSelection, onSelectionChanged, onFilterChanged } = useTableSelection<UserInfos>();
3737
const [showDeletionDialog, setShowDeletionDialog] = useState(false);
3838

3939
function getRowId(params: GetRowIdParams<UserInfos>): string {
4040
return params.data.sub ?? '';
4141
}
4242

43-
const onSelectionChanged = useCallback(
44-
(event: SelectionChangedEvent<UserInfos, {}>) => setRowsSelection(event.api.getSelectedRows() ?? []),
45-
[setRowsSelection]
46-
);
47-
4843
const onAddButton = useCallback(() => props.setOpenAddUserDialog(true), [props]);
4944

5045
const deleteUsers = useCallback(() => {
@@ -147,6 +142,7 @@ const UsersTable: FunctionComponent<UsersTableProps> = (props) => {
147142
rowSelection={defaultRowSelection}
148143
onRowClicked={props.onRowClicked}
149144
onSelectionChanged={onSelectionChanged}
145+
onFilterChanged={onFilterChanged}
150146
tooltipShowDelay={1000}
151147
>
152148
<GridButton

src/utils/hooks.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
66
*/
77

8+
import { FilterChangedEvent, GridApi, IRowNode, SelectionChangedEvent } from 'ag-grid-community';
9+
import { useCallback, useState } from 'react';
10+
811
export function useDebugRender(label: string) {
912
// uncomment when you want the output in the console
1013
/*if (import.meta.env.DEV) {
@@ -13,3 +16,38 @@ export function useDebugRender(label: string) {
1316
console.timeStamp?.(label);
1417
}*/
1518
}
19+
20+
/**
21+
* Custom hook to handle table row selection with proper filtering support
22+
* @returns Selection state and handlers for AG Grid's selection and filter changes
23+
*/
24+
export function useTableSelection<T>() {
25+
const [rowsSelection, setRowsSelection] = useState<T[]>([]);
26+
27+
// update visible selections based on current filter state
28+
const updateVisibleSelection = useCallback((api: GridApi) => {
29+
const visibleSelectedRows: T[] = [];
30+
api.forEachNodeAfterFilterAndSort((node: IRowNode) => {
31+
if (node.isSelected() && node.data) {
32+
visibleSelectedRows.push(node.data);
33+
}
34+
});
35+
setRowsSelection(visibleSelectedRows);
36+
}, []);
37+
38+
const onSelectionChanged = useCallback(
39+
(event: SelectionChangedEvent) => {
40+
updateVisibleSelection(event.api);
41+
},
42+
[updateVisibleSelection]
43+
);
44+
45+
const onFilterChanged = useCallback(
46+
(event: FilterChangedEvent) => {
47+
updateVisibleSelection(event.api);
48+
},
49+
[updateVisibleSelection]
50+
);
51+
52+
return { rowsSelection, onSelectionChanged, onFilterChanged };
53+
}

0 commit comments

Comments
 (0)