From afa689e442ff9b651b9dc8685aa3d4a060d8d1f8 Mon Sep 17 00:00:00 2001 From: Avinash Dwarapu Date: Tue, 2 Jun 2026 15:32:30 +0200 Subject: [PATCH 1/2] fix: Prevent focus from being lost when pagination previous/next buttons are disabled --- pages/pagination/simple.page.tsx | 52 ++++++++++++++++++++++++++++++++ src/pagination/internal.tsx | 6 +++- 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 pages/pagination/simple.page.tsx diff --git a/pages/pagination/simple.page.tsx b/pages/pagination/simple.page.tsx new file mode 100644 index 0000000000..68a9a460ba --- /dev/null +++ b/pages/pagination/simple.page.tsx @@ -0,0 +1,52 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React, { useState } from 'react'; + +import I18nProvider from '~components/i18n'; +import messages from '~components/i18n/messages/all.en'; +import Pagination, { PaginationProps } from '~components/pagination'; + +import ScreenshotArea from '../utils/screenshot-area'; + +const paginationLabels: PaginationProps.Labels = { + nextPageLabel: 'Next page', + previousPageLabel: 'Previous page', + pageLabel: pageNumber => `Page ${pageNumber} of all pages`, + jumpToPageButton: 'Go to page', +}; + +const i18nStrings: PaginationProps.I18nStrings = { + jumpToPageInputLabel: 'Page number', + jumpToPageError: 'Enter a valid page number', + jumpToPageLoadingText: 'Loading page', +}; + +export default function PaginationSimplePage() { + const [basicPageIndex, setBasicPageIndex] = useState(1); + const [jumpPageIndex, setJumpPageIndex] = useState(1); + + return ( + +

Pagination simple

+ +

Basic pagination with 20 pages

+ setBasicPageIndex(event.detail.currentPageIndex)} + /> + +

Basic pagination with jump to page

+ setJumpPageIndex(event.detail.currentPageIndex)} + /> +
+
+ ); +} diff --git a/src/pagination/internal.tsx b/src/pagination/internal.tsx index 62f07af47c..38fc5341e6 100644 --- a/src/pagination/internal.tsx +++ b/src/pagination/internal.tsx @@ -47,6 +47,9 @@ function PageButton({ ...rest }: PageButtonProps) { function handleClick(event: React.MouseEvent) { + if (disabled) { + return; + } event.preventDefault(); onClick(pageIndex); } @@ -61,7 +64,8 @@ function PageButton({ )} type="button" aria-label={ariaLabel} - disabled={disabled} + aria-disabled={disabled} + tabIndex={disabled ? -1 : 0} onClick={handleClick} aria-current={isCurrent} {...(disabled From 38ede85b38d48c15125be1f0b7b747bd6332c858 Mon Sep 17 00:00:00 2001 From: Avinash Dwarapu Date: Thu, 4 Jun 2026 11:02:23 +0200 Subject: [PATCH 2/2] Update unit tests. --- src/pagination/__tests__/pagination.test.tsx | 20 +++++++++---------- src/pagination/internal.tsx | 2 +- .../__tests__/hooks-integration.test.tsx | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/pagination/__tests__/pagination.test.tsx b/src/pagination/__tests__/pagination.test.tsx index f4ee7f7910..692a764b39 100644 --- a/src/pagination/__tests__/pagination.test.tsx +++ b/src/pagination/__tests__/pagination.test.tsx @@ -47,24 +47,24 @@ test('should re-render component correctly after current page state change', () test('should have both arrows disabled when there is only one page', () => { const { wrapper } = renderPagination(); - expect(wrapper.findPreviousPageButton().getElement()).toBeDisabled(); - expect(wrapper.findNextPageButton().getElement()).toBeDisabled(); + expect(wrapper.findPreviousPageButton().getElement()).toHaveAttribute('aria-disabled', 'true'); + expect(wrapper.findNextPageButton().getElement()).toHaveAttribute('aria-disabled', 'true'); expect(wrapper.findPageNumberByIndex(1)!.getElement()).toHaveTextContent('1'); expect(getItemsContent(wrapper)).toEqual(['1']); }); test('should have both arrows disabled when there are no pages', () => { const { wrapper } = renderPagination(); - expect(wrapper.findPreviousPageButton().getElement()).toBeDisabled(); - expect(wrapper.findNextPageButton().getElement()).toBeDisabled(); + expect(wrapper.findPreviousPageButton().getElement()).toHaveAttribute('aria-disabled', 'true'); + expect(wrapper.findNextPageButton().getElement()).toHaveAttribute('aria-disabled', 'true'); expect(wrapper.findPageNumberByIndex(1)!.getElement()).toHaveTextContent('1'); expect(getItemsContent(wrapper)).toEqual(['1']); }); test('should show all buttons when middle page selected', () => { const { wrapper } = renderPagination(); - expect(wrapper.findPreviousPageButton().getElement()).toBeEnabled(); - expect(wrapper.findNextPageButton().getElement()).toBeEnabled(); + expect(wrapper.findPreviousPageButton().getElement()).not.toHaveAttribute('aria-disabled'); + expect(wrapper.findNextPageButton().getElement()).not.toHaveAttribute('aria-disabled'); expect(wrapper.findCurrentPage().getElement()).toHaveTextContent('5'); expect(getItemsContent(wrapper)).toEqual(['1', '2', '3', '4', '5', '6', '7', '8', '9']); }); @@ -86,8 +86,8 @@ test('should not fire nextPageClick event when clicking next page with the last test('should disable `previous` button when first page selected', () => { const { wrapper } = renderPagination(); - expect(wrapper.findPreviousPageButton().getElement()).toBeDisabled(); - expect(wrapper.findNextPageButton().getElement()).toBeEnabled(); + expect(wrapper.findPreviousPageButton().getElement()).toHaveAttribute('aria-disabled', 'true'); + expect(wrapper.findNextPageButton().getElement()).not.toHaveAttribute('aria-disabled'); expect(wrapper.findCurrentPage().getElement()).toHaveTextContent('1'); }); @@ -189,8 +189,8 @@ test('should not fire nextPageClick event when clicking next page with the last ); expect(wrapper.isDisabled()).toBe(true); - expect(wrapper.findPreviousPageButton().getElement()).toBeDisabled(); - expect(wrapper.findNextPageButton().getElement()).toBeDisabled(); + expect(wrapper.findPreviousPageButton().getElement()).toHaveAttribute('aria-disabled', 'true'); + expect(wrapper.findNextPageButton().getElement()).toHaveAttribute('aria-disabled', 'true'); wrapper.findPreviousPageButton().click(); expect(onChange).not.toHaveBeenCalled(); diff --git a/src/pagination/internal.tsx b/src/pagination/internal.tsx index 38fc5341e6..c7e0ad2274 100644 --- a/src/pagination/internal.tsx +++ b/src/pagination/internal.tsx @@ -64,7 +64,7 @@ function PageButton({ )} type="button" aria-label={ariaLabel} - aria-disabled={disabled} + aria-disabled={disabled ? true : undefined} tabIndex={disabled ? -1 : 0} onClick={handleClick} aria-current={isCurrent} diff --git a/src/table/__tests__/hooks-integration.test.tsx b/src/table/__tests__/hooks-integration.test.tsx index 0c53fad866..adadb08839 100644 --- a/src/table/__tests__/hooks-integration.test.tsx +++ b/src/table/__tests__/hooks-integration.test.tsx @@ -82,10 +82,10 @@ test('should apply filtering and display no-match state', () => { test('should navigate through pagination', () => { const { tableWrapper, paginationWrapper } = renderDemo(); expect(tableWrapper.findBodyCell(1, 1)!.getElement().textContent).toEqual('1'); - expect(paginationWrapper.findPreviousPageButton().getElement()).toBeDisabled(); + expect(paginationWrapper.findPreviousPageButton().getElement()).toHaveAttribute('aria-disabled', 'true'); paginationWrapper.findNextPageButton().click(); expect(tableWrapper.findBodyCell(1, 1)!.getElement().textContent).toEqual('11'); - expect(paginationWrapper.findPreviousPageButton().getElement()).toBeEnabled(); + expect(paginationWrapper.findPreviousPageButton().getElement()).not.toHaveAttribute('aria-disabled'); paginationWrapper.findPageNumberByIndex(4)!.click(); expect(tableWrapper.findBodyCell(1, 1)!.getElement().textContent).toEqual('31'); }); @@ -95,7 +95,7 @@ test('pagination should work when filtering is applied', () => { expect(tableWrapper.findBodyCell(1, 1)!.getElement().textContent).toEqual('1'); expect(paginationWrapper.findPageNumbers()).toHaveLength(2); paginationWrapper.findNextPageButton().click(); - expect(paginationWrapper.findNextPageButton().getElement()).toBeDisabled(); + expect(paginationWrapper.findNextPageButton().getElement()).toHaveAttribute('aria-disabled', 'true'); expect(tableWrapper.findBodyCell(1, 1)!.getElement().textContent).toEqual('19'); });