diff --git a/bundlesize.config.json b/bundlesize.config.json index 8d83809581..1013168aeb 100644 --- a/bundlesize.config.json +++ b/bundlesize.config.json @@ -30,7 +30,7 @@ }, { "path": "packages/vue-instantsearch/vue3/umd/index.js", - "maxSize": "63.25 kB" + "maxSize": "63.5 kB" }, { "path": "packages/vue-instantsearch/vue2/cjs/index.js", diff --git a/packages/instantsearch.js/src/components/Pagination/Pagination.tsx b/packages/instantsearch.js/src/components/Pagination/Pagination.tsx index e6e14db5c4..94c1fa4bfd 100644 --- a/packages/instantsearch.js/src/components/Pagination/Pagination.tsx +++ b/packages/instantsearch.js/src/components/Pagination/Pagination.tsx @@ -158,8 +158,8 @@ function PaginationLink({
  • @@ -168,6 +168,7 @@ function PaginationLink({ rootTagName="span" rootProps={{ className: cssClasses.link, + 'aria-label': ariaLabel, }} templateKey={templateKey} templates={templates} diff --git a/packages/instantsearch.js/src/components/Pagination/__tests__/Pagination-test.tsx b/packages/instantsearch.js/src/components/Pagination/__tests__/Pagination-test.tsx deleted file mode 100644 index edadd914e5..0000000000 --- a/packages/instantsearch.js/src/components/Pagination/__tests__/Pagination-test.tsx +++ /dev/null @@ -1,146 +0,0 @@ -/** - * @jest-environment jsdom - */ -/** @jsx h */ - -import { render, fireEvent, createEvent } from '@testing-library/preact'; -import { h } from 'preact'; - -import Paginator from '../../../connectors/pagination/Paginator'; -import Pagination from '../Pagination'; - -import type { PaginationProps } from '../Pagination'; - -describe('Pagination', () => { - const pager = new Paginator({ - currentPage: 0, - total: 20, - padding: 3, - }); - const defaultProps: PaginationProps = { - cssClasses: { - root: 'root', - noRefinementRoot: 'noRefinementRoot', - list: 'list', - item: 'item', - firstPageItem: 'firstPageItem', - lastPageItem: 'lastPageItem', - previousPageItem: 'previousPageItem', - nextPageItem: 'nextPageItem', - pageItem: 'pageItem', - selectedItem: 'selectedItem', - disabledItem: 'disabledItem', - link: 'link', - }, - createURL: (args) => JSON.stringify(args), - templates: { - first: '', - last: '', - next: '', - page: ({ page }) => `${page}`, - previous: '', - }, - currentPage: 0, - pages: pager.pages(), - isFirstPage: pager.isFirstPage(), - isLastPage: pager.isLastPage(), - nbPages: 20, - setCurrentPage: () => {}, - }; - - it('should render five elements', () => { - const { container } = render(); - - expect(container).toMatchSnapshot(); - }); - - it('should display the first/last link', () => { - const { container } = render( - - ); - - expect(container.querySelectorAll('.firstPageItem')).toHaveLength(1); - expect(container.querySelectorAll('.lastPageItem')).toHaveLength(1); - expect(container).toMatchSnapshot(); - }); - - it('should add the noRefinement CSS class with a single page', () => { - const { container } = render(); - - expect(container.querySelectorAll('.noRefinementRoot')).toHaveLength(1); - expect(container).toMatchSnapshot(); - }); - - it('should disable last page if already on it', () => { - const { container } = render( - - ); - - expect(container.querySelectorAll('.lastPageItem')).toHaveLength(1); - expect( - container - .querySelector('.lastPageItem') - ?.classList.contains('disabledItem') - ).toBe(true); - expect(container).toMatchSnapshot(); - }); - - it('should handle special clicks', () => { - const props = { - setCurrentPage: jest.fn(), - }; - - const { container } = render(); - - const firstItem = container.querySelector('.link')!; - - const modifiers = ['ctrlKey', 'shiftKey', 'altKey', 'metaKey'] as const; - modifiers.forEach((modifier) => { - const clickEvent = createEvent.click(firstItem, { [modifier]: true }); - fireEvent(firstItem, clickEvent); - - expect(props.setCurrentPage).toHaveBeenCalledTimes(0); - expect(clickEvent.defaultPrevented).toBe(false); - }); - - const clickEvent = createEvent.click(firstItem); - fireEvent(firstItem, clickEvent); - - expect(props.setCurrentPage).toHaveBeenCalledTimes(1); - expect(clickEvent.defaultPrevented).toBe(true); - }); - - it('should have all buttons disabled if there are no results', () => { - const localPager = new Paginator({ - currentPage: 0, - total: 0, - padding: 3, - }); - const { container } = render( - - ); - - expect(container).toMatchSnapshot(); - }); -}); diff --git a/packages/instantsearch.js/src/components/Pagination/__tests__/__snapshots__/Pagination-test.tsx.snap b/packages/instantsearch.js/src/components/Pagination/__tests__/__snapshots__/Pagination-test.tsx.snap deleted file mode 100644 index d89a31a68b..0000000000 --- a/packages/instantsearch.js/src/components/Pagination/__tests__/__snapshots__/Pagination-test.tsx.snap +++ /dev/null @@ -1,461 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Pagination should add the noRefinement CSS class with a single page 1`] = ` -
    -
    - -
    -
    -`; - -exports[`Pagination should disable last page if already on it 1`] = ` -
    - -
    -`; - -exports[`Pagination should display the first/last link 1`] = ` -
    -
    - -
    -
    -`; - -exports[`Pagination should have all buttons disabled if there are no results 1`] = ` - -`; - -exports[`Pagination should render five elements 1`] = ` -
    -
    - -
    -
    -`; diff --git a/packages/instantsearch.js/src/widgets/pagination/__tests__/__snapshots__/pagination-test.ts.snap b/packages/instantsearch.js/src/widgets/pagination/__tests__/__snapshots__/pagination-test.ts.snap deleted file mode 100644 index 1a020a7d06..0000000000 --- a/packages/instantsearch.js/src/widgets/pagination/__tests__/__snapshots__/pagination-test.ts.snap +++ /dev/null @@ -1,91 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`pagination() calls twice render(, container) 1`] = ` -{ - "createURL": [Function], - "cssClasses": { - "disabledItem": "ais-Pagination-item--disabled disabledItem", - "firstPageItem": "ais-Pagination-item--firstPage firstPageItem", - "item": "ais-Pagination-item item", - "lastPageItem": "ais-Pagination-item--lastPage lastPageItem", - "link": "ais-Pagination-link link", - "list": "ais-Pagination-list list", - "nextPageItem": "ais-Pagination-item--nextPage nextPageItem", - "noRefinementRoot": "ais-Pagination--noRefinement noRefinementRoot", - "pageItem": "ais-Pagination-item--page pageItem", - "previousPageItem": "ais-Pagination-item--previousPage previousPageItem", - "root": "ais-Pagination root customRoot", - "selectedItem": "ais-Pagination-item--selected selectedItem", - }, - "currentPage": 0, - "isFirstPage": true, - "isLastPage": false, - "nbPages": 20, - "pages": [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - ], - "setCurrentPage": [Function], - "showFirst": true, - "showLast": true, - "showNext": true, - "showPrevious": true, - "templates": { - "first": [Function], - "last": [Function], - "next": [Function], - "page": [Function], - "previous": [Function], - }, -} -`; - -exports[`pagination() calls twice render(, container) 2`] = ` -{ - "createURL": [Function], - "cssClasses": { - "disabledItem": "ais-Pagination-item--disabled disabledItem", - "firstPageItem": "ais-Pagination-item--firstPage firstPageItem", - "item": "ais-Pagination-item item", - "lastPageItem": "ais-Pagination-item--lastPage lastPageItem", - "link": "ais-Pagination-link link", - "list": "ais-Pagination-list list", - "nextPageItem": "ais-Pagination-item--nextPage nextPageItem", - "noRefinementRoot": "ais-Pagination--noRefinement noRefinementRoot", - "pageItem": "ais-Pagination-item--page pageItem", - "previousPageItem": "ais-Pagination-item--previousPage previousPageItem", - "root": "ais-Pagination root customRoot", - "selectedItem": "ais-Pagination-item--selected selectedItem", - }, - "currentPage": 0, - "isFirstPage": true, - "isLastPage": false, - "nbPages": 20, - "pages": [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - ], - "setCurrentPage": [Function], - "showFirst": true, - "showLast": true, - "showNext": true, - "showPrevious": true, - "templates": { - "first": [Function], - "last": [Function], - "next": [Function], - "page": [Function], - "previous": [Function], - }, -} -`; diff --git a/packages/instantsearch.js/src/widgets/pagination/__tests__/pagination-test.ts b/packages/instantsearch.js/src/widgets/pagination/__tests__/pagination-test.ts deleted file mode 100644 index b09da7728a..0000000000 --- a/packages/instantsearch.js/src/widgets/pagination/__tests__/pagination-test.ts +++ /dev/null @@ -1,258 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import { - createSingleSearchResponse, - createSearchClient, -} from '@instantsearch/mocks'; -import { castToJestMock } from '@instantsearch/testutils/castToJestMock'; -import algoliasearchHelper, { - SearchResults, - SearchParameters, -} from 'algoliasearch-helper'; -import { render as preactRender } from 'preact'; - -import { - createInitOptions, - createRenderOptions, -} from '../../../../test/createWidget'; -import { getContainerNode as utilsGetContainerNode } from '../../../lib/utils/getContainerNode'; -import pagination from '../pagination'; - -import type { - PaginationCSSClasses, - PaginationWidgetParams, -} from '../pagination'; -import type { AlgoliaSearchHelper } from 'algoliasearch-helper'; - -const render = castToJestMock(preactRender); -jest.mock('preact', () => { - const module = jest.requireActual('preact'); - - module.render = jest.fn(); - - return module; -}); - -const getContainerNode = castToJestMock(utilsGetContainerNode); -jest.mock('../../../lib/utils/getContainerNode', () => { - const module = jest.requireActual('../../../lib/utils/getContainerNode'); - - const _getContainerNode = module.getContainerNode; - module.getContainerNode = jest.fn((...args) => _getContainerNode(...args)); - - return module; -}); - -describe('Usage', () => { - it('throws without container', () => { - expect(() => { - // @ts-expect-error - pagination({ container: undefined }); - }).toThrowErrorMatchingInlineSnapshot(` -"The \`container\` option is required. - -See documentation: https://www.algolia.com/doc/api-reference/widgets/pagination/js/" -`); - }); -}); - -describe('pagination()', () => { - let widget: ReturnType; - let container: HTMLElement; - let helper: AlgoliaSearchHelper; - let results: SearchResults; - let cssClasses: PaginationCSSClasses; - - beforeEach(() => { - render.mockClear(); - getContainerNode.mockClear(); - - container = document.createElement('div'); - cssClasses = { - root: ['root', 'customRoot'], - noRefinementRoot: 'noRefinementRoot', - list: 'list', - item: 'item', - firstPageItem: 'firstPageItem', - lastPageItem: 'lastPageItem', - previousPageItem: 'previousPageItem', - nextPageItem: 'nextPageItem', - pageItem: 'pageItem', - selectedItem: 'selectedItem', - disabledItem: 'disabledItem', - link: 'link', - }; - widget = pagination({ container, scrollTo: false, cssClasses }); - - helper = { - setPage: jest.fn(), - search: jest.fn(), - state: new SearchParameters(), - } as unknown as AlgoliaSearchHelper; - results = new SearchResults(helper.state, [ - createSingleSearchResponse({ - hits: [{ first: 'hit', second: 'hit', objectID: '1' }], - nbHits: 200, - hitsPerPage: 10, - nbPages: 20, - }), - ]); - widget.init!(createInitOptions({ helper })); - }); - - it('sets the page', () => { - widget.getWidgetRenderState(createInitOptions({ helper })).refine(42); - expect(helper.setPage).toHaveBeenCalledTimes(1); - expect(helper.search).toHaveBeenCalledTimes(1); - }); - - it('calls twice render(, container)', () => { - const { state } = algoliasearchHelper(createSearchClient(), '', { - page: 0, - }); - - widget.render!( - createRenderOptions({ - results, - helper, - state, - }) - ); - widget.render!( - createRenderOptions({ - results, - helper, - state, - }) - ); - - const [firstRender, secondRender] = render.mock.calls; - - expect(render).toHaveBeenCalledTimes(2); - // @ts-expect-error - expect(firstRender[0].props).toMatchSnapshot(); - expect(firstRender[1]).toEqual(container); - // @ts-expect-error - expect(secondRender[0].props).toMatchSnapshot(); - expect(secondRender[1]).toEqual(container); - }); - - describe('mocking getContainerNode', () => { - let scrollIntoView: jest.Mock; - - beforeEach(() => { - scrollIntoView = jest.fn(); - }); - - it('should not scroll', () => { - widget = pagination({ container, scrollTo: false }); - widget.init!(createInitOptions({ helper })); - widget.getWidgetRenderState(createInitOptions({ helper })).refine(2); - expect(scrollIntoView).toHaveBeenCalledTimes(0); - }); - - it('should scrollto body', () => { - const { state } = algoliasearchHelper(createSearchClient(), '', { - page: 0, - }); - - // @ts-expect-error - getContainerNode.mockImplementation((input) => - input === 'body' ? { scrollIntoView } : input - ); - - widget = pagination({ container }); - - widget.init!(createInitOptions({ helper })); - widget.render!( - createRenderOptions({ - results, - helper, - state, - }) - ); - - const [firstRender] = render.mock.calls; - - // @ts-expect-error - firstRender[0].props.setCurrentPage(2); - - expect(scrollIntoView).toHaveBeenCalledTimes(1); - }); - }); -}); - -describe('pagination MaxPage', () => { - let widget: ReturnType; - let container: HTMLElement; - let results: SearchResults; - let cssClasses: PaginationCSSClasses; - let paginationOptions: PaginationWidgetParams; - - beforeEach(() => { - container = document.createElement('div'); - cssClasses = { - root: 'root', - noRefinementRoot: 'noRefinementRoot', - list: 'list', - item: 'item', - firstPageItem: 'firstPageItem', - lastPageItem: 'lastPageItem', - previousPageItem: 'previousPageItem', - nextPageItem: 'nextPageItem', - pageItem: 'pageItem', - selectedItem: 'selectedItem', - disabledItem: 'disabledItem', - link: 'link', - }; - results = new SearchResults(new SearchParameters(), [ - createSingleSearchResponse({ - hits: [{ objectID: 'lol' }], - nbHits: 300, - hitsPerPage: 10, - nbPages: 30, - }), - ]); - paginationOptions = { container, scrollTo: false, cssClasses }; - }); - - it('does to have any default', () => { - widget = pagination(paginationOptions); - - expect( - widget.getWidgetRenderState( - createRenderOptions({ - results, - }) - ).nbPages - ).toEqual(30); - }); - - it('does reduce the number of pages if lower than nbPages', () => { - paginationOptions.totalPages = 20; - widget = pagination(paginationOptions); - - expect( - widget.getWidgetRenderState( - createRenderOptions({ - results, - }) - ).nbPages - ).toEqual(20); - }); - - it('does not reduce the number of pages if greater than nbPages', () => { - paginationOptions.totalPages = 40; - widget = pagination(paginationOptions); - - expect( - widget.getWidgetRenderState( - createRenderOptions({ - results, - }) - ).nbPages - ).toEqual(30); - }); -}); diff --git a/packages/instantsearch.js/src/widgets/pagination/__tests__/pagination.test.tsx b/packages/instantsearch.js/src/widgets/pagination/__tests__/pagination.test.tsx index 5ccc094b91..2b7743e19f 100644 --- a/packages/instantsearch.js/src/widgets/pagination/__tests__/pagination.test.tsx +++ b/packages/instantsearch.js/src/widgets/pagination/__tests__/pagination.test.tsx @@ -8,6 +8,7 @@ import { createSingleSearchResponse, } from '@instantsearch/mocks'; import { wait } from '@instantsearch/testutils/wait'; +import { findByLabelText, findByRole } from '@testing-library/dom'; import instantsearch from '../../../index.es'; import configure from '../../configure/configure'; @@ -20,6 +21,111 @@ beforeEach(() => { }); describe('pagination', () => { + describe('options', () => { + test('throws without a `container`', () => { + expect(() => { + const searchClient = createSearchClient(); + + const search = instantsearch({ + indexName: 'indexName', + searchClient, + }); + + search.addWidgets([ + pagination({ + // @ts-expect-error + container: undefined, + }), + ]); + }).toThrowErrorMatchingInlineSnapshot(` +"The \`container\` option is required. + +See documentation: https://www.algolia.com/doc/api-reference/widgets/pagination/js/" +`); + }); + + test('add custom CSS classes', async () => { + const container = document.createElement('div'); + const searchClient = createSearchClient(); + + const search = instantsearch({ + indexName: 'indexName', + searchClient, + }); + + search.addWidgets([ + pagination({ + container, + cssClasses: { + root: 'ROOT', + noRefinementRoot: 'NO_REFINEMENT_ROOT', + list: 'LIST', + item: 'ITEM', + firstPageItem: 'FIRST_PAGE_ITEM', + lastPageItem: 'LAST_PAGE_ITEM', + previousPageItem: 'PREVIOUS_PAGE_ITEM', + nextPageItem: 'NEXT_PAGE_ITEM', + pageItem: 'PAGE_ITEM', + selectedItem: 'SELECTED_ITEM', + disabledItem: 'DISABLED_ITEM', + link: 'LINK', + }, + }), + ]); + + search.start(); + + await wait(0); + + const root = container.firstChild; + + const [ + list, + firstPageItem, + previousPageItem, + nextPageItem, + lastPageItem, + pageLink, + ] = await Promise.all([ + findByRole(container, 'list'), + (await findByLabelText(container, 'First')).parentNode, + (await findByLabelText(container, 'Previous')).parentNode, + (await findByLabelText(container, 'Next')).parentNode, + (await findByLabelText(container, 'Last')).parentNode, + findByRole(container, 'link', { name: 'Page 1' }), + ]); + + expect(root).toHaveClass('ROOT', 'NO_REFINEMENT_ROOT'); + expect(list).toHaveClass('LIST'); + expect(firstPageItem).toHaveClass( + 'FIRST_PAGE_ITEM', + 'ITEM', + 'DISABLED_ITEM' + ); + expect(previousPageItem).toHaveClass( + 'PREVIOUS_PAGE_ITEM', + 'ITEM', + 'DISABLED_ITEM' + ); + expect(nextPageItem).toHaveClass( + 'NEXT_PAGE_ITEM', + 'ITEM', + 'DISABLED_ITEM' + ); + expect(lastPageItem).toHaveClass( + 'LAST_PAGE_ITEM', + 'ITEM', + 'DISABLED_ITEM' + ); + expect(pageLink.parentNode).toHaveClass( + 'ITEM', + 'PAGE_ITEM', + 'SELECTED_ITEM' + ); + expect(pageLink).toHaveClass('LINK'); + }); + }); + describe('templates', () => { test('does not warn with default templates', async () => { const container = document.createElement('div'); diff --git a/packages/react-instantsearch/src/widgets/__tests__/Pagination.test.tsx b/packages/react-instantsearch/src/widgets/__tests__/Pagination.test.tsx index 5a10467aba..259b09ecf8 100644 --- a/packages/react-instantsearch/src/widgets/__tests__/Pagination.test.tsx +++ b/packages/react-instantsearch/src/widgets/__tests__/Pagination.test.tsx @@ -39,1975 +39,6 @@ function createMockedSearchClient({ nbPages }: { nbPages?: number } = {}) { } describe('Pagination', () => { - test('renders with default props', async () => { - const { container } = render( - - - - ); - - await waitFor(() => - expect(container.querySelectorAll('.ais-Pagination-item').length).toEqual( - 5 - ) - ); - - expect(container).toMatchInlineSnapshot(` -
    -
    -
      -
    • - - ‹‹ - -
    • -
    • - - ‹ - -
    • -
    • - - 1 - -
    • -
    • - - › - -
    • -
    • - - ›› - -
    • -
    -
    -
    - `); - }); - - test('renders with props', async () => { - const searchClient = createMockedSearchClient(); - const { container } = render( - - - - ); - - await waitFor(() => - expect( - document.querySelectorAll('.ais-Pagination-item--page') - ).toHaveLength(7) - ); - - expect( - document.querySelectorAll('.ais-Pagination-item--page')[0] - ).toHaveClass('ais-Pagination-item--selected'); - expect( - document.querySelector('.ais-Pagination-item--firstPage') - ).toHaveClass('ais-Pagination-item--disabled'); - expect( - document.querySelector('.ais-Pagination-item--previousPage') - ).toHaveClass('ais-Pagination-item--disabled'); - expect( - document.querySelector('.ais-Pagination-item--nextPage') - ).not.toHaveClass('ais-Pagination-item--disabled'); - expect( - document.querySelector('.ais-Pagination-item--lastPage') - ).not.toHaveClass('ais-Pagination-item--disabled'); - expect(container).toMatchInlineSnapshot(` -
    -
    - -
    -
    - `); - }); - - test('navigates between pages', async () => { - const searchClient = createMockedSearchClient(); - const { container, getByText } = render( - - - - ); - - await waitFor(() => - expect( - document.querySelectorAll('.ais-Pagination-item--page') - ).toHaveLength(7) - ); - - expect( - document.querySelector('.ais-Pagination-item--selected') - ).toHaveTextContent('1'); - expect(container).toMatchInlineSnapshot(` -
    -
    - -
    -
    - `); - - const firstPageItem = document.querySelector( - '.ais-Pagination-item--firstPage' - ); - const previousPageItem = document.querySelector( - '.ais-Pagination-item--previousPage' - ); - const nextPageItem = document.querySelector( - '.ais-Pagination-item--nextPage' - ); - const lastPageItem = document.querySelector( - '.ais-Pagination-item--lastPage' - ); - - searchClient.search.mockClear(); - - // We're on page 1, "First" and "Previous" links are disabled - expect(firstPageItem).toHaveClass('ais-Pagination-item--disabled'); - expect(previousPageItem).toHaveClass('ais-Pagination-item--disabled'); - - userEvent.click( - firstPageItem!.querySelector('.ais-Pagination-link') as HTMLAnchorElement - ); - userEvent.click( - previousPageItem!.querySelector( - '.ais-Pagination-link' - ) as HTMLAnchorElement - ); - - await waitFor(() => expect(searchClient.search).not.toHaveBeenCalled()); - - // We navigate to page 2 - userEvent.click(getByText('2')); - - await waitFor(() => { - expect(searchClient.search).toHaveBeenLastCalledWith([ - expect.objectContaining({ - params: expect.objectContaining({ page: 1 }), - }), - ]); - expect( - document.querySelector('.ais-Pagination-item--selected') - ).toHaveTextContent('2'); - }); - - expect(container).toMatchInlineSnapshot(` -
    -
    - -
    -
    - `); - - // We click on "Next" link - userEvent.click(getByText('›')); - - await waitFor(() => { - expect(searchClient.search).toHaveBeenLastCalledWith([ - expect.objectContaining({ - params: expect.objectContaining({ page: 2 }), - }), - ]); - expect( - document.querySelector('.ais-Pagination-item--selected') - ).toHaveTextContent('3'); - }); - - expect(container).toMatchInlineSnapshot(` -
    -
    - -
    -
    - `); - - // We click on "Last" link - userEvent.click(getByText('››')); - - await waitFor(() => { - expect(searchClient.search).toHaveBeenLastCalledWith([ - expect.objectContaining({ - params: expect.objectContaining({ page: 49 }), - }), - ]); - expect( - document.querySelector('.ais-Pagination-item--selected') - ).toHaveTextContent('50'); - }); - - expect(container).toMatchInlineSnapshot(` -
    -
    - -
    -
    - `); - - searchClient.search.mockClear(); - - // We're on last page, "Next" and "Last" links are disabled - expect(nextPageItem).toHaveClass('ais-Pagination-item--disabled'); - expect(lastPageItem).toHaveClass('ais-Pagination-item--disabled'); - - userEvent.click( - nextPageItem!.querySelector('.ais-Pagination-link') as HTMLAnchorElement - ); - userEvent.click( - lastPageItem!.querySelector('.ais-Pagination-link') as HTMLAnchorElement - ); - - await waitFor(() => expect(searchClient.search).not.toHaveBeenCalled()); - - // We click on "Previous" link - userEvent.click( - previousPageItem!.querySelector( - '.ais-Pagination-link' - ) as HTMLAnchorElement - ); - - await waitFor(() => { - expect(searchClient.search).toHaveBeenLastCalledWith([ - expect.objectContaining({ - params: expect.objectContaining({ page: 48 }), - }), - ]); - expect( - document.querySelector('.ais-Pagination-item--selected') - ).toHaveTextContent('49'); - }); - - expect(container).toMatchInlineSnapshot(` -
    - -
    - `); - - // We click on "First" link - userEvent.click( - firstPageItem!.querySelector('.ais-Pagination-link') as HTMLAnchorElement - ); - - await waitFor(() => { - expect(searchClient.search).toHaveBeenLastCalledWith([ - expect.objectContaining({ - params: expect.objectContaining({ page: 0 }), - }), - ]); - expect( - document.querySelector('.ais-Pagination-item--selected') - ).toHaveTextContent('1'); - }); - - expect(container).toMatchInlineSnapshot(` -
    -
    - -
    -
    - `); - }); - - test('does not navigate when pressing a modifier key', async () => { - const searchClient = createMockedSearchClient(); - const { getByText } = render( - - - - ); - - await waitFor(() => expect(searchClient.search).toHaveBeenCalledTimes(1)); - - searchClient.search.mockClear(); - - const firstPageItem = document.querySelector( - '.ais-Pagination-item--firstPage' - ); - const firstPageLink = firstPageItem!.querySelector('.ais-Pagination-link'); - - userEvent.click(firstPageLink as HTMLAnchorElement, { button: 1 }); - userEvent.click(firstPageLink as HTMLAnchorElement, { altKey: true }); - userEvent.click(firstPageLink as HTMLAnchorElement, { ctrlKey: true }); - userEvent.click(firstPageLink as HTMLAnchorElement, { metaKey: true }); - userEvent.click(firstPageLink as HTMLAnchorElement, { shiftKey: true }); - - const previousPageItem = document.querySelector( - '.ais-Pagination-item--previousPage' - ); - const previousPageLink = previousPageItem!.querySelector( - '.ais-Pagination-link' - ); - - userEvent.click(previousPageLink as HTMLAnchorElement, { button: 1 }); - userEvent.click(previousPageLink as HTMLAnchorElement, { altKey: true }); - userEvent.click(previousPageLink as HTMLAnchorElement, { ctrlKey: true }); - userEvent.click(previousPageLink as HTMLAnchorElement, { metaKey: true }); - userEvent.click(previousPageLink as HTMLAnchorElement, { shiftKey: true }); - - const nextPageItem = document.querySelector( - '.ais-Pagination-item--nextPage' - ); - const nextPageLink = nextPageItem!.querySelector('.ais-Pagination-link'); - - userEvent.click(nextPageLink as HTMLAnchorElement, { button: 1 }); - userEvent.click(nextPageLink as HTMLAnchorElement, { altKey: true }); - userEvent.click(nextPageLink as HTMLAnchorElement, { ctrlKey: true }); - userEvent.click(nextPageLink as HTMLAnchorElement, { metaKey: true }); - userEvent.click(nextPageLink as HTMLAnchorElement, { shiftKey: true }); - - const lastPageItem = document.querySelector( - '.ais-Pagination-item--lastPage' - ); - const lastPageLink = lastPageItem!.querySelector('.ais-Pagination-link'); - - userEvent.click(lastPageLink as HTMLAnchorElement, { button: 1 }); - userEvent.click(lastPageLink as HTMLAnchorElement, { altKey: true }); - userEvent.click(lastPageLink as HTMLAnchorElement, { ctrlKey: true }); - userEvent.click(lastPageLink as HTMLAnchorElement, { metaKey: true }); - userEvent.click(lastPageLink as HTMLAnchorElement, { shiftKey: true }); - - const pageOneLink = getByText('1'); - - userEvent.click(pageOneLink as HTMLAnchorElement, { button: 1 }); - userEvent.click(pageOneLink as HTMLAnchorElement, { altKey: true }); - userEvent.click(pageOneLink as HTMLAnchorElement, { ctrlKey: true }); - userEvent.click(pageOneLink as HTMLAnchorElement, { metaKey: true }); - userEvent.click(pageOneLink as HTMLAnchorElement, { shiftKey: true }); - - expect(searchClient.search).not.toHaveBeenCalled(); - }); - - test('adds items around the current one', async () => { - const searchClient = createMockedSearchClient(); - const { container } = render( - - - - ); - - await waitFor(() => - expect( - document.querySelectorAll('.ais-Pagination-item--page') - ).toHaveLength(9) - ); - - expect(container).toMatchInlineSnapshot(` -
    -
    - -
    -
    - `); - }); - - test('does not add items around the current one when there are not enough pages', async () => { - const client = createAlgoliaSearchClient({ - search: jest.fn((requests: Parameters[0]) => - Promise.resolve( - createMultiSearchResponse( - ...requests.map((request) => - createSingleSearchResponse({ - hits: Array.from({ length: 120 }).map((_, index) => ({ - objectID: String(index), - })), - index: request.indexName, - }) - ) - ) - ) - ) as MockSearchClient['search'], - }); - - const { container } = render( - - - - ); - - await waitFor(() => - expect( - document.querySelectorAll('.ais-Pagination-item--page') - ).toHaveLength(6) - ); - - expect(container).toMatchInlineSnapshot(` -
    -
    - -
    -
    - `); - }); - - test('limits the total pages to display', async () => { - const searchClient = createMockedSearchClient(); - const { container } = render( - - - - ); - - await waitFor(() => - expect( - document.querySelectorAll('.ais-Pagination-item--page') - ).toHaveLength(4) - ); - - expect(container).toMatchInlineSnapshot(` -
    -
    - -
    -
    - `); - }); - - test('hides the "First" item when `showFirst` is `false`', async () => { - const { container } = render( - - - - ); - - await waitFor(() => - expect( - Array.from(container.querySelectorAll('.ais-Pagination-item')).map( - (item) => item.textContent - ) - ).toEqual(['‹', '1', '›', '››']) - ); - - expect( - document.querySelector('.ais-Pagination-item--firstPage') - ).toBeNull(); - expect(container).toMatchInlineSnapshot(` -
    -
    -
      -
    • - - ‹ - -
    • -
    • - - 1 - -
    • -
    • - - › - -
    • -
    • - - ›› - -
    • -
    -
    -
    - `); - }); - - test('hides the "Previous" item when `showPrevious` is `false`', async () => { - const { container } = render( - - - - ); - - await waitFor(() => - expect( - Array.from(container.querySelectorAll('.ais-Pagination-item')).map( - (item) => item.textContent - ) - ).toEqual(['‹‹', '1', '›', '››']) - ); - - expect( - document.querySelector('.ais-Pagination-item--previousPage') - ).toBeNull(); - expect(container).toMatchInlineSnapshot(` -
    -
    -
      -
    • - - ‹‹ - -
    • -
    • - - 1 - -
    • -
    • - - › - -
    • -
    • - - ›› - -
    • -
    -
    -
    - `); - }); - - test('hides the "Next" item when `showNext` is `false`', async () => { - const { container } = render( - - - - ); - - await waitFor(() => - expect( - Array.from(container.querySelectorAll('.ais-Pagination-item')).map( - (item) => item.textContent - ) - ).toEqual(['‹‹', '‹', '1', '››']) - ); - - expect(document.querySelector('.ais-Pagination-item--nextPage')).toBeNull(); - expect(container).toMatchInlineSnapshot(` -
    -
    -
      -
    • - - ‹‹ - -
    • -
    • - - ‹ - -
    • -
    • - - 1 - -
    • -
    • - - ›› - -
    • -
    -
    -
    - `); - }); - - test('hides the "Last" item when `showLast` is `false`', async () => { - const { container } = render( - - - - ); - - await waitFor(() => - expect( - Array.from(container.querySelectorAll('.ais-Pagination-item')).map( - (item) => item.textContent - ) - ).toEqual(['‹‹', '‹', '1', '›']) - ); - - expect(document.querySelector('.ais-Pagination-item--lastPage')).toBeNull(); - expect(container).toMatchInlineSnapshot(` -
    -
    -
      -
    • - - ‹‹ - -
    • -
    • - - ‹ - -
    • -
    • - - 1 - -
    • -
    • - - › - -
    • -
    -
    -
    - `); - }); - test('forwards custom class names and `div` props to the root element', () => { const { container } = render( diff --git a/packages/vue-instantsearch/src/components/Pagination.vue b/packages/vue-instantsearch/src/components/Pagination.vue index a1982e4cb8..b4420b5aab 100644 --- a/packages/vue-instantsearch/src/components/Pagination.vue +++ b/packages/vue-instantsearch/src/components/Pagination.vue @@ -1,5 +1,8 @@