Skip to content

Commit db7bff9

Browse files
authored
SW-4503 Implement v2 enhanced top bar with select all, select none, enhanced selection text (#450)
1 parent 9bcebaf commit db7bff9

File tree

4 files changed

+158
-13
lines changed

4 files changed

+158
-13
lines changed

src/components/table/EnhancedTableToolbar.tsx

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import React from 'react';
2-
import { Theme, Toolbar, Typography } from '@mui/material';
1+
import React, { useMemo } from 'react';
2+
import { Link, Theme, Toolbar, Typography } from '@mui/material';
3+
import { makeStyles } from '@mui/styles';
34
import { isTopBarButton, TopBarButton } from '.';
45
import Button from '../Button/Button';
5-
import { makeStyles } from '@mui/styles';
66
import Tooltip from '../Tooltip/Tooltip';
7+
import { EnhancedTopBarSelectionConfig } from './EnhancedTableToolbarV2';
78

89
const styles = makeStyles((theme: Theme) => ({
910
toolbar: {
@@ -15,22 +16,56 @@ const styles = makeStyles((theme: Theme) => ({
1516
buttonSpacing: {
1617
marginLeft: theme.spacing(1),
1718
},
19+
selectLink: {
20+
color: theme.palette.TwClrTxtBrand,
21+
cursor: 'pointer',
22+
},
1823
}));
1924

2025
interface EnhancedTableToolbarProps {
26+
isAllSelected?: boolean;
2127
numSelected: number;
22-
renderNumSelectedText?: (numSelected: number) => string | JSX.Element;
28+
renderNumSelectedText?: (numSelected: number) => string;
2329
topBarButtons?: (TopBarButton | JSX.Element)[];
30+
topBarSelectionConfig?: EnhancedTopBarSelectionConfig;
2431
}
2532

2633
export default function EnhancedTableToolbar(props: EnhancedTableToolbarProps): JSX.Element | null {
27-
const { numSelected, renderNumSelectedText, topBarButtons } = props;
34+
const { isAllSelected, numSelected, renderNumSelectedText, topBarButtons, topBarSelectionConfig } = props;
2835
const classes = styles();
2936

37+
const topBarSelection = useMemo(() => {
38+
if (!renderNumSelectedText) {
39+
return null;
40+
}
41+
42+
if (!topBarSelectionConfig) {
43+
return renderNumSelectedText(numSelected);
44+
}
45+
46+
const { handleSelectAll, handleSelectNone, renderSelectAllText, renderSelectNoneText, renderEnhancedNumSelectedText } = topBarSelectionConfig;
47+
48+
return (
49+
<>
50+
{renderEnhancedNumSelectedText ? renderEnhancedNumSelectedText() : renderNumSelectedText(numSelected)}{' '}
51+
{!isAllSelected && handleSelectAll && renderSelectAllText && (
52+
<Link className={classes.selectLink} onClick={handleSelectAll}>
53+
{renderSelectAllText()}
54+
</Link>
55+
)}
56+
{isAllSelected && handleSelectNone && renderSelectNoneText && (
57+
<Link className={classes.selectLink} onClick={handleSelectNone}>
58+
{renderSelectNoneText()}
59+
</Link>
60+
)}
61+
</>
62+
);
63+
}, [renderNumSelectedText, topBarSelectionConfig, numSelected]);
64+
3065
return renderNumSelectedText && numSelected > 0 ? (
3166
<Toolbar className={classes.toolbar}>
3267
<Typography color='inherit' variant='subtitle1' component='div' className={classes.flexText}>
33-
{renderNumSelectedText(numSelected)}
68+
{topBarSelection}
3469
</Typography>
3570
{topBarButtons?.map((tbButton: TopBarButton | JSX.Element, index) =>
3671
isTopBarButton(tbButton) ? (
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import React, { useMemo } from 'react';
2+
import EnhancedTableToolbar from './EnhancedTableToolbar';
3+
import { TopBarButton, EnhancedTopBarSelectionProps } from './index';
4+
5+
export interface EnhancedTopBarSelectionConfig {
6+
handleSelectAll?: () => void;
7+
handleSelectNone?: () => void;
8+
renderEnhancedNumSelectedText?: () => string;
9+
renderSelectAllText?: () => string;
10+
renderSelectNoneText?: () => string;
11+
}
12+
13+
type DecoratedEnhancedTopBarSelectionConfig = Omit<
14+
EnhancedTopBarSelectionConfig,
15+
'renderEnhancedNumSelectedText' | 'renderSelectAllText' | 'renderSelectNoneText'
16+
> &
17+
EnhancedTopBarSelectionProps;
18+
19+
interface EnhancedTableToolbarV2Props {
20+
selectedRowsCount: number;
21+
pagesCount: number;
22+
renderNumSelectedText?: (selectedCount: number) => string;
23+
rowsCount: number;
24+
topBarButtons?: (TopBarButton | JSX.Element)[];
25+
topBarSelectionConfig?: DecoratedEnhancedTopBarSelectionConfig;
26+
}
27+
28+
const EnhancedTableToolbarV2 = ({
29+
selectedRowsCount,
30+
pagesCount,
31+
renderNumSelectedText,
32+
rowsCount,
33+
topBarButtons,
34+
topBarSelectionConfig,
35+
}: EnhancedTableToolbarV2Props) => {
36+
const _topBarSelectionConfig = useMemo((): EnhancedTopBarSelectionConfig | undefined => {
37+
if (!topBarSelectionConfig) {
38+
return topBarSelectionConfig;
39+
}
40+
41+
const { renderEnhancedNumSelectedText, renderSelectAllText } = topBarSelectionConfig;
42+
43+
return {
44+
...topBarSelectionConfig,
45+
renderEnhancedNumSelectedText: () => (renderEnhancedNumSelectedText && renderEnhancedNumSelectedText(selectedRowsCount || 0, pagesCount)) || '',
46+
renderSelectAllText: () => (renderSelectAllText && renderSelectAllText(rowsCount)) || '',
47+
};
48+
}, [topBarSelectionConfig, selectedRowsCount, rowsCount, pagesCount]);
49+
50+
if (topBarSelectionConfig) {
51+
return (
52+
<EnhancedTableToolbar
53+
numSelected={selectedRowsCount}
54+
topBarButtons={topBarButtons}
55+
topBarSelectionConfig={_topBarSelectionConfig}
56+
isAllSelected={rowsCount === selectedRowsCount}
57+
renderNumSelectedText={renderNumSelectedText}
58+
/>
59+
);
60+
}
61+
62+
return <EnhancedTableToolbar numSelected={selectedRowsCount} renderNumSelectedText={renderNumSelectedText} topBarButtons={topBarButtons} />;
63+
};
64+
65+
export default EnhancedTableToolbarV2;

src/components/table/index.tsx

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Box, Checkbox, Pagination, Table, TableBody, TableCell, TableContainer, TableRow, Theme, TooltipProps, Typography, useTheme } from '@mui/material';
2-
import React, { useCallback, useEffect, useState } from 'react';
3-
import EnhancedTableToolbar from './EnhancedTableToolbar';
2+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
3+
import EnhancedTableToolbarV2 from './EnhancedTableToolbarV2';
44
import { descendingComparator, getComparator, SortOrder, stableSort } from './sort';
55
import TableCellRenderer, { TableRowType } from './TableCellRenderer';
66
import TableHeader from './TableHeader';
@@ -45,13 +45,20 @@ export interface HeadCell {
4545
}
4646

4747
export interface LocalizationProps {
48-
renderNumSelectedText?: (numSelected: number) => string | JSX.Element;
48+
renderNumSelectedText?: (numSelected: number) => string;
4949
renderPaginationText?: (from: number, to: number, total: number) => string;
5050
booleanFalseText: string;
5151
booleanTrueText: string;
5252
editText: string;
5353
}
5454

55+
// Only the text related props need to be passed in by the implementer, and extra data is passed to the caller
56+
export type EnhancedTopBarSelectionProps = {
57+
renderEnhancedNumSelectedText?: (selectedCount: number, pagesCount: number) => string;
58+
renderSelectAllText?: (rowsCount: number) => string;
59+
renderSelectNoneText?: () => string;
60+
};
61+
5562
export interface Props<T> extends LocalizationProps {
5663
id?: string;
5764
orderBy: string;
@@ -80,6 +87,8 @@ export interface Props<T> extends LocalizationProps {
8087
reloadData?: () => void;
8188
stickyHeader?: boolean;
8289
hideHeader?: boolean;
90+
// Adds "select all rows across all pages" and "clear selection" to the table top bar
91+
enhancedTopBarSelectionConfig?: EnhancedTopBarSelectionProps;
8392
}
8493

8594
export type TopBarButton = {
@@ -127,6 +136,7 @@ export default function EnhancedTable<T extends TableRowType>({
127136
editText,
128137
renderNumSelectedText,
129138
renderPaginationText,
139+
enhancedTopBarSelectionConfig,
130140
}: Props<T>): JSX.Element {
131141
const classes = tableStyles();
132142
const theme = useTheme();
@@ -291,15 +301,29 @@ export default function EnhancedTable<T extends TableRowType>({
291301
}
292302
}
293303

304+
const pagesCount = Math.ceil(rows.length / maxItemsPerPage);
305+
294306
return (
295307
<>
296308
{showTopBar && (
297-
<EnhancedTableToolbar
298-
numSelected={selectedRows ? selectedRows.length : 0}
309+
<EnhancedTableToolbarV2
310+
selectedRowsCount={selectedRows?.length || 0}
311+
pagesCount={pagesCount}
299312
renderNumSelectedText={renderNumSelectedText}
313+
rowsCount={rows.length}
300314
topBarButtons={topBarButtons}
315+
topBarSelectionConfig={
316+
enhancedTopBarSelectionConfig
317+
? {
318+
...enhancedTopBarSelectionConfig,
319+
handleSelectAll: () => setSelectedRows && setSelectedRows(rows),
320+
handleSelectNone: () => setSelectedRows && setSelectedRows([]),
321+
}
322+
: undefined
323+
}
301324
/>
302325
)}
326+
303327
<TableContainer id={id} sx={{ overflowX: 'visible' }}>
304328
<DndContext sensors={sensors} onDragEnd={handleDragEnd}>
305329
<Table stickyHeader={stickyHeader} aria-labelledby='tableTitle' size='medium' aria-label='enhanced table' className={classes.table}>
@@ -406,7 +430,7 @@ export default function EnhancedTable<T extends TableRowType>({
406430
</Typography>
407431
)}
408432
<Pagination
409-
count={Math.ceil(rows.length / maxItemsPerPage)}
433+
count={pagesCount}
410434
page={itemsToSkip / maxItemsPerPage + 1}
411435
shape='rounded'
412436
onChange={handleChangePage}

src/stories/Table.stories.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Story } from '@storybook/react';
22
import React, { useState } from 'react';
3-
import Table, { Props as TableProps } from '../components/table/index';
3+
import Table, { EnhancedTopBarSelectionProps, Props as TableProps } from '../components/table/index';
44
import CellRenderer from '..//components/table/TableCellRenderer';
55
import { RendererProps } from '../components/table/types';
66
import { Box } from '@mui/material';
@@ -70,6 +70,7 @@ export const Default = Template.bind({});
7070
export const Selectable = Template.bind({});
7171
export const Presorted = Template.bind({});
7272
export const ShowTopBar = Template.bind({});
73+
export const ShowTopBarV2 = Template.bind({});
7374

7475
Default.args = {
7576
orderBy: 'name',
@@ -134,3 +135,23 @@ ShowTopBar.args = {
134135
controlledOnSelect: true,
135136
showTopBar: true,
136137
};
138+
139+
const enhancedTopBarSelectionConfig: EnhancedTopBarSelectionProps = {
140+
renderEnhancedNumSelectedText(selectedCount: number, pageCount: number): string {
141+
return `${selectedCount} selected across ${pageCount} pages`;
142+
},
143+
renderSelectAllText(rowsCount: number): string {
144+
return `Select all ${rowsCount} rows`;
145+
},
146+
renderSelectNoneText(): string {
147+
return 'Clear selection';
148+
},
149+
};
150+
151+
ShowTopBarV2.args = {
152+
...Default.args,
153+
showCheckbox: true,
154+
controlledOnSelect: true,
155+
showTopBar: true,
156+
enhancedTopBarSelectionConfig,
157+
};

0 commit comments

Comments
 (0)