Files
grafana/packages/grafana-ui/src/components/InteractiveTable/InteractiveTable.test.tsx
T
Tom Ratcliffe e2cb3e74f3 Chore: Apply testing-library rules to grafana-ui package tests (#105140)
* Apply testing-library rules to grafana-ui package tests

* Apply auto fixes

* Fix eslint test issues in grafana-ui package

* Fix prettier issues

* Address review feedback
2025-05-09 17:45:33 +01:00

251 lines
10 KiB
TypeScript

import { render, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import * as React from 'react';
import { InteractiveTable } from './InteractiveTable';
import { Column } from './types';
interface TableData {
id: string;
country?: string;
value?: string;
}
function getRowId(row: TableData) {
return row.id;
}
function setup(jsx: React.JSX.Element) {
render(jsx);
return {
user: userEvent.setup(),
};
}
describe('InteractiveTable', () => {
it('should not render hidden columns', () => {
const columns: Array<Column<TableData>> = [
{ id: 'id', header: 'ID' },
{ id: 'country', header: 'Country', visible: () => false },
];
const data: TableData[] = [
{ id: '1', country: 'Sweden' },
{ id: '2', country: 'Portugal' },
];
render(<InteractiveTable columns={columns} data={data} getRowId={getRowId} />);
expect(screen.getByRole('columnheader', { name: 'ID' })).toBeInTheDocument();
expect(screen.queryByRole('columnheader', { name: 'Country' })).not.toBeInTheDocument();
});
it('should correctly sort rows', async () => {
// We are not testing the sorting logic here since it is already tested in react-table,
// but instead we are testing that the sorting is applied correctly to the table and correct aria attributes are set
// according to https://www.w3.org/WAI/ARIA/apg/example-index/table/sortable-table
const columns: Array<Column<TableData>> = [
{ id: 'id', header: 'ID' },
{ id: 'value', header: 'Value', sortType: 'string' },
{ id: 'country', header: 'Country', sortType: 'number' },
];
const data: TableData[] = [
{ id: '1', value: '1', country: 'Sweden' },
{ id: '2', value: '3', country: 'Portugal' },
{ id: '3', value: '2', country: 'Italy' },
];
const { user } = setup(<InteractiveTable columns={columns} data={data} getRowId={getRowId} />);
const valueColumnHeader = screen.getByRole('columnheader', { name: 'Value' });
const countryColumnHeader = screen.getByRole('columnheader', { name: 'Country' });
const valueColumnSortButton = within(valueColumnHeader).getByRole('button');
const countryColumnSortButton = within(countryColumnHeader).getByRole('button');
expect(valueColumnHeader).not.toHaveAttribute('aria-sort');
expect(countryColumnHeader).not.toHaveAttribute('aria-sort');
await user.click(countryColumnSortButton);
expect(valueColumnHeader).not.toHaveAttribute('aria-sort');
expect(countryColumnHeader).toHaveAttribute('aria-sort', 'ascending');
await user.click(valueColumnSortButton);
expect(valueColumnHeader).toHaveAttribute('aria-sort', 'ascending');
expect(countryColumnHeader).not.toHaveAttribute('aria-sort');
await user.click(valueColumnSortButton);
expect(valueColumnHeader).toHaveAttribute('aria-sort', 'descending');
expect(countryColumnHeader).not.toHaveAttribute('aria-sort');
await user.click(valueColumnSortButton);
expect(valueColumnHeader).not.toHaveAttribute('aria-sort');
expect(countryColumnHeader).not.toHaveAttribute('aria-sort');
});
describe('row expansion', () => {
it('correctly expands rows', async () => {
const columns: Array<Column<TableData>> = [{ id: 'id', header: 'ID' }];
const data: TableData[] = [{ id: '1', value: '1', country: 'Sweden' }];
const { user } = setup(
<InteractiveTable
columns={columns}
data={data}
getRowId={getRowId}
renderExpandedRow={(row) => <div data-testid={`test-${row.id}`}>{row.country}</div>}
/>
);
const expanderButton = screen.getByRole('button', { name: /toggle row expanded/i });
await user.click(expanderButton);
expect(screen.getByTestId('test-1')).toHaveTextContent('Sweden');
expect(expanderButton).toHaveAttribute(
// ancestor tr's id should match the expander button's aria-controls attribute
'aria-controls',
screen.getByTestId('test-1').parentElement?.parentElement?.id
);
});
it('does not render expand all when showExpandAll is false', async () => {
const columns: Array<Column<TableData>> = [{ id: 'id', header: 'ID' }];
const data: TableData[] = [{ id: '1', value: '1', country: 'Sweden' }];
setup(
<InteractiveTable
columns={columns}
data={data}
getRowId={getRowId}
renderExpandedRow={(row) => <div data-testid={`test-${row.id}`}>{row.country}</div>}
showExpandAll={false}
/>
);
expect(screen.queryByRole('button', { name: 'Expand all rows' })).not.toBeInTheDocument();
});
it('does not render expand all when showExpandAll is not provided', async () => {
const columns: Array<Column<TableData>> = [{ id: 'id', header: 'ID' }];
const data: TableData[] = [{ id: '1', value: '1', country: 'Sweden' }];
setup(
<InteractiveTable
columns={columns}
data={data}
getRowId={getRowId}
renderExpandedRow={(row) => <div data-testid={`test-${row.id}`}>{row.country}</div>}
/>
);
expect(screen.queryByRole('button', { name: 'Expand all rows' })).not.toBeInTheDocument();
});
it('renders expand all when showExpandAll is true', async () => {
const columns: Array<Column<TableData>> = [{ id: 'id', header: 'ID' }];
const data: TableData[] = [{ id: '1', value: '1', country: 'Sweden' }];
setup(
<InteractiveTable
columns={columns}
data={data}
getRowId={getRowId}
renderExpandedRow={(row) => <div data-testid={`test-${row.id}`}>{row.country}</div>}
showExpandAll
/>
);
expect(screen.getByRole('button', { name: 'Expand all rows' })).toBeInTheDocument();
});
it('expands all rows when expand all is clicked', async () => {
const columns: Array<Column<TableData>> = [{ id: 'id', header: 'ID' }];
const data: TableData[] = [
{ id: '1', value: '1', country: 'Sweden' },
{ id: '2', value: '2', country: 'Belgium' },
{ id: '3', value: '3', country: 'France' },
];
const { user } = setup(
<InteractiveTable
columns={columns}
data={data}
getRowId={getRowId}
renderExpandedRow={(row) => <div data-testid={`test-${row.id}`}>{row.country}</div>}
showExpandAll
/>
);
expect(screen.queryByTestId('test-1')).not.toBeInTheDocument();
expect(screen.queryByTestId('test-2')).not.toBeInTheDocument();
expect(screen.queryByTestId('test-3')).not.toBeInTheDocument();
const expandAllButton = screen.getByRole('button', { name: 'Expand all rows' });
await user.click(expandAllButton);
expect(screen.getByTestId('test-1')).toBeInTheDocument();
expect(screen.getByTestId('test-2')).toBeInTheDocument();
expect(screen.getByTestId('test-3')).toBeInTheDocument();
});
});
describe('pagination', () => {
it('does not render pagination controls if pageSize is not set', () => {
const columns: Array<Column<TableData>> = [{ id: 'id', header: 'ID' }];
const data: TableData[] = [{ id: '1', value: '1', country: 'Sweden' }];
render(<InteractiveTable columns={columns} data={data} getRowId={getRowId} />);
expect(screen.queryByRole('button', { name: /next/i })).not.toBeInTheDocument();
expect(screen.queryByRole('button', { name: /previous/i })).not.toBeInTheDocument();
render(<InteractiveTable columns={columns} data={data} getRowId={getRowId} pageSize={0} />);
expect(screen.queryByRole('button', { name: /next/i })).not.toBeInTheDocument();
expect(screen.queryByRole('button', { name: /previous/i })).not.toBeInTheDocument();
});
it('renders pagination controls if pageSize is set and more items than page size', () => {
const columns: Array<Column<TableData>> = [{ id: 'id', header: 'ID' }];
const data: TableData[] = [
{ id: '1', value: '1', country: 'Sweden' },
{ id: '2', value: '2', country: 'Belgium' },
];
render(<InteractiveTable columns={columns} data={data} getRowId={getRowId} pageSize={1} />);
expect(screen.getByRole('button', { name: /next/i })).toBeInTheDocument();
expect(screen.getByRole('button', { name: /previous/i })).toBeInTheDocument();
});
it('does not render pagination controls if pageSize is set and fewer items than page size', () => {
const columns: Array<Column<TableData>> = [{ id: 'id', header: 'ID' }];
const data: TableData[] = [{ id: '1', value: '1', country: 'Sweden' }];
render(<InteractiveTable columns={columns} data={data} getRowId={getRowId} pageSize={10} />);
expect(screen.queryByRole('button', { name: /next/i })).not.toBeInTheDocument();
expect(screen.queryByRole('button', { name: /previous/i })).not.toBeInTheDocument();
});
});
describe('headerTooltip', () => {
it('does not render tooltips if headerTooltips is not set', () => {
const columns: Array<Column<TableData>> = [{ id: 'id', header: 'ID' }];
const data: TableData[] = [{ id: '1', value: '1', country: 'Sweden' }];
render(<InteractiveTable columns={columns} data={data} getRowId={getRowId} />);
expect(screen.queryByTestId('header-tooltip-icon')).not.toBeInTheDocument();
});
it('renders tooltips if headerTooltips is set', () => {
const columns: Array<Column<TableData>> = [{ id: 'id', header: 'ID' }];
const data: TableData[] = [{ id: '1', value: '1', country: 'Sweden' }];
const headerTooltips = {
id: { content: 'this is the id' },
};
render(<InteractiveTable columns={columns} data={data} getRowId={getRowId} headerTooltips={headerTooltips} />);
expect(screen.getByTestId('header-tooltip-icon')).toBeInTheDocument();
});
});
describe('controlled sort', () => {
it('should call fetchData with the correct sortBy argument', async () => {
const columns: Array<Column<TableData>> = [{ id: 'id', header: 'ID', sortType: 'string' }];
const data: TableData[] = [{ id: '1', value: '1', country: 'Sweden' }];
const fetchData = jest.fn();
render(<InteractiveTable columns={columns} data={data} getRowId={getRowId} fetchData={fetchData} />);
const valueColumnHeader = screen.getByRole('button', {
name: /id/i,
});
await userEvent.click(valueColumnHeader);
expect(fetchData).toHaveBeenCalledWith({ sortBy: [{ id: 'id', desc: false }] });
});
});
});