Skip to content

Commit 9b28456

Browse files
Feat: New showFooter DataTable attribute and footerContent column function
1 parent 59c6ac1 commit 9b28456

File tree

11 files changed

+6049
-5224
lines changed

11 files changed

+6049
-5224
lines changed

src/DataTable/DataTable.tsx

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,41 @@
11
import * as React from 'react';
22
import { ThemeProvider } from 'styled-components';
3-
import { tableReducer } from './tableReducer';
3+
import useColumns from '../hooks/useColumns';
4+
import useDidUpdateEffect from '../hooks/useDidUpdateEffect';
5+
import { CellBase } from './Cell';
6+
import NoData from './NoDataWrapper';
7+
import NativePagination from './Pagination';
8+
import ProgressWrapper from './ProgressWrapper';
9+
import ResponsiveWrapper from './ResponsiveWrapper';
410
import Table from './Table';
5-
import Head from './TableHead';
6-
import HeadRow from './TableHeadRow';
7-
import Row from './TableRow';
11+
import Body from './TableBody';
812
import Column from './TableCol';
913
import ColumnCheckbox from './TableColCheckbox';
14+
import ColumnExpander from './TableColExpander';
15+
import Foot from './TableFoot';
16+
import FootRow from './TableFootRow';
17+
import TableFooterCell from './TableFooterCell';
18+
import Head from './TableHead';
19+
import HeadRow from './TableHeadRow';
1020
import Header from './TableHeader';
21+
import Row from './TableRow';
1122
import Subheader from './TableSubheader';
12-
import Body from './TableBody';
13-
import ResponsiveWrapper from './ResponsiveWrapper';
14-
import ProgressWrapper from './ProgressWrapper';
1523
import Wrapper from './TableWrapper';
16-
import ColumnExpander from './TableColExpander';
17-
import { CellBase } from './Cell';
18-
import NoData from './NoDataWrapper';
19-
import NativePagination from './Pagination';
20-
import useDidUpdateEffect from '../hooks/useDidUpdateEffect';
21-
import { prop, getNumberOfPages, sort, isEmpty, isRowSelected, recalculatePage } from './util';
2224
import { defaultProps } from './defaultProps';
2325
import { createStyles } from './styles';
26+
import { tableReducer } from './tableReducer';
2427
import {
2528
Action,
2629
AllRowsAction,
2730
SingleRowAction,
28-
TableRow,
2931
SortAction,
32+
SortOrder,
3033
TableProps,
34+
TableRow,
3135
TableState,
32-
SortOrder,
3336
} from './types';
34-
import useColumns from '../hooks/useColumns';
37+
import { getNumberOfPages, isEmpty, isRowSelected, prop, recalculatePage, sort } from './util';
38+
import { STOP_PROP_TAG } from './constants';
3539

3640
function DataTable<T>(props: TableProps<T>): JSX.Element {
3741
const {
@@ -81,6 +85,7 @@ function DataTable<T>(props: TableProps<T>): JSX.Element {
8185
noHeader = defaultProps.noHeader,
8286
fixedHeader = defaultProps.fixedHeader,
8387
fixedHeaderScrollHeight = defaultProps.fixedHeaderScrollHeight,
88+
showFooter = defaultProps.showFooter,
8489
pagination = defaultProps.pagination,
8590
subHeader = defaultProps.subHeader,
8691
subHeaderAlign = defaultProps.subHeaderAlign,
@@ -280,6 +285,14 @@ function DataTable<T>(props: TableProps<T>): JSX.Element {
280285
return false;
281286
};
282287

288+
const showTableFoot = () => {
289+
if (!showFooter) {
290+
return false;
291+
}
292+
293+
return sortedData.length > 0 && !progressPending;
294+
};
295+
283296
// recalculate the pagination and currentPage if the rows length changes
284297
if (pagination && !paginationServer && sortedData.length > 0 && tableRows.length === 0) {
285298
const updatedPage = getNumberOfPages(sortedData.length, rowsPerPage);
@@ -489,6 +502,30 @@ function DataTable<T>(props: TableProps<T>): JSX.Element {
489502
})}
490503
</Body>
491504
)}
505+
506+
{showTableFoot() && (
507+
<Foot className="rdt_TableFoot" role="rowgroup">
508+
<FootRow className="rdt_TableFootRow" role="row" $dense={dense}>
509+
{selectableRows && <CellBase style={{ flex: '0 0 48px' }} />}
510+
{showFooter &&
511+
tableColumns.map(column => (
512+
<TableFooterCell<T>
513+
id={column.id as string}
514+
key={column.id}
515+
dataTag={column.ignoreRowClick || column.button ? null : STOP_PROP_TAG}
516+
column={column}
517+
rows={tableRows}
518+
isDragging={false}
519+
onDragStart={handleDragStart}
520+
onDragOver={handleDragOver}
521+
onDragEnd={handleDragEnd}
522+
onDragEnter={handleDragEnter}
523+
onDragLeave={handleDragLeave}
524+
/>
525+
))}
526+
</FootRow>
527+
</Foot>
528+
)}
492529
</Table>
493530
</Wrapper>
494531
</ResponsiveWrapper>

src/DataTable/TableFoot.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import styled from 'styled-components';
2+
3+
const Foot = styled.div`
4+
display: flex;
5+
width: 100%;
6+
${({ theme }) => theme.foot.style};
7+
`;
8+
9+
export default Foot;

src/DataTable/TableFootRow.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import styled from 'styled-components';
2+
3+
const FootRow = styled.div<{
4+
$dense?: boolean;
5+
}>`
6+
display: flex;
7+
align-items: stretch;
8+
width: 100%;
9+
${({ theme }) => theme.footRow.style};
10+
${({ $dense, theme }) => $dense && theme.footRow.denseStyle};
11+
`;
12+
13+
export default FootRow;

src/DataTable/TableFooterCell.tsx

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import * as React from 'react';
2+
import styled, { css, CSSObject } from 'styled-components';
3+
import { CellExtended } from './Cell';
4+
import { getFooterProperty } from './util';
5+
import { TableColumn } from './types';
6+
7+
interface FooterCellStyleProps {
8+
$renderAsCell: boolean | undefined;
9+
$wrapCell: boolean | undefined;
10+
$allowOverflow: boolean | undefined;
11+
$cellStyle: CSSObject | undefined;
12+
$isDragging: boolean;
13+
}
14+
15+
const overflowCSS = css<FooterCellStyleProps>`
16+
div:first-child {
17+
white-space: ${({ $wrapCell }) => ($wrapCell ? 'normal' : 'nowrap')};
18+
overflow: ${({ $allowOverflow }) => ($allowOverflow ? 'visible' : 'hidden')};
19+
text-overflow: ellipsis;
20+
}
21+
`;
22+
23+
const FooterCellStyle = styled(CellExtended).attrs(props => ({
24+
style: props.style,
25+
}))<FooterCellStyleProps>`
26+
${({ $renderAsCell }) => !$renderAsCell && overflowCSS};
27+
${({ theme, $isDragging }) => $isDragging && theme.cells.draggingStyle};
28+
${({ $cellStyle }) => $cellStyle};
29+
`;
30+
31+
interface FooterCellProps<T> {
32+
id: string;
33+
dataTag: string | null;
34+
column: TableColumn<T>;
35+
rows: Array<T>;
36+
isDragging: boolean;
37+
onDragStart: (e: React.DragEvent<HTMLDivElement>) => void;
38+
onDragOver: (e: React.DragEvent<HTMLDivElement>) => void;
39+
onDragEnd: (e: React.DragEvent<HTMLDivElement>) => void;
40+
onDragEnter: (e: React.DragEvent<HTMLDivElement>) => void;
41+
onDragLeave: (e: React.DragEvent<HTMLDivElement>) => void;
42+
}
43+
44+
function FooterCell<T>({
45+
id,
46+
column,
47+
rows,
48+
dataTag,
49+
isDragging,
50+
onDragStart,
51+
onDragOver,
52+
onDragEnd,
53+
onDragEnter,
54+
onDragLeave,
55+
}: FooterCellProps<T>): JSX.Element {
56+
return (
57+
<FooterCellStyle
58+
id={id}
59+
data-column-id={column.id}
60+
role="cell"
61+
className="rdt_TableFooterCell"
62+
data-tag={dataTag}
63+
$cellStyle={column.style}
64+
$renderAsCell={!!column.cell}
65+
$allowOverflow={column.allowOverflow}
66+
button={column.button}
67+
center={column.center}
68+
compact={column.compact}
69+
grow={column.grow}
70+
hide={column.hide}
71+
maxWidth={column.maxWidth}
72+
minWidth={column.minWidth}
73+
right={column.right}
74+
width={column.width}
75+
$wrapCell={column.wrap}
76+
$isDragging={isDragging}
77+
onDragStart={onDragStart}
78+
onDragOver={onDragOver}
79+
onDragEnd={onDragEnd}
80+
onDragEnter={onDragEnter}
81+
onDragLeave={onDragLeave}
82+
>
83+
<div data-tag={dataTag}>{getFooterProperty(rows, column.footerContent)}</div>
84+
</FooterCellStyle>
85+
);
86+
}
87+
88+
export default React.memo(FooterCell) as typeof FooterCell;

src/DataTable/__tests__/DataTable.test.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2178,6 +2178,24 @@ describe('DataTable::fixedHeader', () => {
21782178
});
21792179
});
21802180

2181+
describe('DataTable::footer', () => {
2182+
test('should render correctly when a footer is enabled', () => {
2183+
const mock = dataMock();
2184+
const { container } = render(<DataTable data={mock.data} columns={mock.columns} showFooter />);
2185+
2186+
expect(container.firstChild).toMatchSnapshot();
2187+
});
2188+
2189+
test('should render correctly when a footer is enabled and footer cells contain content', () => {
2190+
const mock = dataMock({
2191+
footerContent: () => `footer`,
2192+
});
2193+
const { container } = render(<DataTable data={mock.data} columns={mock.columns} showFooter />);
2194+
2195+
expect(container.firstChild).toMatchSnapshot();
2196+
});
2197+
});
2198+
21812199
describe('DataTable::striped', () => {
21822200
test('should render correctly when striped', () => {
21832201
const mock = dataMock();

0 commit comments

Comments
 (0)