Skip to content

Commit

Permalink
Merge branch 'main' into brooke/data-testids
Browse files Browse the repository at this point in the history
  • Loading branch information
bruugey authored Feb 21, 2024
2 parents 466710f + 744092c commit cdae4af
Show file tree
Hide file tree
Showing 15 changed files with 365 additions and 267 deletions.
6 changes: 6 additions & 0 deletions .changeset/tasty-pens-sit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@leafygreen-ui/table': patch
---

Fixes a bug where nested row chevrons and selectable row checkboxes would be rendered above a sticky header.
[Jira Ticket](https://jira.mongodb.org/browse/LG-4037)
8 changes: 8 additions & 0 deletions .changeset/thick-dragons-vanish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@leafygreen-ui/table': minor
---

Exposes `virtualizerOptions` property in `useLeafyGreenTable` options. This is used to define options for `react-virtual`.
As of now, we have `[email protected]` as out virtualizer dependency. Virtualizer options for this version can be found [on GitHub](https://github.com/TanStack/virtual/blob/v2/docs/src/pages/docs/api.md)
Resolves [LG-3984](https://jira.mongodb.org/browse/LG-3984)

180 changes: 126 additions & 54 deletions packages/table/src/Cell/HeaderCell/HeaderCell.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import React from 'react';
import React, { useRef } from 'react';
import { ColumnDef, Header } from '@tanstack/react-table';
import {
queryByRole as globalQueryByRole,
render,
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { axe } from 'jest-axe';

import {
getTestColumnsProps,
useTestHookCall,
} from '../../utils/testHookCalls.testutils';
import { renderHook } from '@leafygreen-ui/testing-lib';

import useLeafyGreenTable, { LGTableDataType } from '../../useLeafyGreenTable';
import { Person } from '../../utils/makeData.testutils';

import HeaderCell, { HeaderCellProps } from '.';

Expand All @@ -34,23 +35,47 @@ function renderSimpleHeaderCell(props: HeaderCellProps<unknown>) {
);
}

const HeaderCellWithHook = (props: getTestColumnsProps) => {
const { containerRef, table } = useTestHookCall({
columnProps: props,
const headerCellTestColumns: Array<ColumnDef<Partial<Person>>> = [
{
accessorKey: 'firstName',
header: 'First Name',
enableSorting: true,
},
{
accessorKey: 'age',
header: 'Age',
enableSorting: true,
},
];

const headerCellTestData: Array<Partial<Person>> = [
{
firstName: 'Aaron',
age: 99,
},
{
firstName: 'Zara',
age: 21,
},
];

const TestSortableHeaderCell = () => {
const containerRef = useRef<HTMLDivElement>(null);

const table = useLeafyGreenTable({
containerRef,
columns: headerCellTestColumns,
data: headerCellTestData,
});

return (
<div ref={containerRef}>
<table>
<thead>
<tr>
<HeaderCell
data-testid="lg-header-cell-test"
header={table.getHeaderGroups()[0].headers[0]}
>
test header cell
</HeaderCell>
<th>th 2</th>
{table.getHeaderGroups()[0].headers.map(header => (
<HeaderCell key={header.id} header={header} />
))}
</tr>
</thead>
<tbody>
Expand All @@ -64,6 +89,20 @@ const HeaderCellWithHook = (props: getTestColumnsProps) => {
);
};

/** Returns the `header` data for a given column def */
const useMockTestHeaderData = (
columnDef: ColumnDef<any>,
): Header<LGTableDataType<any>, unknown> => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const table = useLeafyGreenTable({
containerRef: React.createRef<HTMLDivElement>(),
data: [],
columns: [columnDef],
});

return table.getFlatHeaders()[0];
};

describe('packages/table/HeaderCell', () => {
describe('a11y', () => {
test('does not have basic accessibility issues', async () => {
Expand All @@ -75,51 +114,84 @@ describe('packages/table/HeaderCell', () => {

describe('sort prop', () => {
test('sort prop renders sort icon button', async () => {
const { getByTestId } = render(<HeaderCellWithHook enableSorting />);
const sortIconButton = getByTestId('lg-table-sort-icon-button');
expect(sortIconButton).toBeInTheDocument();
const { getAllByTestId } = render(<TestSortableHeaderCell />);
const sortIconButtons = getAllByTestId('lg-table-sort-icon-button');
expect(sortIconButtons[0]).toBeInTheDocument();
expect(sortIconButtons[1]).toBeInTheDocument();
});

test('initial state of sort icon is unsorted', async () => {
const { queryByLabelText } = render(<HeaderCellWithHook enableSorting />);
const sortIcon = queryByLabelText('Unsorted Icon');
expect(sortIcon).toBeInTheDocument();
});
// Sorting behaves differently for string and numeric data
describe.each([
{
columnIndex: 0,
dataType: 'string',
initialSort: 'Ascending',
secondSort: 'Descending',
},
{
columnIndex: 1,
dataType: 'number',
initialSort: 'Descending',
secondSort: 'Ascending',
},
])('for $dataType data', ({ columnIndex, initialSort, secondSort }) => {
const iconButtonTestId = 'lg-table-sort-icon-button';

test('clicking sort icon switches to sort descending', async () => {
const { getByTestId } = render(<HeaderCellWithHook enableSorting />);
const sortIconButton = getByTestId('lg-table-sort-icon-button');
userEvent.click(sortIconButton);
const sortIcon = globalQueryByRole(sortIconButton, 'img');
expect(sortIcon).toHaveAttribute('aria-label', 'Sort Descending Icon');
});
test('initial state of sort icon is unsorted', async () => {
const { queryAllByLabelText } = render(<TestSortableHeaderCell />);
const unsortedIcon = queryAllByLabelText('Unsorted Icon')[columnIndex];
expect(unsortedIcon).toBeInTheDocument();
});

test('clicking sort icon twice switches to sort ascending', async () => {
const { getByTestId } = render(<HeaderCellWithHook enableSorting />);
const sortIconButton = getByTestId('lg-table-sort-icon-button');
userEvent.click(sortIconButton);
userEvent.click(sortIconButton);
const sortIcon = globalQueryByRole(sortIconButton, 'img');
expect(sortIcon).toHaveAttribute('aria-label', 'Sort Ascending Icon');
});
test(`clicking sort icon switches to sort ${initialSort}`, async () => {
const { getAllByTestId } = render(<TestSortableHeaderCell />);
const sortIconButton = getAllByTestId(iconButtonTestId)[columnIndex];
userEvent.click(sortIconButton);
const sortIcon = globalQueryByRole(sortIconButton, 'img');
expect(sortIcon).toHaveAttribute(
'aria-label',
`Sort ${initialSort} Icon`,
);
});

test(`clicking sort icon twice switches to sort ${secondSort}`, async () => {
const { getAllByTestId } = render(<TestSortableHeaderCell />);
const sortIconButton = getAllByTestId(iconButtonTestId)[columnIndex];
userEvent.click(sortIconButton);
userEvent.click(sortIconButton);
const sortIcon = globalQueryByRole(sortIconButton, 'img');
expect(sortIcon).toHaveAttribute(
'aria-label',
`Sort ${secondSort} Icon`,
);
});

test('clicking sort icon three times reverts to unsorted icon', async () => {
const { getByTestId } = render(<HeaderCellWithHook enableSorting />);
const sortIconButton = getByTestId('lg-table-sort-icon-button');
userEvent.click(sortIconButton);
userEvent.click(sortIconButton);
userEvent.click(sortIconButton);
const sortIcon = globalQueryByRole(sortIconButton, 'img');
expect(sortIcon).toHaveAttribute('aria-label', 'Unsorted Icon');
test(`clicking sort icon three times reverts to unsorted icon`, async () => {
const { getAllByTestId } = render(<TestSortableHeaderCell />);
const sortIconButton = getAllByTestId(iconButtonTestId)[columnIndex];
userEvent.click(sortIconButton);
userEvent.click(sortIconButton);
userEvent.click(sortIconButton);
const sortIcon = globalQueryByRole(sortIconButton, 'img');
expect(sortIcon).toHaveAttribute('aria-label', 'Unsorted Icon');
});
});
});
describe('width prop', () => {
test('setting custom size changes HeaderCell width', async () => {
const { getByTestId } = render(
<HeaderCellWithHook enableSorting size={700} />,
);
const headerCell = getByTestId('lg-header-cell-test');
expect(getComputedStyle(headerCell).width).toBe('700px');
});

test('setting size in columnDef sets HeaderCell width', async () => {
const { result } = renderHook(() =>
useMockTestHeaderData({
accessorKey: 'id',
size: 700,
}),
);

const mockHeader = result.current;

const { getByTestId } = render(
<HeaderCell data-testid="lg-header-cell-test" header={mockHeader} />,
);
const headerCell = getByTestId('lg-header-cell-test');
expect(getComputedStyle(headerCell).width).toBe('700px');
});
});
3 changes: 0 additions & 3 deletions packages/table/src/Table.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -546,20 +546,17 @@ export const SortableRows: StoryFn<StoryTableProps> = args => {
accessorFn: row => row.lastName,
id: 'lastName',
cell: info => info.getValue(),
// eslint-disable-next-line react/display-name
header: () => <span>Last Name</span>,
enableSorting: true,
},
{
accessorKey: 'age',
// eslint-disable-next-line react/display-name
header: () => 'Age',
size: 50,
enableSorting: true,
},
{
accessorKey: 'visits',
// eslint-disable-next-line react/display-name
header: () => <span>Visits</span>,
size: 50,
enableSorting: true,
Expand Down
Loading

0 comments on commit cdae4af

Please sign in to comment.