Skip to content

Commit

Permalink
[WNMGDS-2161] Compact pagination (#2373)
Browse files Browse the repository at this point in the history
* Change aria-current to page on Pagination Page

* Add pagination heading for SR, streamline nav buttons and update styles.

* Fix typos

* Update snapshots

* Fix unit tests for Pagination

* Add custom heading tag to Pagination.
  • Loading branch information
zarahzachz authored and pwolfert committed Mar 22, 2023
1 parent 4e96db9 commit 4e796a7
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 67 deletions.
2 changes: 1 addition & 1 deletion packages/design-system/src/components/Pagination/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default function Page({
{isActive ? (
<span
className="ds-c-button ds-c-button--ghost ds-c-pagination__current-page"
aria-current="true"
aria-current="page"
>
{index}
</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ function getNav() {
return screen.getByRole('navigation');
}

function getLabel() {
return screen.getByRole('heading');
}

function getPrevLink() {
return screen.getByRole('link', { name: 'Previous Page' });
}
Expand Down Expand Up @@ -54,11 +58,12 @@ describe('Pagination', () => {
describe('accessibility attributes', () => {
it('should have navigation label', () => {
renderPagination({ totalPages: 8 });
expect(getNav().getAttribute('aria-label')).toEqual('Pagination');
expect(getLabel().textContent).toContain('Pagination');
expect(getLabel().textContent).toContain('8');
});
it('should set a custom navigation label', () => {
renderPagination({ totalPages: 8, ariaLabel: 'Pagey page page' });
expect(getNav().getAttribute('aria-label')).toEqual('Pagey page page');
expect(getLabel().textContent).toContain('Pagey page page');
});
});

Expand Down Expand Up @@ -127,19 +132,21 @@ describe('Pagination', () => {
expect(queryPrevLink()).toBeTruthy();
});

it('should hide "previous" navigation slot if current page is first page of set', () => {
it('should render disabled "previous" navigation slot if current page is first page of set', () => {
renderPagination({ currentPage: 1 });
expect(queryPrevLink()).toBeFalsy();
expect(queryPrevLink()).toHaveAttribute('aria-disabled');
expect(queryPrevLink()).not.toHaveAttribute('href');
});

it('should show "next" navigation slot if current page is not last page of set', () => {
renderPagination({ currentPage: 2 });
expect(queryNextLink()).toBeTruthy();
});

it('should hide "next" navigation slot if current page is last page of set', () => {
it('should render disabled "next" navigation slot if current page is last page of set', () => {
renderPagination({ currentPage: 3 });
expect(queryNextLink()).toBeFalsy();
expect(queryNextLink()).toHaveAttribute('aria-disabled');
expect(queryNextLink()).not.toHaveAttribute('href');
});
});

Expand Down
104 changes: 53 additions & 51 deletions packages/design-system/src/components/Pagination/Pagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import classNames from 'classnames';
import { ArrowIcon } from '../Icons';
import { t } from '../i18n';

export type PaginationHeadingLevel = '1' | '2' | '3' | '4' | '5' | '6';
export interface PaginationProps {
/**
* Defines `aria-label` on wrapping Pagination element. Since this exists on a `<nav>` element, the word "navigation" should be omitted from this label. Optional.
Expand All @@ -24,6 +25,10 @@ export interface PaginationProps {
* Defines active page in Pagination.
*/
currentPage: number;
/**
* Heading type to override default `<h2>`.
*/
headingLevel?: PaginationHeadingLevel;
/**
* Determines if navigation is hidden when current page is the first or last of Pagination page set. Optional.
*/
Expand Down Expand Up @@ -130,6 +135,7 @@ function Pagination({
currentPage,
renderHref,
onPageChange,
headingLevel,
isNavigationHidden,
startLabelText,
startAriaLabel,
Expand Down Expand Up @@ -223,34 +229,38 @@ function Pagination({
const startIcon = <ArrowIcon direction="left" className="ds-c-pagination__nav--image" />;
const endIcon = <ArrowIcon direction="right" className="ds-c-pagination__nav--image" />;

const Heading = `h${headingLevel}` as const;
const headingElement = (
<Heading id="pagination-heading">
{ariaLabel ?? t('pagination.ariaLabel')} -{' '}
{t('pagination.pageXOfY', {
number: `${currentPage}`,
total: `${totalPages}`,
})}
</Heading>
);

return (
<nav className={classes} aria-label={ariaLabel ?? t('pagination.ariaLabel')} {...rest}>
{currentPage === 1 ? (
<span
className="ds-c-pagination__nav ds-c-pagination__nav--disabled"
aria-disabled="true"
style={{ visibility: isNavigationHidden ? 'hidden' : 'visible' }}
aria-hidden={isNavigationHidden}
>
<span className="ds-c-pagination__nav--img-container ds-c-pagination__nav--img-container-previous">
{startIcon}
</span>
{startLabelText ?? t('pagination.startLabelText')}
<nav className={classes} aria-labelledby="pagination-heading" {...rest}>
<span aria-live="polite" role="status" className="ds-u-visibility--screen-reader">
{headingElement}
</span>

<Button
variation="ghost"
href={renderHref(currentPage - 1)}
onClick={pageChange(currentPage - 1)}
aria-label={startAriaLabel ?? t('pagination.startAriaLabel')}
className="ds-c-pagination__nav"
disabled={currentPage === 1}
style={{ visibility: currentPage === 1 && isNavigationHidden ? 'hidden' : 'visible' }}
aria-hidden={currentPage === 1 ? isNavigationHidden : false}
>
<span className="ds-c-pagination__nav--img-container ds-c-pagination__nav--img-container-previous">
{startIcon}
</span>
) : (
<Button
variation="ghost"
href={renderHref(currentPage - 1)}
onClick={pageChange(currentPage - 1)}
aria-label={startAriaLabel ?? t('pagination.startAriaLabel')}
className="ds-c-pagination__nav"
>
<span className="ds-c-pagination__nav--img-container ds-c-pagination__nav--img-container-previous">
{startIcon}
</span>
{startLabelText ?? t('pagination.startLabelText')}
</Button>
)}
{startLabelText ?? t('pagination.startLabelText')}
</Button>

<span
className="ds-c-pagination__page-count"
Expand All @@ -264,38 +274,30 @@ function Pagination({

<ul role="list">{pages}</ul>

{currentPage === totalPages ? (
<span
className="ds-c-pagination__nav ds-c-pagination__nav--disabled"
style={{ visibility: isNavigationHidden ? 'hidden' : 'visible' }}
aria-hidden={isNavigationHidden}
aria-disabled="true"
>
{endLabelText ?? t('pagination.endLabelText')}
<span className="ds-c-pagination__nav--img-container ds-c-pagination__nav--img-container-next">
{endIcon}
</span>
<Button
variation="ghost"
href={renderHref(currentPage + 1)}
onClick={pageChange(currentPage + 1)}
aria-label={endAriaLabel ?? t('pagination.endAriaLabel')}
className="ds-c-pagination__nav"
disabled={currentPage === totalPages}
style={{
visibility: currentPage === totalPages && isNavigationHidden ? 'hidden' : 'visible',
}}
aria-hidden={currentPage === totalPages ? isNavigationHidden : false}
>
{endLabelText ?? t('pagination.endLabelText')}
<span className="ds-c-pagination__nav--img-container ds-c-pagination__nav--img-container-next">
{endIcon}
</span>
) : (
<Button
variation="ghost"
href={renderHref(currentPage + 1)}
onClick={pageChange(currentPage + 1)}
aria-label={endAriaLabel ?? t('pagination.endAriaLabel')}
className="ds-c-pagination__nav"
>
{endLabelText ?? t('pagination.endLabelText')}
<span className="ds-c-pagination__nav--img-container ds-c-pagination__nav--img-container-next">
{endIcon}
</span>
</Button>
)}
</Button>
</nav>
);
}

Pagination.defaultProps = {
compact: false,
headingLevel: '2',
isNavigationHidden: false,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ exports[`Page should render static el if current 1`] = `
<div>
<li>
<span
aria-current="true"
aria-current="page"
class="ds-c-button ds-c-button--ghost ds-c-pagination__current-page"
>
1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ exports[`Pagination pagination slot behavior less than 7 pages should show all p
Array [
<li>
<span
aria-current="true"
aria-current="page"
class="ds-c-button ds-c-button--ghost ds-c-pagination__current-page"
>
1
Expand Down Expand Up @@ -52,13 +52,26 @@ Array [
exports[`Pagination should render component 1`] = `
<DocumentFragment>
<nav
aria-label="Pagination"
aria-labelledby="pagination-heading"
class="ds-c-pagination"
>
<span
aria-live="polite"
class="ds-u-visibility--screen-reader"
role="status"
>
<h2
id="pagination-heading"
>
Pagination results - Page 2 of 3
</h2>
</span>
<a
aria-hidden="false"
aria-label="Previous Page"
class="ds-c-button ds-c-button--ghost ds-c-pagination__nav"
href="#1"
style="visibility: visible;"
>
<span
class="ds-c-pagination__nav--img-container ds-c-pagination__nav--img-container-previous"
Expand Down Expand Up @@ -106,7 +119,7 @@ exports[`Pagination should render component 1`] = `
</li>
<li>
<span
aria-current="true"
aria-current="page"
class="ds-c-button ds-c-button--ghost ds-c-pagination__current-page"
>
2
Expand All @@ -123,9 +136,11 @@ exports[`Pagination should render component 1`] = `
</li>
</ul>
<a
aria-hidden="false"
aria-label="Next Page"
class="ds-c-button ds-c-button--ghost ds-c-pagination__nav"
href="#3"
style="visibility: visible;"
>
Next
<span
Expand Down Expand Up @@ -157,10 +172,23 @@ exports[`Pagination with compact prop enabled should render compact variant 1`]
aria-labelledby="pagination-heading"
class="ds-c-pagination ds-c-pagination--compact"
>
<span
aria-live="polite"
class="ds-u-visibility--screen-reader"
role="status"
>
<h2
id="pagination-heading"
>
Pagination results - Page 2 of 3
</h2>
</span>
<a
aria-hidden="false"
aria-label="Previous Page"
class="ds-c-button ds-c-button--ghost ds-c-pagination__nav"
href="#1"
style="visibility: visible;"
>
<span
class="ds-c-pagination__nav--img-container ds-c-pagination__nav--img-container-previous"
Expand Down Expand Up @@ -198,9 +226,11 @@ exports[`Pagination with compact prop enabled should render compact variant 1`]
role="list"
/>
<a
aria-hidden="false"
aria-label="Next Page"
class="ds-c-button ds-c-button--ghost ds-c-pagination__nav"
href="#3"
style="visibility: visible;"
>
Next
<span
Expand Down
2 changes: 1 addition & 1 deletion packages/design-system/src/components/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
"clearAllText": "Clear all"
},
"pagination": {
"ariaLabel": "Pagination",
"ariaLabel": "Pagination results",
"pageXOfY": "Page {{number}} of {{total}}",
"startLabelText": "Previous",
"startAriaLabel": "Previous Page",
Expand Down
2 changes: 1 addition & 1 deletion packages/design-system/src/components/locale/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
"clearAllText": "Borrar todo"
},
"pagination": {
"ariaLabel": "Paginación",
"ariaLabel": "Resultados de paginación",
"pageXOfY": "Página {{number}} de {{total}}",
"startLabelText": "Anterior",
"startAriaLabel": "Página anterior",
Expand Down
5 changes: 2 additions & 3 deletions packages/design-system/src/styles/components/_Pagination.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
}
li {
display: inline-block;
list-style: none;
}
.ds-c-button {
color: var(--pagination-link__color);
Expand Down Expand Up @@ -96,7 +95,7 @@
border: 0;
padding: $spacer-half $spacer-2;

&:not(.ds-c-pagination__nav--disabled) {
&:not([aria-disabled='true']) {
color: LinkText;
text-decoration: underline;
text-decoration-thickness: 3px;
Expand All @@ -117,7 +116,7 @@
}
}
}
.ds-c-pagination__nav--disabled {
[aria-disabled='true'] {
border: 1px solid transparent;
color: var(--pagination-link__color--disabled);
line-height: 1.3;
Expand Down

0 comments on commit 4e796a7

Please sign in to comment.