Skip to content

Commit

Permalink
feat(table,storybook): add header grouping to table header (#784)
Browse files Browse the repository at this point in the history
  • Loading branch information
mmorin-equisoft authored Apr 25, 2024
1 parent 09ce84b commit 7b35956
Show file tree
Hide file tree
Showing 6 changed files with 349 additions and 17 deletions.
52 changes: 36 additions & 16 deletions packages/react/src/components/table/table-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import {
Column,
flexRender,
RowData,
SortDirection,
} from '@tanstack/react-table';
import { devConsole } from '../../utils/dev-console';
import { SortButtonIcon, SortState } from './sort-button-icon';
import { TableColumn } from './types';
import { isAGroupColumn, isLastColumnInAGroup } from './utils/table-utils';

interface CustomHeader<TData extends RowData, TValue = unknown> extends Header<TData, TValue> {
column: Column<TData, TValue> & {
Expand All @@ -29,7 +31,14 @@ const SortButton = styled.button<{ $textAlign: string }>`
}
`;

const StyledHeader = styled.th<{ $sticky: boolean, $startOffset: number; $textAlign: CSSProperties['textAlign'] }>`
interface StyledHeaderProps {
hasRightBorder: boolean;
$sticky: boolean;
$startOffset: number;
$textAlign: CSSProperties['textAlign'];
}

const StyledHeader = styled.th<StyledHeaderProps>`
background-color: inherit;
box-sizing: border-box;
position: relative;
Expand All @@ -41,6 +50,10 @@ const StyledHeader = styled.th<{ $sticky: boolean, $startOffset: number; $textAl
z-index: 5;
`}
${({ hasRightBorder }) => hasRightBorder && css`
border-right: 1px solid ${({ theme }) => theme.greys.grey};
`}
&:before {
border-bottom: 1px solid ${({ theme }) => theme.greys.grey};
bottom: 0;
Expand All @@ -64,18 +77,21 @@ const StyledSortButtonIcon = styled(SortButtonIcon)`
margin-left: var(--spacing-1x);
`;

function getHeading<TData extends object, TValue>(
header: CustomHeader<TData, TValue>,
): ReactElement {
const currentSort = header.column.getIsSorted();
let sortState: SortState = 'none';

if (currentSort === 'asc') {
sortState = 'ascending';
}
if (currentSort === 'desc') {
sortState = 'descending';
function getSortState(currentSort: false | SortDirection): SortState {
switch (currentSort) {
case 'asc':
return 'ascending';
case 'desc':
return 'descending';
default:
return 'none';
}
}

function getHeading<TData extends object, TValue>(header: CustomHeader<TData, TValue>): ReactElement {
const colSpan = header.colSpan > 1 ? header.colSpan : undefined;
const hasRightBorder = isAGroupColumn(header.column) || isLastColumnInAGroup(header.column);
const sortState: SortState = getSortState(header.column.getIsSorted());

if (!header.column.columnDef.header && !header.column.columnDef.headerAriaLabel) {
devConsole.warn(
Expand All @@ -88,12 +104,14 @@ function getHeading<TData extends object, TValue>(
<StyledHeader
aria-label={header.column.columnDef.headerAriaLabel}
aria-sort={sortState}
key={header.id}
className={header.column.columnDef.className ?? ''}
colSpan={colSpan}
hasRightBorder={hasRightBorder}
key={header.id}
scope="col"
$textAlign={header.column.columnDef.textAlign}
$startOffset={header.getStart()}
$sticky={header.column.columnDef.sticky ?? false}
$textAlign={header.column.columnDef.textAlign}
>
{header.isPlaceholder ? null : (
<SortButton
Expand All @@ -114,12 +132,14 @@ function getHeading<TData extends object, TValue>(
return (
<StyledHeader
aria-label={header.column.columnDef.headerAriaLabel}
key={header.id}
className={header.column.columnDef.className ?? undefined}
colSpan={colSpan}
hasRightBorder={hasRightBorder}
key={header.id}
scope="col"
$textAlign={header.column.columnDef.textAlign}
$startOffset={header.getStart()}
$sticky={header.column.columnDef.sticky ?? false}
$textAlign={header.column.columnDef.textAlign}
>
{!header.isPlaceholder && flexRender(
header.column.columnDef.header,
Expand Down
15 changes: 14 additions & 1 deletion packages/react/src/components/table/table-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import styled, { css, FlattenInterpolation, ThemedStyledProps, ThemeProps } from 'styled-components';
import { ResolvedTheme } from '../../themes/theme';
import { TableColumn } from './types';
import { isLastColumnInAGroup } from './utils/table-utils';

interface StyledTableRowProps {
$clickable?: boolean;
Expand All @@ -23,6 +24,13 @@ interface CustomCell<TData extends RowData, TValue = unknown> extends Cell<TData
};
}

interface StyledCellProps {
hasRightBorder: boolean;
$sticky?: boolean,
$startOffset: number;
$textAlign: CSSProperties['textAlign']
}

function getRowBackgroundColor({
theme,
$selected,
Expand Down Expand Up @@ -128,7 +136,7 @@ export const StyledTableRow = styled.tr<StyledTableRowProps>`
${getCellBackgroundCss}
`;

const StyledCell = styled.td<{ $sticky?: boolean, $startOffset: number; $textAlign: CSSProperties['textAlign'] }>`
const StyledCell = styled.td<StyledCellProps>`
background-color: inherit;
text-align: ${({ $textAlign }) => $textAlign};
Expand All @@ -137,6 +145,10 @@ const StyledCell = styled.td<{ $sticky?: boolean, $startOffset: number; $textAli
position: sticky;
z-index: 2;
`}
${({ hasRightBorder }) => hasRightBorder && css`
border-right: 1px solid ${({ theme }) => theme.greys.grey};
`}
`;

function getCell<TData extends object, TValue>(cell: CustomCell<TData, TValue>): ReactElement | null {
Expand All @@ -146,6 +158,7 @@ function getCell<TData extends object, TValue>(cell: CustomCell<TData, TValue>):
$textAlign={cell.column.columnDef.textAlign}
$startOffset={cell.column.getStart()}
key={cell.id}
hasRightBorder={isLastColumnInAGroup(cell.column)}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</StyledCell>
Expand Down
36 changes: 36 additions & 0 deletions packages/react/src/components/table/table.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,31 @@ const columnsWithHeaderAriaLabel: TableColumn<TestData>[] = [
},
];

const columnsWithHeadersGrouped: TableColumn<TestData>[] = [
{
header: 'Group 1',
columns: [
{
header: 'Column 1',
accessorKey: 'column1',
},
{
header: 'Column 2',
accessorKey: 'column2',
},
],
},
{
header: 'Group 2',
columns: [
{
header: 'Column 3',
accessorKey: 'column3',
},
],
},
];

const columns: TableColumn<TestData>[] = [
{
header: 'Column 1',
Expand Down Expand Up @@ -311,4 +336,15 @@ describe('Table', () => {

expect(tree).toMatchSnapshot();
});

test('has column with headers grouped', () => {
const tree = renderWithProviders(
<Table<TestData>
columns={columnsWithHeadersGrouped}
data={data}
/>,
);

expect(tree).toMatchSnapshot();
});
});
Loading

0 comments on commit 7b35956

Please sign in to comment.