Skip to content

Commit d3462be

Browse files
author
ShtykovAlexander
authored
feat: add grouped table (#408)
* feat: add grouped table * fix: fix types * fix: revert table default story & fix typing * fix: delete flow fixme comment
1 parent b752cfd commit d3462be

File tree

3 files changed

+136
-4
lines changed

3 files changed

+136
-4
lines changed

src/components/Table/Table.stories.js

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,81 @@ const TABLE_DATA = [
283283
},
284284
];
285285

286+
const GROUPED_TABLE_DATA = [
287+
{
288+
id: '1',
289+
group: 'First',
290+
createdAt: '2018-09-03T09:59:43.000Z',
291+
updatedAt: '2018-09-03T09:59:43.000Z',
292+
firstName: 'Cecily',
293+
lastName: 'Oen',
294+
email: 'parasol@skilfish.co.uk',
295+
},
296+
{
297+
id: '2',
298+
group: 'First',
299+
createdAt: '2018-09-03T10:34:15.000Z',
300+
updatedAt: '2018-09-03T10:34:15.000Z',
301+
firstName: 'Larry',
302+
lastName: 'Marwick',
303+
email: 'parasol@skilfish.co.uk',
304+
},
305+
{
306+
id: '3',
307+
group: 'First',
308+
createdAt: '2018-09-03T09:59:46.000Z',
309+
updatedAt: '2018-09-03T09:59:46.000Z',
310+
firstName: 'Evangelina',
311+
lastName: 'Korner',
312+
email: 'maidenliness@semiflexion.co.uk',
313+
},
314+
{
315+
id: '4',
316+
group: 'Second',
317+
createdAt: '2018-09-03T09:59:43.000Z',
318+
updatedAt: '2018-09-03T09:59:43.000Z',
319+
firstName: 'Dian',
320+
lastName: 'Wegge',
321+
email: 'clinanthium@illuminize.com',
322+
},
323+
{
324+
id: '5',
325+
group: 'Second',
326+
createdAt: '2018-09-03T09:59:45.000Z',
327+
updatedAt: '2018-09-03T09:59:45.000Z',
328+
firstName: 'Chas',
329+
lastName: 'Dalrymple',
330+
email: 'inadaptive@brucine.net',
331+
},
332+
{
333+
id: '6',
334+
group: 'Third',
335+
createdAt: '2018-09-03T09:59:43.000Z',
336+
updatedAt: '2018-09-03T09:59:43.000Z',
337+
firstName: 'Preston',
338+
lastName: 'Bonini',
339+
email: 'tiza@unlovingness.net',
340+
},
341+
{
342+
id: '7',
343+
group: 'Third',
344+
createdAt: '2018-09-03T09:59:45.000Z',
345+
updatedAt: '2018-09-03T09:59:45.000Z',
346+
firstName: 'Chas',
347+
lastName: 'Dalrymple',
348+
email: 'inadaptive@brucine.net',
349+
},
350+
{
351+
id: '8',
352+
group: 'First',
353+
createdAt: '2018-09-03T09:59:45.000Z',
354+
updatedAt: '2018-09-03T09:59:45.000Z',
355+
firstName: 'Chas',
356+
lastName: 'Dalrymple',
357+
email: 'inadaptive@brucine.net',
358+
},
359+
];
360+
286361
const fetchData = async (page, pageSize) => {
287362
await (() => new Promise(resolve => setTimeout(resolve, 5000)))();
288363

@@ -418,6 +493,28 @@ defaultStory.story = {
418493
name: 'default',
419494
};
420495

496+
export const grouped = () => (
497+
<div style={{ display: 'flex', height: '600px' }}>
498+
<TableBuilder
499+
columns={ TABLE_COLUMNS }
500+
data={ GROUPED_TABLE_DATA }
501+
groupBy={ (data) =>
502+
data.reduce(
503+
(acc, item) => {
504+
if (!acc[item.group]) {
505+
return { ...acc, [item.group]: [item] };
506+
}
507+
acc[item.group] = [...acc[item.group], item];
508+
return acc;
509+
}, {}) }
510+
/>
511+
</div>
512+
);
513+
514+
grouped.story = {
515+
name: 'grouped',
516+
};
517+
421518
export const withLoader = () => (
422519
<div style={{ display: 'flex', height: '600px' }}>
423520
<Table>

src/components/Table/TableBodyRow.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ const [TableBodyRowTag, theme] = createThemeTag(name, ({ COLORS }: *) => ({
3535
borderLeft: `1px solid ${COLORS.SECONDARY_BORDER_COLOR}`,
3636
borderRight: `1px solid ${COLORS.SECONDARY_BORDER_COLOR}`,
3737
},
38+
borderedLess: {
39+
border: 'none',
40+
},
3841
},
3942
}));
4043

src/components/Table/TableBuilder.js

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import { TableHeaderCell } from './TableHeaderCell';
1111
import { Table } from './Table';
1212
import { Checkbox } from '../Checkbox';
1313
import { Pagination } from '../Pagination';
14+
import { TableBodyRow } from './TableBodyRow';
15+
import { TableBodyCell } from './TableBodyCell';
16+
import { Heading } from '../Heading';
1417

1518
const DEFAULT_SORT_ENABLE = true;
1619

@@ -63,7 +66,7 @@ type TableBulderProps = {
6366
withPagination?: boolean,
6467
/** Options to show loader */
6568
loading?: boolean,
66-
/** Calback to render cell */
69+
/** Callback to render cell */
6770
renderCell?: (column: ColumnType, data: any, opts: { expandRow: () => void, isExpanded?: boolean }) => React$Node,
6871
/** Callback to render head cell */
6972
renderHeadCell?: (column: ColumnType) => React$Node,
@@ -77,6 +80,10 @@ type TableBulderProps = {
7780
expandedRowRender?: (data: ExpandedRowRenderData) => React$Node,
7881
/** Callback executed when the row `isExpanded` state is changed */
7982
onExpand?: ({ key: string, isExpanded: boolean }) => void,
83+
/** Callback to group data by field*/
84+
groupBy: <T: $Shape<any>>(data: T[]) => { [key: string]: T[]},
85+
/** Callback to render grouped table title */
86+
renderGroupTitle?: (key: string, data: Array<Object>) => React$Node,
8087
};
8188

8289
type TableBuilderState = {|
@@ -315,12 +322,11 @@ class TableBuilder extends PureComponent<TableBulderProps, TableBuilderState> {
315322
);
316323
}
317324

318-
renderBody = () => {
325+
renderBody = (data: Array<Object>) => {
319326
const {
320327
columns,
321328
onActionClick,
322329
action,
323-
data,
324330
withSelection,
325331
renderCell,
326332
noData,
@@ -364,6 +370,32 @@ class TableBuilder extends PureComponent<TableBulderProps, TableBuilderState> {
364370
);
365371
}
366372

373+
renderContent = () => {
374+
const { groupBy, data, renderGroupTitle } = this.props;
375+
376+
if (groupBy && typeof groupBy === 'function') {
377+
378+
const groupedData = groupBy(data) || {};
379+
return (
380+
<div style={{ overflow: 'auto' }}>
381+
{ Object.keys(groupedData).map((key) => (
382+
<div key={ key }>
383+
{ renderGroupTitle && typeof renderGroupTitle === 'function' ?
384+
renderGroupTitle(key, groupedData[key]) :
385+
<TableBodyRow borderedLess>
386+
<TableBodyCell>
387+
<Heading type="h4">{ key }</Heading>
388+
</TableBodyCell>
389+
</TableBodyRow> }
390+
{ this.renderBody(groupedData[key]) }
391+
</div>
392+
)) }
393+
</div>);
394+
}
395+
396+
return this.renderBody(data);
397+
};
398+
367399
onChangePagination = (page: number, pageSize: number) => {
368400
const { onChange, tableState } = this.props;
369401

@@ -404,7 +436,7 @@ class TableBuilder extends PureComponent<TableBulderProps, TableBuilderState> {
404436
return (
405437
<Table modifiers={ rest }>
406438
{ this.renderHeader() }
407-
{ this.renderBody() }
439+
{ this.renderContent() }
408440
{ this.renderFooter() }
409441
</Table>
410442
);

0 commit comments

Comments
 (0)