Skip to content

Commit 9d8993b

Browse files
committed
decoupled counters
1 parent 429564a commit 9d8993b

File tree

5 files changed

+29
-29
lines changed

5 files changed

+29
-29
lines changed

src/__tests__/use-collection-expadable-rows.test.tsx

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -240,29 +240,25 @@ describe('data grouping', () => {
240240
test('computes total counts correctly', () => {
241241
const expandable = renderUseCollection(items, { expandableRows: { getId, getParentId } });
242242
expect(expandable.result.collectionProps.totalItemsCount).toBe(3);
243+
expect(expandable.result.collectionProps.expandableRows!.getItemsCount).toBe(undefined);
243244

244245
const grouped = renderUseCollection(items, { expandableRows: { getId, getParentId, dataGrouping: true } });
245-
expect(grouped.result.collectionProps.totalItemsCount).toBe(6);
246+
expect(grouped.result.collectionProps.totalItemsCount).toBe(3);
247+
expect(grouped.result.collectionProps.expandableRows!.getItemsCount!(null)).toBe(6);
246248
});
247249

248250
test('computes total selected counts correctly', () => {
249251
const expandable = renderUseCollection(items, {
250252
expandableRows: { getId, getParentId },
251253
selection: { defaultSelectedItems: [{ id: 'a' }, { id: 'a.1.1' }], keepSelection: true },
252254
});
253-
expect(expandable.result.collectionProps.totalSelectedItemsCount).toBe(2);
255+
expect(expandable.result.collectionProps.expandableRows!.getSelectedItemsCount).toBe(undefined);
254256

255257
const grouped = renderUseCollection(items, {
256258
expandableRows: { getId, getParentId, dataGrouping: true },
257259
selection: { defaultSelectedItems: [{ id: 'a' }, { id: 'a.1.1' }], keepSelection: true },
258260
});
259-
expect(grouped.result.collectionProps.totalSelectedItemsCount).toBe(1);
260-
});
261-
262-
test('does not return per-item counts when dataGrouping=undefined', () => {
263-
const { result } = renderUseCollection(items, { expandableRows: { getId, getParentId }, selection: {} });
264-
expect(result.collectionProps.expandableRows!.getItemsCount).toBe(undefined);
265-
expect(result.collectionProps.expandableRows!.getSelectedItemsCount).toBe(undefined);
261+
expect(grouped.result.collectionProps.expandableRows!.getSelectedItemsCount!(null)).toBe(1);
266262
});
267263

268264
test('can call selection counts on missing items', () => {
@@ -297,7 +293,7 @@ describe('data grouping', () => {
297293
});
298294
const expandableRows = result.collectionProps.expandableRows!;
299295
const sumCounts = result.items.reduce((sum, i) => sum + expandableRows.getItemsCount!(i), 0);
300-
expect(sumCounts).toBe(result.collectionProps.totalItemsCount);
296+
expect(sumCounts).toBe(result.collectionProps.expandableRows!.getItemsCount!(null));
301297
}
302298
});
303299

src/interfaces.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,16 +117,15 @@ interface UseCollectionResultBase<T> {
117117
groupSelection?: GroupSelectionState<T>;
118118
onGroupSelectionChange(event: CustomEventLike<GroupSelectionChangeDetail<T>>): void;
119119
// The counts reflect the number of nested selectable/selected nodes (deeply), including the given one.
120-
// When expandableRows.dataGrouping={}, only leaf nodes are considered. They return 1 when called on leaf nodes.
121-
getItemsCount?: (item: T) => number;
122-
getSelectedItemsCount?: (item: T) => number;
120+
// When expandableRows.dataGrouping={}, only leaf nodes are counted.
121+
// The getters return 1 when called on leaf nodes. They can be called with null, indicating table's root level.
122+
getItemsCount?: (item: null | T) => number;
123+
getSelectedItemsCount?: (item: null | T) => number;
123124
};
124125
trackBy?: string | ((item: T) => string);
125126
ref: React.RefObject<CollectionRef>;
126-
// The counts reflect the number of selectable/selected nodes (deeply).
127-
// When expandableRows.dataGrouping={}, only leaf nodes are considered.
127+
// The count of all root items (on all pages). It is used together with the firstIndex to announce page changes.
128128
totalItemsCount: number;
129-
totalSelectedItemsCount: number;
130129
firstIndex: number;
131130
};
132131
filterProps: {

src/operations/index.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ export function processItems<T>(
2121
totalItemsCount: number;
2222
filteredItemsCount: number | undefined;
2323
selectedItems: undefined | T[];
24-
getItemsCount?: (item: T) => number;
25-
getSelectedItemsCount?: (item: T) => number;
24+
getItemsCount?: (item: null | T) => number;
25+
getSelectedItemsCount?: (item: null | T) => number;
2626
getChildren: (item: T) => T[];
2727
} {
2828
const filterPredicate = composeFilters(
@@ -35,14 +35,15 @@ export function processItems<T>(
3535
: computeFlatItems(allItems, filterPredicate, sortingComparator);
3636
const filteredItemsCount = filterPredicate ? totalItemsCount : undefined;
3737

38-
let getSelectedItemsCount: undefined | ((item: T) => number) = undefined;
38+
let getSelectedItemsCount: undefined | ((item: null | T) => number) = undefined;
3939
let selectedItems: undefined | T[] = undefined;
4040
if (selection && expandableRows?.dataGrouping && state.groupSelection) {
4141
const trackBy = selection?.trackBy ?? expandableRows?.getId;
4242
const selectionTreeProps = { getChildren: getChildren, trackBy };
4343
const selectionTree = new SelectionTree(items, selectionTreeProps, state.groupSelection);
44-
getSelectedItemsCount = selectionTree.getSelectedItemsCount;
4544
selectedItems = selectionTree.getSelectedItems();
45+
getSelectedItemsCount = (item: null | T) =>
46+
!item ? selectedItems!.length : selectionTree.getSelectedItemsCount(item);
4647
}
4748

4849
const pageProps = createPageProps(pagination, state.currentPageIndex, items);

src/operations/items-tree.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ export function computeTreeItems<T>(
3232
const idToChildren = new Map<string, T[]>();
3333
const idToCount = new Map<string, number>();
3434
let items: T[] = [];
35-
let totalItemsCount = 0;
35+
let rootItemsCount = 0;
36+
let selectableItemsCount = 0;
3637

3738
for (const item of allItems) {
3839
const parentId = treeProps.getParentId(item);
@@ -67,19 +68,23 @@ export function computeTreeItems<T>(
6768
sortLevel(items);
6869
}
6970

70-
function computeGroupCounts(item: T) {
71+
function computeSelectableCount(item: T) {
7172
const children = getChildren(item);
73+
// In grouped table, we count all leaf nodes as selectable.
7274
let itemCount = children.length === 0 ? 1 : 0;
7375
for (const child of children) {
74-
itemCount += computeGroupCounts(child);
76+
itemCount += computeSelectableCount(child);
7577
}
7678
idToCount.set(treeProps.getId(item), itemCount);
7779
return itemCount;
7880
}
7981
for (const item of items) {
80-
totalItemsCount += treeProps.dataGrouping ? computeGroupCounts(item) : 1;
82+
rootItemsCount += 1;
83+
selectableItemsCount += computeSelectableCount(item);
8184
}
82-
const getItemsCount = treeProps.dataGrouping ? (item: T) => idToCount.get(treeProps.getId(item)) ?? 0 : undefined;
85+
const getItemsCount = treeProps.dataGrouping
86+
? (item: null | T) => (!item ? selectableItemsCount : idToCount.get(treeProps.getId(item)) ?? 0)
87+
: undefined;
8388

84-
return { items, totalItemsCount, getChildren, getItemsCount };
89+
return { items, totalItemsCount: rootItemsCount, getChildren, getItemsCount };
8590
}

src/utils.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,8 @@ export function createSyncProps<T>(
144144
actualPageIndex?: number;
145145
allItems: readonly T[];
146146
totalItemsCount: number;
147-
getItemsCount?: (item: T) => number;
148-
getSelectedItemsCount?: (item: T) => number;
147+
getItemsCount?: (item: null | T) => number;
148+
getSelectedItemsCount?: (item: null | T) => number;
149149
getChildren: (item: T) => T[];
150150
}
151151
): Pick<UseCollectionResult<T>, 'collectionProps' | 'filterProps' | 'paginationProps' | 'propertyFilterProps'> {
@@ -239,7 +239,6 @@ export function createSyncProps<T>(
239239
ref: collectionRef,
240240
firstIndex: 1,
241241
totalItemsCount,
242-
totalSelectedItemsCount: selectedItems.length,
243242
...(options.pagination?.pageSize
244243
? {
245244
firstIndex: ((actualPageIndex ?? currentPageIndex) - 1) * options.pagination.pageSize + 1,

0 commit comments

Comments
 (0)