import React from 'react';
import 'jest-styled-components';
import { render, fireEvent, screen } from '@testing-library/react';
import { Grommet } from '../../Grommet';
import { Box } from '../../Box';
import { Button } from '../../Button';
import { Text } from '../../Text';
import { DataTable, Sections, SortType } from '..';
import { BackgroundType, BorderType } from '../../../utils';
interface TestDataItem {
a: string;
b: number;
}
const DATA: TestDataItem[] = [];
for (let i = 0; i < 95; i += 1) {
DATA.push({ a: `entry-${i}`, b: i });
}
describe('DataTable', () => {
test('empty', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('basic', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('!primaryKey', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('paths', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('primaryKey', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('footer', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('footer node', () => {
const { getByText } = render(
Total },
{ property: 'b', header: 'B' },
]}
data={[
{ a: 'one', b: 1 },
{ a: 'two', b: 2 },
]}
/>
,
);
expect(getByText('Total')).not.toBeNull();
});
test('sortable', () => {
const { container, getByText } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
const headerCell = getByText('A');
fireEvent.click(headerCell, {});
expect(container.firstChild).toMatchSnapshot();
});
test('sort null data', () => {
const { container, getByText } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
let headerCell = getByText('A');
fireEvent.click(headerCell, {});
expect(container.firstChild).toMatchSnapshot();
headerCell = getByText('C');
fireEvent.click(headerCell, {});
expect(container.firstChild).toMatchSnapshot();
headerCell = getByText('D');
fireEvent.click(headerCell, {});
expect(container.firstChild).toMatchSnapshot();
});
test('onSort', () => {
const onSort = jest.fn();
const { container, getByText } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
const headerCell = getByText('A');
fireEvent.click(headerCell, {});
expect(onSort).toBeCalledWith(
expect.objectContaining({ property: 'a', direction: 'asc' }),
);
expect(container.firstChild).toMatchSnapshot();
});
test('onSort external', () => {
const onSort = jest.fn();
const { container, getByText } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
const headerCell = getByText('A');
fireEvent.click(headerCell, {});
expect(onSort).toBeCalledWith(
expect.objectContaining({
property: 'a',
direction: 'desc',
external: true,
}),
);
expect(container.firstChild).toMatchSnapshot();
});
test('sort', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('sort controlled', () => {
const Test = () => {
const [sort, setSort] = React.useState({
property: 'a',
direction: 'asc',
});
return (
);
};
const { asFragment } = render();
expect(asFragment()).toMatchSnapshot();
const sortButton = screen.getByRole('button', { name: 'Sort data' });
fireEvent.click(sortButton);
expect(asFragment()).toMatchSnapshot();
});
test('sort nested object', () => {
const { container, getByText } = render(
datum.b && datum.b.value,
},
]}
data={[
{ a: 'zero', b: { value: 1 } },
{ a: 'one', b: { value: 2 } },
{ a: 'two', b: { value: 3 } },
]}
sort={{ property: 'b.value', direction: 'asc' }}
/>
,
);
expect(container.querySelectorAll('td').item(0).textContent).toBe('1');
expect(container.querySelectorAll('td').item(1).textContent).toBe('2');
expect(container.querySelectorAll('td').item(2).textContent).toBe('3');
fireEvent.click(getByText('Value'));
expect(container.querySelectorAll('td').item(0).textContent).toBe('3');
expect(container.querySelectorAll('td').item(1).textContent).toBe('2');
expect(container.querySelectorAll('td').item(2).textContent).toBe('1');
expect(container.firstChild).toMatchSnapshot();
});
test('sort nested object with onSort', () => {
const onSort = jest.fn();
const { container, getByText } = render(
datum.b && datum.b.value,
},
]}
data={[
{ a: 'zero', b: { value: 1 } },
{ a: 'one', b: { value: 2 } },
{ a: 'two', b: { value: 3 } },
]}
onSort={onSort}
sort={{ property: 'b.value', direction: 'asc' }}
/>
,
);
expect(container.querySelectorAll('td').item(0).textContent).toBe('1');
expect(container.querySelectorAll('td').item(1).textContent).toBe('2');
expect(container.querySelectorAll('td').item(2).textContent).toBe('3');
fireEvent.click(getByText('Value'));
expect(onSort).toBeCalledWith(
expect.objectContaining({ property: 'b.value' }),
);
expect(container.querySelectorAll('td').item(0).textContent).toBe('3');
expect(container.querySelectorAll('td').item(1).textContent).toBe('2');
expect(container.querySelectorAll('td').item(2).textContent).toBe('1');
expect(container.firstChild).toMatchSnapshot();
});
test('sort external', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('search', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
fireEvent.click(
container.querySelector(
'[aria-label="Open search by a"]',
) as HTMLButtonElement,
);
const searchInput = container.querySelector(
'[name="search-a"]',
) as HTMLInputElement;
expect(document.activeElement).toBe(searchInput);
fireEvent.change(searchInput, {
target: { value: '[' },
});
expect(container.firstChild).toMatchSnapshot();
});
test('resizeable', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('aggregate', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('aggregate with nested object', () => {
const { container, getByText } = render(
datum.obj2.value,
},
]}
data={[
{ a: 'one', obj: { value: 1 }, obj2: { value: 10 } },
{ a: 'two', obj: { value: 2 }, obj2: { value: 20 } },
]}
/>
,
);
expect(getByText('3')).toBeTruthy();
expect(container.firstChild).toMatchSnapshot();
});
test('rowDetails', () => {
const { container, getAllByLabelText } = render(
{row.a}}
primaryKey="b"
/>
,
);
const expandButtons = getAllByLabelText('expand');
fireEvent.click(expandButtons[1], {});
expect(container.firstChild).toMatchSnapshot();
});
test('rowDetails condtional', () => {
const { container, getAllByLabelText } = render(
{
if (row.b === 1.1) {
return {row.a} ;
}
return (
{row.a} : {row.b}{' '}
);
}}
primaryKey="b"
/>
,
);
const expandButtons = getAllByLabelText('expand');
fireEvent.click(expandButtons[1], {});
expect(container.firstChild).toMatchSnapshot();
});
test('groupBy', () => {
const { container, getByText } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
const headerCell = getByText('A');
fireEvent.click(headerCell, {});
expect(container.firstChild).toMatchSnapshot();
});
test('groupBy 0 value', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('groupBy toggle', () => {
function TestComponent() {
const [groupBy, setGroupBy] = React.useState();
const toggle = () => setGroupBy(groupBy === undefined ? 'a' : undefined);
return (
);
}
const { container, getByText } = render();
expect(container.firstChild).toMatchSnapshot();
fireEvent.click(getByText('toggle'));
expect(container.firstChild).toMatchSnapshot();
fireEvent.click(getByText('toggle'));
expect(container.firstChild).toMatchSnapshot();
});
test('click', () => {
const onClickRow = jest.fn();
const { container, getByText } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
fireEvent.click(getByText('beta'));
expect(onClickRow).toBeCalledWith(
expect.objectContaining({ datum: { a: 'beta' } }),
);
expect(container.firstChild).toMatchSnapshot();
});
test('disabled click', () => {
const onClickRow = jest.fn();
const { container, getByText } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
fireEvent.click(getByText('beta'));
expect(onClickRow).toBeCalledWith(
expect.objectContaining({ datum: { a: 'beta' } }),
);
expect(container.firstChild).toMatchSnapshot();
});
test('background', () => {
const backgrounds: (
| BackgroundType
| BackgroundType[]
| Sections
)[] = [
'accent-1',
['accent-1', 'accent-2'],
{ header: 'accent-1', body: 'accent-2', footer: 'accent-3' },
];
const { container } = render(
{backgrounds.map((background) => (
))}
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('border', () => {
const borders: (BorderType | Sections)[] = [
true,
'top',
{ color: 'accent-1', side: 'top', size: 'small' },
{
header: 'top',
body: { color: 'accent-1', side: 'top', size: 'small' },
},
];
const { container } = render(
{borders.map((border) => (
))}
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('pad', () => {
const { container } = render(
{[
'small',
{ vertical: 'small', horizontal: 'medium' },
{
header: 'small',
body: { vertical: 'small', horizontal: 'medium' },
},
].map((pad) => (
))}
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('rowProps', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('groupBy property', () => {
const { container, getByText } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
const headerCell = getByText('A');
fireEvent.click(headerCell, {});
expect(container.firstChild).toMatchSnapshot();
});
test('groupBy expand', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('groupBy onExpand', () => {
const onExpand = jest.fn((groupState) => groupState);
const { getAllByLabelText } = render(
,
);
const expandButtons = getAllByLabelText('expand');
fireEvent.click(expandButtons[1], {});
expect(onExpand).toBeCalled();
expect(onExpand.mock.results[0].value).toEqual(['one']);
expect(onExpand.mock.results[0].value).toMatchSnapshot();
});
test('replace', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('themeColumnSizes', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('absoluteColumnSizes', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('relativeColumnSizes', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('fill', () => {
const fills: (boolean | 'vertical' | 'horizontal')[] = [
true,
'horizontal',
'vertical',
];
const { container } = render(
{fills.map((fill) => (
))}
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('pin', () => {
const pins: (boolean | 'header' | 'footer')[] = [true, 'header', 'footer'];
const { container } = render(
{pins.map((pin) => (
))}
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('pin + background', () => {
const theme = {
dataTable: {
pinned: {
header: {
background: {
color: 'blue',
},
},
footer: {
background: {
color: 'green',
},
},
},
},
};
const pins: (boolean | 'header' | 'footer')[] = [true, 'header', 'footer'];
const { container } = render(
{pins.map((pin) => (
))}
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('pin + background context', () => {
const { container } = render(
{[
'background-back',
'background-front',
{ color: 'background-back', dark: true },
].map((contextBackground) => (
))}
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('select', () => {
const onSelect = jest.fn();
const { container, getByLabelText } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
fireEvent.click(getByLabelText('select beta'));
expect(onSelect).toBeCalledWith(expect.arrayContaining(['alpha', 'beta']), {
a: 'beta',
});
expect(container.firstChild).toMatchSnapshot();
});
test('select with allowSelectAll={false}', () => {
const onSelect = jest.fn();
const { container, getByLabelText } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
fireEvent.click(getByLabelText('select beta'));
expect(onSelect).toBeCalledWith(expect.arrayContaining(['alpha', 'beta']), {
a: 'beta',
});
expect(container.firstChild).toMatchSnapshot();
});
test('disabled select', () => {
const onSelect = jest.fn();
const { container, getByText } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
fireEvent.click(getByText('alpha'));
expect(onSelect).not.toBeCalled();
});
test('custom theme', () => {
const customTheme = {
dataTable: {
header: {
background: 'skyblue',
border: {
color: 'brand',
size: 'medium',
},
gap: 'none',
pad: { horizontal: 'small', vertical: 'xsmall' },
font: {
weight: 'bold',
},
hover: {
background: {
color: 'light-2',
},
},
},
resize: {
hover: {
border: {
color: 'red',
side: 'end',
size: 'xsmall',
},
},
},
},
};
const { container, getByLabelText } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
fireEvent.mouseOver(getByLabelText('select beta'));
expect(container.firstChild).toMatchSnapshot();
});
test('units', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('placeholder', () => {
const { container } = render(
test placeholder}
/>
test placeholder}
/>
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('should paginate', () => {
const { container, getAllByText } = render(
,
);
const results = getAllByText('entry', { exact: false });
// default DataTable step 50
expect(results.length).toEqual(50);
expect(container.firstChild).toMatchSnapshot();
});
test('should pin and paginate', () => {
const { container, getAllByText } = render(
,
);
const results = getAllByText('entry', { exact: false });
expect(results.length).toEqual(50);
expect(container.firstChild).toMatchSnapshot();
});
test('should apply pagination styling', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('should show correct item index when "show" is a number', () => {
const show = 15;
const { container, getByText } = render(
,
);
const result = getByText(`entry-${show}`);
expect(result).toBeTruthy();
expect(container.firstChild).toMatchSnapshot();
});
test('should show correct page when "show" is { page: # }', () => {
const desiredPage = 2;
const { container } = render(
,
);
const activePage = (
container.querySelector(`[aria-current="page"]`) as HTMLButtonElement
).innerHTML;
expect(activePage).toEqual(`${desiredPage}`);
expect(container.firstChild).toMatchSnapshot();
});
test('should render correct num items per page (step)', () => {
const step = 14;
const { container, getAllByText } = render(
,
);
const results = getAllByText('entry', { exact: false });
expect(results.length).toEqual(step);
expect(container.firstChild).toMatchSnapshot();
});
test('should render new data when page changes', () => {
const { container, getByLabelText } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
fireEvent.click(getByLabelText('Go to next page'));
expect(container.firstChild).toMatchSnapshot();
});
test('should not show paginate controls when data is empty array', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('should not show paginate controls when length of data < step', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('onSelect select/unselect all', () => {
const onSelect = jest.fn();
const { container, getByLabelText } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
let headerCheckBox;
headerCheckBox = getByLabelText('select all');
fireEvent.click(headerCheckBox);
expect(onSelect).toBeCalledWith([1.1, 1.2, 2.1, 2.2]);
expect(container.firstChild).toMatchSnapshot();
// aria-label should have changed since all entries
// are selected
headerCheckBox = getByLabelText('unselect all');
fireEvent.click(headerCheckBox);
expect(onSelect).toBeCalledWith([]);
expect(container.firstChild).toMatchSnapshot();
});
test('onSelect + groupBy should select/deselect all when grouped', () => {
const onSelect = jest.fn();
const { container, getByLabelText } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
let headerCheckBox;
headerCheckBox = getByLabelText('select all');
fireEvent.click(headerCheckBox);
expect(container.firstChild).toMatchSnapshot();
// aria-label should have changed since all entries
// are selected
headerCheckBox = getByLabelText('unselect all');
fireEvent.click(headerCheckBox);
expect(container.firstChild).toMatchSnapshot();
});
test('onSelect + groupBy should select all items within a group', () => {
const onSelect = jest.fn();
const { container, getByLabelText } = render(
,
);
const groupCheckBox = getByLabelText('select one');
fireEvent.click(groupCheckBox);
expect(onSelect).toBeCalledWith(
expect.arrayContaining([1.1, 1.2]),
expect.objectContaining({ a: 'one' }),
);
expect(container.firstChild).toMatchSnapshot();
});
test(`onSelect + groupBy should render indeterminate checkbox on table and
group if subset of group items are selected`, () => {
const onSelect = jest.fn();
const { container, getAllByLabelText, getByLabelText } = render(
,
);
const groupCheckBox = getByLabelText('select one');
fireEvent.click(groupCheckBox);
const expandButtons = getAllByLabelText('expand');
fireEvent.click(expandButtons[1], {});
fireEvent.click(getByLabelText('unselect 1.1'));
expect(container.firstChild).toMatchSnapshot();
});
test(`onSelect + groupBy should render indeterminate checkbox on table and
group when controlled`, () => {
const onSelect = jest.fn();
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('verticalAlign', () => {
const { asFragment } = render(
,
);
expect(asFragment()).toMatchSnapshot();
});
test('should base table body max height on global size', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('should base table body max height on css value', () => {
const { container } = render(
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('rowProps on group header rows', () => {
const { container } = render(
Name with extra,
primary: true,
},
]}
rowProps={{ 'Fort Collins': { background: 'yellow' } }}
data={[
{ name: 'Bryan', location: 'Fort Collins' },
{ name: 'Doug', location: 'Fort Collins' },
{ name: 'Tracy', location: 'San Francisco' },
]}
groupBy="location"
/>
,
);
expect(container.firstChild).toMatchSnapshot();
});
test('border on CheckBox cell', () => {
const { asFragment } = render(
{}}
/>
,
);
expect(asFragment()).toMatchSnapshot();
});
test('onSelect datum argument should be defined', () => {
const onSelect = jest.fn();
render(
,
);
fireEvent.click(screen.getByRole('checkbox', { name: 'select Alan' }));
expect(onSelect).toBeCalledWith(['Alan'], { name: 'Alan', percent: 20 });
});
});