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`, ); }); }); });