Skip to content

Commit

Permalink
feat(DataGrid): Add column grouping
Browse files Browse the repository at this point in the history
  • Loading branch information
m7kvqbe1 committed May 20, 2024
1 parent bc105b0 commit c443f69
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ export const storyAccessibilityConfig = {
enabled: false,
},
],
'Data Grid': [
{
id: 'empty-table-header',
enabled: false,
},
],
'Date Picker': [
{
id: 'aria-required-attr',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { fn } from '@storybook/test'
import { StoryFn, Meta } from '@storybook/react'
import { ColumnDef } from '@tanstack/react-table'

import { storyAccessibilityConfig } from '../../a11y/storyAccessibilityConfig'
import { DataGrid, TABLE_COLUMN_ALIGNMENT } from '.'
import { Badge } from '../Badge'

Expand All @@ -15,6 +16,14 @@ type Order = {
price: string
}

const disableEmptyTableHeaderRule = {
a11y: {
config: {
rules: storyAccessibilityConfig['Data Grid'],
},
},
}

// const generateRandomData = (length: number): Order[] => {
// const data: Order[] = Array.from({ length }, (_, i) => {
// return {
Expand Down Expand Up @@ -321,10 +330,59 @@ ColumnAlignment.args = {
onSelectedRowsChange: fn(),
}

const mergedColumns = columns.map((item, index) => ({
...item,
...columnsWithArbitraryContent[index],
enableSorting: true,
const groupedColumns = [
{
header: 'Group 1',
columns: [
{
header: 'Name',
accessorKey: 'productName',
enableSorting: false,
},
{
header: 'Price',
accessorKey: 'price',
enableSorting: false,
},
],
},
{
header: 'Group 2',
columns: [
{
header: 'Quantity',
accessorKey: 'quantity',
enableSorting: false,
},
],
},
]

export const ColumnGrouping: StoryFn<typeof DataGrid> = (props) => {
return (
<Wrapper>
<DataGrid {...props} columns={groupedColumns} />
</Wrapper>
)
}

ColumnGrouping.storyName = 'Column grouping'
ColumnGrouping.args = {
data,
isFullWidth: true,
onSelectedRowsChange: fn(),
}

const mergedColumns = groupedColumns.map((group) => ({
...group,
columns: group.columns.map((column) => ({
...column,
...columnsWithArbitraryContent.find(
// @ts-ignore
(c) => c.accessorKey === column.accessorKey
),
enableSorting: true,
})),
}))

export const KitchenSink: StoryFn<typeof DataGrid> = (props) => {
Expand All @@ -342,6 +400,7 @@ export const KitchenSink: StoryFn<typeof DataGrid> = (props) => {
}

KitchenSink.storyName = 'Kitchen sink'
KitchenSink.parameters = disableEmptyTableHeaderRule
KitchenSink.args = {
columns: mergedColumns,
data,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -488,4 +488,65 @@ describe('DataGrid', () => {
})
})
})

describe('with column grouping', () => {
const columnGroups: ColumnDef<DataRow>[] = [
{
header: 'Group 1',
columns: [
{
accessorKey: 'first',
header: 'First',
cell: (info) => info.getValue(),
enableSorting: true,
},
{
accessorKey: 'second',
header: 'Second',
cell: (info) => info.getValue(),
enableSorting: false,
},
],
},
{
header: 'Group 2',
columns: [
{
accessorKey: 'third',
header: 'Third',
cell: (info) => info.getValue(),
enableSorting: true,
},
],
},
]

beforeEach(() => {
render(<DataGrid data={data} columns={columnGroups} />)
})

it('should render the column groups correctly', () => {
expect(screen.getByText('Group 1')).toBeInTheDocument()
expect(screen.getByText('Group 2')).toBeInTheDocument()

expect(screen.getByText('First')).toBeInTheDocument()
expect(screen.getByText('Second')).toBeInTheDocument()
expect(screen.getByText('Third')).toBeInTheDocument()
})

it('should render the column groups with the correct order', () => {
const firstRow = screen.getAllByRole('row')[0]
const firstRowHeaders = within(firstRow).getAllByRole('columnheader')

expect(firstRowHeaders[0]).toHaveTextContent('Group 1')
expect(firstRowHeaders[1]).toHaveTextContent('Group 2')

const secondRow = screen.getAllByRole('row')[1]
const secondRowHeaders = within(secondRow).getAllByRole('columnheader')

expect(secondRowHeaders[0]).toHaveTextContent('First')
expect(secondRowHeaders[1]).toHaveTextContent('Second')
expect(secondRowHeaders[2]).toHaveTextContent('Third')
})
})
})
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
/// <reference path="./DataGrid.d.ts" />

import React, { useState, useEffect } from 'react'
import React, { useState, useEffect, useMemo } from 'react'
import _noop from 'lodash/noop'
import {
flexRender,
Expand Down Expand Up @@ -214,6 +214,12 @@ export const DataGrid = <T extends object>(props: DataGridProps<T>) => {
)
}, [rowSelection, table, onSelectedRowsChange])

const hasGroupedHeaders = useMemo(() => {
return table.getHeaderGroups().reduce((acc, group) => {
return acc || group.headers.some((header) => header.depth > 1)
}, false)
}, [table])

return (
<StyledDataGrid className={className}>
<StyledTable
Expand All @@ -225,7 +231,7 @@ export const DataGrid = <T extends object>(props: DataGridProps<T>) => {
<StyledHead>
{table
.getHeaderGroups()
.map(({ id: headerGroupId, headers }: HeaderGroup<T>) => (
.map(({ id: headerGroupId, headers, depth }: HeaderGroup<T>) => (
<StyledRow key={headerGroupId}>
{headers.map(
({
Expand All @@ -239,14 +245,17 @@ export const DataGrid = <T extends object>(props: DataGridProps<T>) => {
id: headerId,
isPlaceholder,
getContext,
colSpan,
}: Header<T, unknown>) => (
<StyledCol
aria-sort={getAriaSort(getCanSort(), getIsSorted())}
$isSortable={getCanSort()}
$isHeaderGroup={hasGroupedHeaders && depth === 0}
$alignment={columnDef.meta?.align}
$width={getSize() === 150 ? undefined : getSize()}
key={headerId}
onClick={getToggleSortingHandler()}
colSpan={colSpan}
>
<div>
{isPlaceholder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,19 @@ interface StyledColProps {
$isSortable?: boolean
$alignment?: 'left' | 'right' | 'center'
$width?: number
$isHeaderGroup?: boolean
}

export const StyledCol = styled.th<StyledColProps>`
position: relative;
padding: ${spacing('9')} ${spacing('4')} ${spacing('9')} ${spacing('8')};
width: ${({ $width }) => $width || 'auto'};
border-bottom: 1px solid ${color('neutral', '200')};
color: ${color('neutral', '600')};
font-weight: 700;
text-align: left;
text-transform: 'capitalize';
font-size: ${fontSize('s')};
color: ${color('neutral', '600')};
font-weight: 600;
text-transform: capitalize;
border-bottom: 1px solid ${color('neutral', '200')};
> div {
display: flex;
Expand All @@ -43,6 +44,18 @@ export const StyledCol = styled.th<StyledColProps>`
background: ${color('neutral', '200')};
}
${({ $isHeaderGroup }) =>
$isHeaderGroup &&
css`
text-transform: uppercase;
font-size: ${fontSize('m')};
&:not(:last-of-type) > div::after {
top: 100%;
height: 80px;
}
`}
${({ $isSortable }) =>
$isSortable &&
css`
Expand Down

0 comments on commit c443f69

Please sign in to comment.