import React from 'react';
import { getByText, waitFor } from '@testing-library/dom';
import { fireEvent, render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { axe } from 'jest-axe';
import { consoleOnce } from '@leafygreen-ui/lib';
import { DEFAULT_LGID_ROOT, getLgIds } from '../getLgIds';
import PaginationCurrentPageControls from './Navigation';
const onBackArrowClick = jest.fn();
const onForwardArrowClick = jest.fn();
let offsetParentSpy: jest.SpyInstance;
beforeAll(() => {
offsetParentSpy = jest.spyOn(HTMLElement.prototype, 'offsetParent', 'get');
offsetParentSpy.mockImplementation(function (this: HTMLElement) {
return this.parentElement;
});
});
afterAll(() => {
offsetParentSpy.mockRestore();
});
afterEach(() => {
jest.clearAllMocks();
});
describe('PaginationCurrentPageControls', () => {
describe('a11y', () => {
test('does not have basic accessibility issues', async () => {
const { container } = render(
,
);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
});
describe('page range display', () => {
test('renders "1 of many" when numTotalItems is undefined', () => {
const { getByTestId } = render(
,
);
expect(getByTestId('lg-pagination-page-range').textContent).toBe(
'1 of many',
);
});
test('renders "0 of 0" when numTotalItems is 0', () => {
const { getByTestId } = render(
,
);
expect(getByTestId('lg-pagination-page-range').textContent).toBe(
'0 of 0',
);
});
test('renders correct total pages', () => {
const { getByTestId } = render(
,
);
expect(getByTestId('lg-pagination-page-range').textContent).toBe(
'1 of 103',
);
});
test('renders correct total pages when itemsPerPage changes', () => {
const { getByTestId } = render(
,
);
expect(getByTestId('lg-pagination-page-range').textContent).toBe(
'1 of 21',
);
});
});
describe('page select', () => {
test('page select is not rendered by default', () => {
const { queryByTestId } = render(
,
);
expect(
queryByTestId('lg-pagination-page-select'),
).not.toBeInTheDocument();
});
test('page select is rendered with onCurrentPageOptionChange prop', () => {
const { queryByTestId } = render(
,
);
expect(queryByTestId('lg-pagination-page-select')).toBeInTheDocument();
});
test('falls back to the text branch (no empty dropdown) when numTotalItems is 0', () => {
const { queryByTestId } = render(
,
);
expect(
queryByTestId('lg-pagination-page-select'),
).not.toBeInTheDocument();
expect(queryByTestId('lg-pagination-page-range')?.textContent).toBe(
'0 of 0',
);
});
test('page select options are rendered correctly', async () => {
const { getByTestId, queryByRole } = render(
,
);
const selectButton = getByTestId('lg-pagination-page-select');
userEvent.click(selectButton);
const listbox = await waitFor(() => {
const listbox = queryByRole('listbox');
expect(listbox).toBeVisible();
return listbox as HTMLElement;
});
expect(getByText(listbox, '1')).toBeInTheDocument();
expect(getByText(listbox, '2')).toBeInTheDocument();
expect(getByText(listbox, '3')).toBeInTheDocument();
});
test('calls onCurrentPageOptionChange when page is selected', async () => {
const onCurrentPageOptionChange = jest.fn();
const { getByTestId, queryByRole } = render(
,
);
const selectButton = getByTestId('lg-pagination-page-select');
userEvent.click(selectButton);
const listbox = await waitFor(() => {
const listbox = queryByRole('listbox');
expect(listbox).toBeVisible();
return listbox as HTMLElement;
});
const option2 = getByText(listbox, '2');
userEvent.click(option2);
await waitFor(() => {
expect(onCurrentPageOptionChange).toHaveBeenCalledWith(
'2',
expect.anything(),
);
});
});
});
describe('back button', () => {
test('is disabled on the first page', () => {
const { getByTestId } = render(
,
);
const backButton = getByTestId('lg-pagination-back-button');
expect(backButton.getAttribute('aria-disabled')).toBe('true');
});
test('is not disabled on a middle page', () => {
const { getByTestId } = render(
,
);
const backButton = getByTestId('lg-pagination-back-button');
expect(backButton.getAttribute('aria-disabled')).toBe('false');
});
test('is not disabled on the last page', () => {
const { getByTestId } = render(
,
);
const backButton = getByTestId('lg-pagination-back-button');
expect(backButton.getAttribute('aria-disabled')).toBe('false');
});
test('is disabled when numTotalItems is 0, even when currentPage > 1', () => {
const { getByTestId } = render(
,
);
const backButton = getByTestId('lg-pagination-back-button');
expect(backButton.getAttribute('aria-disabled')).toBe('true');
});
test('calls onBackArrowClick when clicked', () => {
const { getByTestId } = render(
,
);
const backButton = getByTestId('lg-pagination-back-button');
fireEvent.click(backButton);
expect(onBackArrowClick).toHaveBeenCalledTimes(1);
});
});
describe('forward button', () => {
test('is not disabled on the first page', () => {
const { getByTestId } = render(
,
);
const nextButton = getByTestId('lg-pagination-next-button');
expect(nextButton.getAttribute('aria-disabled')).toBe('false');
});
test('is not disabled on a middle page', () => {
const { getByTestId } = render(
,
);
const nextButton = getByTestId('lg-pagination-next-button');
expect(nextButton.getAttribute('aria-disabled')).toBe('false');
});
test('is disabled on the last page', () => {
const { getByTestId } = render(
,
);
const nextButton = getByTestId('lg-pagination-next-button');
expect(nextButton.getAttribute('aria-disabled')).toBe('true');
});
test('is disabled when numTotalItems is 0', () => {
const { getByTestId } = render(
,
);
const nextButton = getByTestId('lg-pagination-next-button');
expect(nextButton.getAttribute('aria-disabled')).toBe('true');
});
test('calls onForwardArrowClick when clicked', () => {
const { getByTestId } = render(
,
);
const nextButton = getByTestId('lg-pagination-next-button');
fireEvent.click(nextButton);
expect(onForwardArrowClick).toHaveBeenCalledTimes(1);
});
});
describe('shouldDisableBackArrow override', () => {
test('true overrides correctly on first page', () => {
const { getByTestId } = render(
,
);
const backButton = getByTestId('lg-pagination-back-button');
expect(backButton.getAttribute('aria-disabled')).toBe('true');
});
test('true overrides correctly on middle page', () => {
const { getByTestId } = render(
,
);
const backButton = getByTestId('lg-pagination-back-button');
expect(backButton.getAttribute('aria-disabled')).toBe('true');
});
test('false overrides correctly on first page', () => {
const { getByTestId } = render(
,
);
const backButton = getByTestId('lg-pagination-back-button');
expect(backButton.getAttribute('aria-disabled')).toBe('false');
});
});
describe('shouldDisableForwardArrow override', () => {
test('true overrides correctly on last page', () => {
const { getByTestId } = render(
,
);
const nextButton = getByTestId('lg-pagination-next-button');
expect(nextButton.getAttribute('aria-disabled')).toBe('true');
});
test('true overrides correctly on middle page', () => {
const { getByTestId } = render(
,
);
const nextButton = getByTestId('lg-pagination-next-button');
expect(nextButton.getAttribute('aria-disabled')).toBe('true');
});
test('false overrides correctly on last page', () => {
const { getByTestId } = render(
,
);
const nextButton = getByTestId('lg-pagination-next-button');
expect(nextButton.getAttribute('aria-disabled')).toBe('false');
});
});
describe('validation', () => {
let consoleOnceSpy: jest.SpyInstance;
beforeEach(() => {
consoleOnceSpy = jest
.spyOn(consoleOnce, 'error')
.mockImplementation(() => {});
});
afterEach(() => {
consoleOnceSpy.mockRestore();
});
test('console errors when currentPage is less than 1', () => {
render(
,
);
expect(consoleOnceSpy).toHaveBeenCalledWith(
`Value of the 'currentPage' prop is invalid.`,
);
});
test('console errors when currentPage exceeds total pages', () => {
render(
,
);
expect(consoleOnceSpy).toHaveBeenCalledWith(
`Value of the 'currentPage' prop is invalid.`,
);
});
test('does not console error for valid currentPage', () => {
render(
,
);
expect(consoleOnceSpy).not.toHaveBeenCalled();
});
});
describe('lgid props', () => {
const defaultLgIds = getLgIds();
test('renders default lgid values when data-lgid is not provided', () => {
const { getByTestId } = render(
,
);
expect(getByTestId(defaultLgIds.navigation)).toBeInTheDocument();
});
test('renders default data-lgid attribute when data-lgid is not provided', () => {
const { getByTestId } = render(
,
);
expect(getByTestId(defaultLgIds.navigation)).toHaveAttribute(
'data-lgid',
`${DEFAULT_LGID_ROOT}-navigation`,
);
});
test('renders custom lgid value when data-lgid is provided', () => {
const customLgId = 'lg-my-pagination';
const customLgIds = getLgIds(customLgId);
const { getByTestId } = render(
,
);
expect(getByTestId(customLgIds.navigation)).toBeInTheDocument();
});
test('renders custom data-lgid attribute when data-lgid is provided', () => {
const customLgId = 'lg-my-pagination';
const customLgIds = getLgIds(customLgId);
const { getByTestId } = render(
,
);
expect(getByTestId(customLgIds.navigation)).toHaveAttribute(
'data-lgid',
`${customLgId}-navigation`,
);
});
});
});