Skip to content

Commit ce8b109

Browse files
authored
feat(AnalyticalTable): add selectedRowIds object to onRowSelect event & improve performance when selecting rows (#4534)
Fixes #4517
1 parent 728e013 commit ce8b109

File tree

4 files changed

+108
-42
lines changed

4 files changed

+108
-42
lines changed

packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx

Lines changed: 82 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ interface PropTypes {
3232
row?: Record<string, unknown>;
3333
isSelected?: boolean;
3434
selectedFlatRows: Record<string, unknown>[];
35+
selectedRowIds: Record<string | number, boolean>;
3536
}>
3637
) => void;
3738
}
@@ -290,14 +291,15 @@ describe('AnalyticalTable', () => {
290291
filterable
291292
columns={columns}
292293
onRowSelect={(e) => {
293-
const { allRowsSelected, isSelected, row, selectedFlatRows } = e.detail;
294+
const { allRowsSelected, isSelected, row, selectedFlatRows, selectedRowIds } = e.detail;
294295
setRelevantPayload({
295296
allRowsSelected,
296297
isSelected,
297298
row: row.id,
298299
selectedFlatRows: selectedFlatRows.map((item) => ({
299300
id: item?.id
300-
}))
301+
})),
302+
selectedRowIds
301303
});
302304
props.onRowSelect(e);
303305
}}
@@ -307,6 +309,7 @@ describe('AnalyticalTable', () => {
307309
/>
308310
<div data-testid="payloadHelper">
309311
{JSON.stringify(relevantPayload?.selectedFlatRows?.filter(Boolean).length)}
312+
{JSON.stringify(relevantPayload?.selectedRowIds)}
310313
</div>
311314
</>
312315
);
@@ -329,12 +332,12 @@ describe('AnalyticalTable', () => {
329332
cy.get('@onRowSelectSpy').should('have.been.calledWithMatch', {
330333
detail: { isSelected: true }
331334
});
332-
cy.findByTestId('payloadHelper').should('have.text', '1');
335+
cy.findByTestId('payloadHelper').should('have.text', '1{"0.2":true}');
333336
cy.findByText('Judith Mathews').click();
334337
cy.get('@onRowSelectSpy').should('have.been.calledWithMatch', {
335338
detail: { isSelected: true }
336339
});
337-
cy.findByTestId('payloadHelper').should('have.text', '2');
340+
cy.findByTestId('payloadHelper').should('have.text', '2{"0.2":true,"0.2.0":true}');
338341

339342
// global filter + select
340343
cy.findByTestId('input').typeIntoUi5Input('Katy Bradshaw');
@@ -345,7 +348,7 @@ describe('AnalyticalTable', () => {
345348
detail: { isSelected: true }
346349
});
347350
cy.get('@onRowSelectSpy').should('have.been.calledThrice');
348-
cy.findByTestId('payloadHelper').should('have.text', '3');
351+
cy.findByTestId('payloadHelper').should('have.text', '3{"1":true,"0.2":true,"0.2.0":true}');
349352

350353
cy.findByTestId('input').typeIntoUi5Input('{selectall}{backspace}');
351354

@@ -360,14 +363,15 @@ describe('AnalyticalTable', () => {
360363
detail: { isSelected: true }
361364
});
362365
cy.get('@onRowSelectSpy').should('have.callCount', 4);
363-
cy.findByTestId('payloadHelper').should('have.text', '4');
366+
cy.findByTestId('payloadHelper').should('have.text', '4{"0":true,"1":true,"0.2":true,"0.2.0":true}');
364367
});
365368

366369
it('programmatic and user selection', () => {
367370
const data = generateMoreData(20);
368371
const TestComp = ({ onRowSelect }: PropTypes) => {
369372
const [selectedRowIds, setSelectedRowIds] = useState({});
370373
const [selectedFlatRows, setSelectedFlatRows] = useState([]);
374+
const [selectedRowIdsCb, setSelectedRowIdsCb] = useState({});
371375
return (
372376
<>
373377
<Button onClick={() => setSelectedRowIds({ 2: true, 3: false })}>Set selected rows</Button>
@@ -376,12 +380,18 @@ describe('AnalyticalTable', () => {
376380
columns={columns}
377381
onRowSelect={(e) => {
378382
setSelectedFlatRows(e.detail.selectedFlatRows.map((item) => item.id));
383+
setSelectedRowIdsCb(e.detail.selectedRowIds);
379384
onRowSelect(e);
380385
}}
381386
selectionMode={AnalyticalTableSelectionMode.MultiSelect}
382387
selectedRowIds={selectedRowIds}
383388
/>
384-
"event.detail.selectedFlatRows:"<div data-testid="payload">{JSON.stringify(selectedFlatRows)}</div>
389+
<p>
390+
"event.detail.selectedFlatRows:"<span data-testid="payload">{JSON.stringify(selectedFlatRows)}</span>
391+
</p>
392+
<p>
393+
"e.detail.selectedRowIds:"<span data-testid="payloadRowsById">{JSON.stringify(selectedRowIdsCb)}</span>
394+
</p>
385395
</>
386396
);
387397
};
@@ -393,12 +403,14 @@ describe('AnalyticalTable', () => {
393403
cy.findByText('Name-5').click();
394404
cy.findByText('Name-5').click();
395405
cy.findByTestId('payload').should('have.text', '["0","1"]');
406+
cy.findByTestId('payloadRowsById').should('have.text', '{"0":true,"1":true}');
396407
cy.get('@onRowSelectSpy').should('have.callCount', 4);
397408

398409
cy.findByText('Set selected rows').click();
399410
cy.get('@onRowSelectSpy').should('have.callCount', 4);
400411
cy.findByText('Name-1').click();
401412
cy.findByTestId('payload').should('have.text', '["1","2"]');
413+
cy.findByTestId('payloadRowsById').should('have.text', '{"1":true,"2":true,"3":false}');
402414
});
403415

404416
it('row & header height', () => {
@@ -451,16 +463,6 @@ describe('AnalyticalTable', () => {
451463
});
452464

453465
it('GroupBy selection', () => {
454-
interface PropTypes {
455-
onRowSelect: (
456-
e?: CustomEvent<{
457-
allRowsSelected: boolean;
458-
row?: Record<string, unknown>;
459-
isSelected?: boolean;
460-
selectedFlatRows: Record<string, unknown>[];
461-
}>
462-
) => void;
463-
}
464466
const GroupBySelectTable = (props: PropTypes) => {
465467
const { onRowSelect } = props;
466468
const [relevantPayload, setRelevantPayload] = useState<Record<string, any>>({});
@@ -483,14 +485,15 @@ describe('AnalyticalTable', () => {
483485
columns={columns}
484486
tableInstance={tableInstance}
485487
onRowSelect={(e) => {
486-
const { allRowsSelected, isSelected, row, selectedFlatRows } = e.detail;
488+
const { allRowsSelected, isSelected, row, selectedFlatRows, selectedRowIds } = e.detail;
487489
setRelevantPayload({
488490
allRowsSelected,
489491
isSelected,
490492
row: row.id,
491493
selectedFlatRows: selectedFlatRows.map((item) => ({
492494
id: item?.id
493-
}))
495+
})),
496+
selectedRowIds
494497
});
495498
onRowSelect(e);
496499
}}
@@ -500,6 +503,7 @@ describe('AnalyticalTable', () => {
500503
<div data-testid="selectedFlatRowsLength">
501504
{JSON.stringify(relevantPayload?.selectedFlatRows?.filter(Boolean).length)}
502505
</div>
506+
<div data-testid="selectedRowIds">{JSON.stringify(relevantPayload?.selectedRowIds)}</div>
503507
<div data-testid="isSelected">{`${relevantPayload.isSelected}`}</div>
504508
</>
505509
);
@@ -510,6 +514,7 @@ describe('AnalyticalTable', () => {
510514
cy.findByText('QWE').click();
511515
cy.get('@onRowSelectSpy').should('have.callCount', 1);
512516
cy.findByTestId('selectedFlatRowsLength').should('have.text', '1');
517+
cy.findByTestId('selectedRowIds').should('have.text', '{"2":true}');
513518
cy.findByTestId('isSelected').should('have.text', 'true');
514519

515520
cy.findByText('Friend Name').click();
@@ -519,11 +524,13 @@ describe('AnalyticalTable', () => {
519524
cy.findByText('25').click();
520525
cy.get('@onRowSelectSpy').should('have.callCount', 2);
521526
cy.findByTestId('selectedFlatRowsLength').should('have.text', '2');
527+
cy.findByTestId('selectedRowIds').should('have.text', '{"2":true,"4":true}');
522528
cy.findByTestId('isSelected').should('have.text', 'true');
523529

524530
cy.findByText('25').click();
525531
cy.get('@onRowSelectSpy').should('have.callCount', 3);
526532
cy.findByTestId('selectedFlatRowsLength').should('have.text', '1');
533+
cy.findByTestId('selectedRowIds').should('have.text', '{"2":true}');
527534
cy.findByTestId('isSelected').should('have.text', 'false');
528535
});
529536

@@ -1791,6 +1798,62 @@ describe('AnalyticalTable', () => {
17911798
cy.get('[data-column-index="2"][data-row-index="3"]').children().should('have.text', 'Y');
17921799
});
17931800

1801+
it('select-all', () => {
1802+
const select = cy.spy().as('selectSpy');
1803+
const TestComp = () => {
1804+
const [stringifiedPl, setStringifiedPl] = useState('');
1805+
const handleSelect = (e) => {
1806+
const { allRowsSelected, selectedFlatRows, selectedRowIds } = e.detail;
1807+
setStringifiedPl(
1808+
JSON.stringify({
1809+
selectedRowIds,
1810+
selectedFlatRows: selectedFlatRows.map((item) => ({
1811+
id: item?.id
1812+
})),
1813+
allRowsSelected
1814+
})
1815+
);
1816+
select(e);
1817+
};
1818+
return (
1819+
<>
1820+
<AnalyticalTable
1821+
columns={columns}
1822+
data={data}
1823+
selectionMode={AnalyticalTableSelectionMode.MultiSelect}
1824+
onRowSelect={handleSelect}
1825+
/>
1826+
<span data-testid="payload">{stringifiedPl}</span>
1827+
</>
1828+
);
1829+
};
1830+
cy.mount(<TestComp />);
1831+
cy.get('[data-visible-column-index="0"][data-visible-row-index="0"]').click();
1832+
cy.get('@selectSpy').should('have.been.calledOnce');
1833+
cy.findByTestId('payload').should(
1834+
'have.text',
1835+
'{"selectedRowIds":{"0":true,"1":true,"2":true,"3":true},"selectedFlatRows":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}],"allRowsSelected":true}'
1836+
);
1837+
cy.findByText('X').click();
1838+
cy.get('@selectSpy').should('have.been.calledTwice');
1839+
cy.findByTestId('payload').should(
1840+
'have.text',
1841+
'{"selectedRowIds":{"0":true,"1":true,"3":true},"selectedFlatRows":[{"id":"0"},{"id":"1"},{"id":"3"}],"allRowsSelected":false}'
1842+
);
1843+
cy.get('[data-visible-column-index="0"][data-visible-row-index="0"]').click();
1844+
cy.get('@selectSpy').should('have.been.calledThrice');
1845+
cy.findByTestId('payload').should(
1846+
'have.text',
1847+
'{"selectedRowIds":{"0":true,"1":true,"2":true,"3":true},"selectedFlatRows":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}],"allRowsSelected":true}'
1848+
);
1849+
cy.get('[data-visible-column-index="0"][data-visible-row-index="0"]').click();
1850+
cy.get('@selectSpy').should('have.callCount', 4);
1851+
cy.findByTestId('payload').should(
1852+
'have.text',
1853+
'{"selectedRowIds":{},"selectedFlatRows":[],"allRowsSelected":false}'
1854+
);
1855+
});
1856+
17941857
cypressPassThroughTestsFactory(AnalyticalTable, { data, columns });
17951858
});
17961859

packages/main/src/components/AnalyticalTable/hooks/useRowSelectionColumn.tsx

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -66,21 +66,21 @@ const Cell = ({ row, webComponentsReactProperties: { selectionMode } }) => {
6666
);
6767
};
6868

69-
/*
70-
* TABLE HOOKS
71-
*/
72-
73-
const headerProps = (
74-
props,
75-
{
76-
instance: {
77-
flatRows,
78-
webComponentsReactProperties: { onRowSelect, selectionMode },
79-
toggleAllRowsSelected,
80-
isAllRowsSelected
81-
}
82-
}
83-
) => {
69+
function getNextSelectedRowIds(rowsById) {
70+
return Object.keys(rowsById).reduce((acc, cur) => {
71+
acc[cur] = true;
72+
return acc;
73+
}, {});
74+
}
75+
76+
const headerProps = (props, { instance }) => {
77+
const {
78+
flatRows,
79+
webComponentsReactProperties: { onRowSelect, selectionMode },
80+
toggleAllRowsSelected,
81+
isAllRowsSelected,
82+
rowsById
83+
} = instance;
8484
const style = { ...props.style, cursor: 'pointer', display: 'flex', justifyContent: 'center' };
8585
if (
8686
props.key === 'header___ui5wcr__internal_selection_column' &&
@@ -93,7 +93,8 @@ const headerProps = (
9393
// cannot use instance.selectedFlatRows here as it only returns all rows on the first level
9494
enrichEventWithDetails(e, {
9595
allRowsSelected: !isAllRowsSelected,
96-
selectedFlatRows: !isAllRowsSelected ? flatRows : []
96+
selectedFlatRows: !isAllRowsSelected ? flatRows : [],
97+
selectedRowIds: !isAllRowsSelected ? getNextSelectedRowIds(rowsById) : {}
9798
})
9899
);
99100
}

packages/main/src/components/AnalyticalTable/hooks/useSelectionChangeCallback.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,20 @@ export const useSelectionChangeCallback = (hooks) => {
1717
row: row,
1818
isSelected: row.isSelected,
1919
selectedFlatRows: row.isSelected ? [row] : [],
20-
allRowsSelected: false
20+
allRowsSelected: false,
21+
selectedRowIds
2122
};
2223

2324
if (webComponentsReactProperties.selectionMode === AnalyticalTableSelectionMode.MultiSelect) {
24-
const selectedRowIdsArray = Object.entries(selectedRowIds).reduce((acc, [key, val]) => {
25-
if (val) {
26-
return [...acc, key];
25+
// when selecting a row on a filtered table, `preFilteredRowsById` has to be used, otherwise filtered out rows are undefined
26+
const tempRowsById = filters?.length > 0 ? preFilteredRowsById : rowsById;
27+
const selectedRowIdsArrayMapped = Object.keys(selectedRowIds).reduce((acc, key) => {
28+
if (selectedRowIds[key]) {
29+
acc.push(tempRowsById[key]);
2730
}
2831
return acc;
2932
}, []);
30-
// when selecting a row on a filtered table, `preFilteredRowsById` has to be used, otherwise filtered out rows are undefined
31-
const tempRowsById = filters?.length > 0 ? preFilteredRowsById : rowsById;
32-
const selectedRowIdsArrayMapped = selectedRowIdsArray.map((item) => tempRowsById[item]);
33+
3334
payload.selectedFlatRows = selectedRowIdsArrayMapped;
3435
if (selectedRowIdsArrayMapped.length === Object.keys(tempRowsById).length) {
3536
payload.allRowsSelected = true;

packages/main/src/components/AnalyticalTable/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,7 @@ export interface AnalyticalTablePropTypes extends Omit<CommonProps, 'title'> {
532532
row?: Record<string, unknown>;
533533
isSelected?: boolean;
534534
selectedFlatRows: Record<string, unknown>[];
535+
selectedRowIds: Record<string | number, boolean>;
535536
}>
536537
) => void;
537538
/**

0 commit comments

Comments
 (0)