From bce1039dbbe2809976b61e98623521ab2923819c Mon Sep 17 00:00:00 2001 From: Matyas Forian-Szabo Date: Mon, 16 Jun 2025 14:25:37 +0200 Subject: [PATCH] feat(ui-pagination): add prop to customize screenreader label on buttons The new prop, called screenReaderLabelPageButton allows to customize screenreader messages on the page buttons (e.g. 1,2,3,4...) INSTUI-4602 --- .../src/Pagination/PaginationButton/index.tsx | 3 +++ .../src/Pagination/PaginationButton/props.ts | 13 +++++++++--- .../ui-pagination/src/Pagination/README.md | 14 ++++++++++--- .../Pagination/__tests__/Pagination.test.tsx | 21 +++++++++++++++++++ .../ui-pagination/src/Pagination/index.tsx | 8 +++++++ .../ui-pagination/src/Pagination/props.ts | 16 ++++++++++++-- 6 files changed, 67 insertions(+), 8 deletions(-) diff --git a/packages/ui-pagination/src/Pagination/PaginationButton/index.tsx b/packages/ui-pagination/src/Pagination/PaginationButton/index.tsx index 728c3ae11d..28432e380c 100644 --- a/packages/ui-pagination/src/Pagination/PaginationButton/index.tsx +++ b/packages/ui-pagination/src/Pagination/PaginationButton/index.tsx @@ -69,6 +69,9 @@ class PaginationButton extends Component { {...props} aria-current={this.props.current ? 'page' : undefined} elementRef={this.handleRef} + {...(this.props.screenReaderLabel + ? { 'aria-label': this.props.screenReaderLabel } + : {})} > {this.props.children} diff --git a/packages/ui-pagination/src/Pagination/PaginationButton/props.ts b/packages/ui-pagination/src/Pagination/PaginationButton/props.ts index 5bd02d6086..3b8860a3ef 100644 --- a/packages/ui-pagination/src/Pagination/PaginationButton/props.ts +++ b/packages/ui-pagination/src/Pagination/PaginationButton/props.ts @@ -50,6 +50,12 @@ type PaginationPageOwnProps = { | React.MouseEvent | React.FocusEvent ) => void + /** + * The text screenreaders should say when this button is in focus (sets the + * `aria-label` attribute). + * If left undefined (default) SRs will announce text in the child node(s). + */ + screenReaderLabel?: string } type PropKeys = keyof PaginationPageOwnProps @@ -65,13 +71,14 @@ type PaginationPageProps = const propTypes: PropValidators = { children: PropTypes.node.isRequired, current: PropTypes.bool, - onClick: PropTypes.func + onClick: PropTypes.func, + screenReaderLabel: PropTypes.string } const allowedProps: AllowedPropKeys = [ 'children', - 'current' - + 'current', + 'screenReaderLabel' // we don't want to pass onClick // 'onClick' ] diff --git a/packages/ui-pagination/src/Pagination/README.md b/packages/ui-pagination/src/Pagination/README.md index 10ab9d9b98..a9e15c8610 100644 --- a/packages/ui-pagination/src/Pagination/README.md +++ b/packages/ui-pagination/src/Pagination/README.md @@ -63,9 +63,10 @@ The pagination component provides props to handle most of the pagination use-cas render() ``` -You can set any `totalPageNumber`, the component can handle it easily. Furthermore, you can set -`siblingCount`, which indicates how many pages are visible on either side of the `currentPage` and the -`boundaryCount`, which indicates how many pages are visible in the beginning and end. +You can set any `totalPageNumber`, the component can handle it easily.\ +Furthermore, you can set `siblingCount`, which indicates how many pages are visible on either side of the `currentPage` and the +`boundaryCount`, which indicates how many pages are visible in the beginning and end.\ +Also, you can set `screenReaderLabelPageButton` to customize what a screenreader will announce when the button receives focus. - ```js class Example extends React.Component { @@ -87,6 +88,9 @@ You can set any `totalPageNumber`, the component can handle it easily. Furthermo onPageChange={(nextPage) => this.setState({ currentPage: nextPage })} siblingCount={3} boundaryCount={2} + screenReaderLabelPageButton={(currentPage, totalPageNumber) => + `Page ${currentPage} of ${totalPageNumber}` + } /> ) } @@ -110,6 +114,9 @@ You can set any `totalPageNumber`, the component can handle it easily. Furthermo onPageChange={(nextPage) => setCurrentPage(nextPage)} siblingCount={3} boundaryCount={2} + screenReaderLabelPageButton={(currentPage, totalPageNumber) => + `Page ${currentPage} of ${totalPageNumber}` + } /> ) } @@ -531,6 +538,7 @@ type: embed
Ensure page links and the next/previous buttons are labeled correctly for screen readers + Use `screenReaderLabelPageButton` or `screenReaderLabelNumberInput` for better screenreader experience
``` diff --git a/packages/ui-pagination/src/Pagination/__tests__/Pagination.test.tsx b/packages/ui-pagination/src/Pagination/__tests__/Pagination.test.tsx index b5041ed15b..100ae3d8d4 100644 --- a/packages/ui-pagination/src/Pagination/__tests__/Pagination.test.tsx +++ b/packages/ui-pagination/src/Pagination/__tests__/Pagination.test.tsx @@ -1128,5 +1128,26 @@ describe('', () => { ) expect(container.firstChild).toHaveTextContent('12345678910Next Page') }) + + it('should add aria-label when screenReaderLabelPageButton is set', async () => { + render( + + `Page ${currentPage} of ${totalPageNumber}` + } + /> + ) + const paginationButtons = screen.getAllByRole('button', { name: /\d$/ }) + + for (let i: number = 0; i < paginationButtons.length; i++) { + expect(paginationButtons[i]).toHaveAttribute( + 'aria-label', + `Page ${i + 1} of ${paginationButtons.length}` + ) + } + }) }) }) diff --git a/packages/ui-pagination/src/Pagination/index.tsx b/packages/ui-pagination/src/Pagination/index.tsx index ef7459c1ff..53f3c22ed5 100644 --- a/packages/ui-pagination/src/Pagination/index.tsx +++ b/packages/ui-pagination/src/Pagination/index.tsx @@ -332,6 +332,14 @@ class Pagination extends Component { key={i} onClick={() => this.handleNavigation(i, currentPage)} current={i === currentPage} + {...(this.props.screenReaderLabelPageButton + ? { + screenReaderLabel: this.props.screenReaderLabelPageButton( + i, + this.props.totalPageNumber! + ) + } + : {})} > {this.props.renderPageIndicator?.(i, currentPage)} diff --git a/packages/ui-pagination/src/Pagination/props.ts b/packages/ui-pagination/src/Pagination/props.ts index 88fde5bee1..bc3c274b40 100644 --- a/packages/ui-pagination/src/Pagination/props.ts +++ b/packages/ui-pagination/src/Pagination/props.ts @@ -97,7 +97,7 @@ type PaginationOwnProps = { * * (__only__ for `input` variant) */ - labelNumberInput?: (numberOfPages: number) => React.ReactNode + labelNumberInput?: (totalPageNumber: number) => React.ReactNode /** * ScreenReaderLabel for number input @@ -106,7 +106,17 @@ type PaginationOwnProps = { */ screenReaderLabelNumberInput?: ( currentPage: number, - numberOfPages: number + totalPageNumber: number + ) => string + + /** + * ScreenReaderLabel for page number buttons + * + * (__only__ for `full` and `compact variants) + */ + screenReaderLabelPageButton?: ( + currentPage: number, + totalPageNumber: number ) => string /** @@ -212,6 +222,7 @@ const propTypes: PropValidators = { labelLast: PropTypes.string, labelNumberInput: PropTypes.func, screenReaderLabelNumberInput: PropTypes.func, + screenReaderLabelPageButton: PropTypes.func, variant: PropTypes.oneOf(['full', 'compact', 'input']), margin: PropTypes.string, as: PropTypes.elementType, @@ -239,6 +250,7 @@ const allowedProps: AllowedPropKeys = [ 'labelLast', 'labelNumberInput', 'screenReaderLabelNumberInput', + 'screenReaderLabelPageButton', 'variant', 'margin', 'as',