Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(table)!: expandable rows #759

Merged
merged 13 commits into from
Apr 24, 2024
Merged
6 changes: 3 additions & 3 deletions packages/react/src/components/table/table-footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import {
flexRender,
RowData,
} from '@tanstack/react-table';
import { CustomColumnDef } from './types';
import { TableColumn } from './types';

interface CustomFooter<TData extends RowData, TValue = unknown> extends Header<TData, TValue> {
column: Column<TData, TValue> & {
columnDef: CustomColumnDef<TData, TValue>;
columnDef: TableColumn<TData, TValue>;
};
}

Expand Down Expand Up @@ -73,7 +73,7 @@ export const TableFooter = <T extends object>({
footerGroup,
sticky,
}: TableFooterProps<T>): ReactElement => (
<StyleFooterRow key={footerGroup.id} $sticky={sticky}>
<StyleFooterRow $sticky={sticky}>
{footerGroup.headers.map((footer) => getFooter(footer as CustomFooter<T>, sticky))}
</StyleFooterRow>
);
11 changes: 6 additions & 5 deletions packages/react/src/components/table/table-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import {
flexRender,
RowData,
} from '@tanstack/react-table';
import { devConsole } from '../../utils/dev-console';
import { SortButtonIcon, SortState } from './sort-button-icon';
import { CustomColumnDef } from './types';
import { TableColumn } from './types';

interface CustomHeader<TData extends RowData, TValue = unknown> extends Header<TData, TValue> {
column: Column<TData, TValue> & {
columnDef: CustomColumnDef<TData, TValue>;
columnDef: TableColumn<TData, TValue>;
};
}

Expand Down Expand Up @@ -77,12 +78,12 @@ function getHeading<TData extends object, TValue>(
}

if (!header.column.columnDef.header && !header.column.columnDef.headerAriaLabel) {
console.warn(
devConsole.warn(
`aria-label missing for column ${header.id} without text. please add headerAriaLabel to column.`,
);
}

if (header.column.getCanSort()) {
if (header.column.columnDef.sortable) {
return (
<StyledHeader
aria-label={header.column.columnDef.headerAriaLabel}
Expand Down Expand Up @@ -137,7 +138,7 @@ export const TableHeader = <T extends object>({
headerGroup,
sticky,
}: TableHeaderProps<T>): ReactElement => (
<StyleHeaderRow key={headerGroup.id} $sticky={sticky}>
<StyleHeaderRow $sticky={sticky}>
{headerGroup.headers.map((header) => getHeading(header as CustomHeader<T>))}
</StyleHeaderRow>
);
15 changes: 7 additions & 8 deletions packages/react/src/components/table/table-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@ import {
} from '@tanstack/react-table';
import styled, { css, FlattenInterpolation, ThemedStyledProps, ThemeProps } from 'styled-components';
import { ResolvedTheme } from '../../themes/theme';
import { CustomColumnDef } from './types';
import { TableColumn } from './types';

interface StyledTableRowProps {
$clickable: boolean;
$error: boolean;
$selected: boolean;
$clickable?: boolean;
$error?: boolean;
$selected?: boolean;
$striped?: boolean;
}

interface CustomCell<TData extends RowData, TValue = unknown> extends Cell<TData, TValue> {
column: Column<TData, TValue> & {
columnDef: CustomColumnDef<TData, TValue>;
columnDef: TableColumn<TData, TValue>;
};
}

Expand Down Expand Up @@ -66,7 +66,7 @@ function getCellBackgroundCss({
`;
}

const StyledTableRow = styled.tr<StyledTableRowProps>`
export const StyledTableRow = styled.tr<StyledTableRowProps>`
&:not(:first-child) {
border-top: 1px solid ${({ theme }) => theme.component['table-row-border-color']};
}
Expand Down Expand Up @@ -167,14 +167,13 @@ export const TableRow = <T extends object>({
}: TableRowProps<T>): ReactElement => (
<StyledTableRow
$clickable={!!onClick}
data-testid={`table-row-${row.index}`}
$error={error}
key={row.id}
$striped={striped}
$selected={row.getIsSelected()}
onClick={() => onClick && onClick(row)}
// eslint-disable-next-line react/jsx-props-no-spreading
{...(onClick ? { tabIndex: 0, role: 'button' } : null)}
data-testid={`table-row-${row.id}`}
>
{row.getVisibleCells().map((cell) => getCell(cell as CustomCell<T>))}
</StyledTableRow>
Expand Down
47 changes: 25 additions & 22 deletions packages/react/src/components/table/table.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { getByTestId } from '../../test-utils/enzyme-selectors';
import { mountWithProviders, mountWithTheme, renderWithProviders } from '../../test-utils/renderer';
import { DeviceType } from '../device-context-provider/device-context-provider';
import { Table, TableColumn, TableProps } from './table';
import { Table, TableProps } from './table';
import { TableColumn } from './types';

interface TestData {
column1: string;
Expand Down Expand Up @@ -33,17 +34,17 @@ const data: TestData[] = [
];

function renderTable(
columnsArray: TableColumn<TestData>,
columnsArray: TableColumn<TestData>[],
currentDevice?: DeviceType,
props?: TablePropsLite,
): cheerio.Cheerio {
return renderWithProviders(
<Table<TestData> columns={columnsArray} data={data} {...props} />,
<Table columns={columnsArray} data={data} {...props} />,
currentDevice,
);
}

const columnsWithHeaderAriaLabel: TableColumn<TestData> = [
const columnsWithHeaderAriaLabel: TableColumn<TestData>[] = [
{
header: 'Column 1',
headerAriaLabel: 'column 1 aria label',
Expand All @@ -56,7 +57,7 @@ const columnsWithHeaderAriaLabel: TableColumn<TestData> = [
},
];

const columns: TableColumn<TestData> = [
const columns: TableColumn<TestData>[] = [
{
header: 'Column 1',
accessorKey: 'column1',
Expand All @@ -67,7 +68,7 @@ const columns: TableColumn<TestData> = [
},
];

const columnsTextAligned: TableColumn<TestData> = [
const columnsTextAligned: TableColumn<TestData>[] = [
{
header: 'Column 1',
accessorKey: 'column1',
Expand All @@ -80,7 +81,7 @@ const columnsTextAligned: TableColumn<TestData> = [
},
];

const columnsSorted: TableColumn<TestData> = [
const columnsSorted: TableColumn<TestData>[] = [
{
header: 'Column 1',
accessorKey: 'column1',
Expand All @@ -93,7 +94,7 @@ const columnsSorted: TableColumn<TestData> = [
},
];

const columnsSticky: TableColumn<TestData3Columns> = [
const columnsSticky: TableColumn<TestData3Columns>[] = [
{
header: 'Column 1',
accessorKey: 'column1',
Expand Down Expand Up @@ -147,19 +148,21 @@ const stickyColumnsData: TestData3Columns[] = [

describe('Table', () => {
test('column sorting should be set to defaultSort value when defaultSort is set', () => {
const wrapper = mountWithProviders(<Table
columns={columnsSorted}
data={data}
defaultSort={{ id: 'column1', desc: false }}
/>);
const wrapper = mountWithProviders(
<Table
columns={columnsSorted}
data={data}
defaultSort={{ id: 'column1', desc: false }}
/>,
);

expect(getByTestId(wrapper, 'sort-icon').prop('sort')).toBe('ascending');
});

test('onRowClick callback is called when a row is clicked', () => {
const callback = jest.fn();
const wrapper = mountWithTheme(
<Table<TestData>
<Table
selectableRows
columns={columns}
data={data}
Expand All @@ -176,7 +179,7 @@ describe('Table', () => {
const callback = jest.fn();

mountWithTheme(
<Table<TestData>
<Table
selectableRows
columns={columns}
data={data}
Expand All @@ -191,7 +194,7 @@ describe('Table', () => {
test('onSelectedRowsChange callback is called when row-checkbox is checked', () => {
const callback = jest.fn();
const wrapper = mountWithTheme(
<Table<TestData>
<Table
selectableRows
columns={columns}
data={data}
Expand All @@ -207,7 +210,7 @@ describe('Table', () => {
test('onSelectedRowsChange callback is called with all rows when row-checkbox-all is checked', () => {
const callback = jest.fn();
const wrapper = mountWithTheme(
<Table<TestData>
<Table
selectableRows
columns={columns}
data={data}
Expand Down Expand Up @@ -275,32 +278,32 @@ describe('Table', () => {
});

test('has error rows styles', () => {
const tree = renderWithProviders(<Table<TestData> columns={columns} data={errorData} />);
const tree = renderWithProviders(<Table columns={columns} data={errorData} />);

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

test('has selectable rows styles', () => {
const tree = renderWithProviders(<Table<TestData> selectableRows columns={columns} data={data} />);
const tree = renderWithProviders(<Table selectableRows columns={columns} data={data} />);

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

test('has sticky header styles', () => {
const tree = renderWithProviders(<Table<TestData> stickyHeader columns={columns} data={data} />);
const tree = renderWithProviders(<Table stickyHeader columns={columns} data={data} />);

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

test('has sticky column styles', () => {
const tree = renderWithProviders(<Table<TestData3Columns> columns={columnsSticky} data={stickyColumnsData} />);
const tree = renderWithProviders(<Table columns={columnsSticky} data={stickyColumnsData} />);

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

test('has aria-label on header columns', () => {
const tree = renderWithProviders(
<Table<TestData>
<Table
columns={columnsWithHeaderAriaLabel}
data={data}
/>,
Expand Down
Loading
Loading