Skip to content

Commit ad8131a

Browse files
Add support for numbered pagination (#291)
* Align pagination with NHS.UK frontend * Split out pagination child components * Add support for numbered pagination * Add to upgrade guide
1 parent 97f19db commit ad8131a

File tree

11 files changed

+1383
-106
lines changed

11 files changed

+1383
-106
lines changed

docs/upgrade-to-6.0.md

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,20 @@ The updated [header](https://service-manual.nhs.uk/design-system/components/head
2222

2323
You can now use smaller versions of the [radios](https://service-manual.nhs.uk/design-system/components/radios) and [checkboxes](https://service-manual.nhs.uk/design-system/components/checkboxes) components by adding the `small` prop.
2424

25-
### Panel component
25+
### Numbered pagination component
2626

27-
The [panel](https://service-manual.nhs.uk/design-system/components/panel) component from NHS.UK frontend v9.3.0 has been added:
27+
The [pagination](https://service-manual.nhs.uk/design-system/components/notification-banner) component from NHS.UK frontend v10.1 has been updated to support numbered pagination:
2828

2929
```jsx
30-
<Panel>
31-
<Panel.Title>Booking complete</Panel.Title>
32-
We have sent you a confirmation email
33-
</Panel>
30+
<Pagination>
31+
<Pagination.Link href="/results?page=1" previous />
32+
<Pagination.Item href="/results?page=1" number={1} />
33+
<Pagination.Item href="/results?page=2" number={2} current />
34+
<Pagination.Item href="/results?page=3" number={3} />
35+
<Pagination.Link href="/results?page=3" next />
36+
</Pagination>
3437
```
3538

36-
This replaces the [list panel component](#list-panel) which was removed in NHS.UK frontend v6.0.0.
37-
3839
### Notification banner component
3940

4041
The [notification banner](https://service-manual.nhs.uk/design-system/components/notification-banner) component from NHS.UK frontend v10 has been added:
@@ -46,23 +47,32 @@ The [notification banner](https://service-manual.nhs.uk/design-system/components
4647
</NotificationBanner>
4748
```
4849

50+
### Panel component
51+
52+
The [panel](https://service-manual.nhs.uk/design-system/components/panel) component from NHS.UK frontend v9.3.0 has been added:
53+
54+
```jsx
55+
<Panel>
56+
<Panel.Title>Booking complete</Panel.Title>
57+
We have sent you a confirmation email
58+
</Panel>
59+
```
60+
61+
This replaces the [list panel component](#list-panel) which was removed in NHS.UK frontend v6.0.0.
62+
4963
### Support for React Server Components (RSC)
5064

5165
All components have been tested as React Server Components (RSC) but due to [multipart namespace component limitations](https://ivicabatinic.from.hr/posts/multipart-namespace-components-addressing-rsc-and-dot-notation-issues) an alternative syntax (without dot notation) can be used as a workaround:
5266

5367
```patch
54-
<Pagination>
55-
- <Pagination.Link href="/section/treatments" previous>
56-
+ <PaginationLink href="/section/treatments" previous>
57-
Treatments
58-
- </Pagination.Link>
59-
+ </PaginationLink>
60-
- <Pagination.Link href="/section/symptoms" next>
61-
+ <PaginationLink href="/section/symptoms" next>
62-
Symptoms
63-
- </Pagination.Link>
64-
+ </PaginationLink>
65-
</Pagination>
68+
<Breadcrumb>
69+
- <Breadcrumb.Item href="#">Home</Breadcrumb.Item>
70+
- <Breadcrumb.Item href="#">NHS services</Breadcrumb.Item>
71+
- <Breadcrumb.Item href="#">Hospitals</Breadcrumb.Item>
72+
+ <BreadcrumbItem href="#">Home</BreadcrumbItem>
73+
+ <BreadcrumbItem href="#">NHS services</BreadcrumbItem>
74+
+ <BreadcrumbItem href="#">Hospitals</BreadcrumbItem>
75+
</Breadcrumb>
6676
```
6777

6878
## Breaking changes
@@ -441,6 +451,26 @@ To align with NHS.UK frontend, the error summary component is automatically aler
441451
</ErrorSummary>
442452
```
443453

454+
### Pagination
455+
456+
To align with NHS.UK frontend, the pagination link component automatically renders its own "Previous page" or "Next page" text, with "page" being visually hidden. You will need to make the following changes:
457+
458+
- rename the `Pagination.Link` component to `Pagination.Item`
459+
- move text content (or the `children` prop) to the `labelText` prop
460+
461+
```patch
462+
<Pagination>
463+
- <Pagination.Link href="/section/treatments" previous>
464+
- Treatments
465+
- </Pagination.Link>
466+
- <Pagination.Link href="/section/symptoms" next>
467+
- Symptoms
468+
- </Pagination.Link>
469+
+ <Pagination.Item labelText="Treatments" href="/section/treatments" previous />
470+
+ <Pagination.Item labelText="Symptoms" href="/section/symptoms" next />
471+
</Pagination>
472+
```
473+
444474
### Select
445475

446476
You must rename the `Select` prop `selectRef` to `ref` for consistency with other components:

src/__tests__/index.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ describe('Index', () => {
9696
'NotificationBannerLink',
9797
'NotificationBannerTitle',
9898
'Pagination',
99+
'PaginationItem',
99100
'PaginationLink',
101+
'PaginationLinkText',
100102
'Panel',
101103
'PanelTitle',
102104
'Radios',
Lines changed: 38 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,54 @@
11
import classNames from 'classnames';
2-
import { forwardRef, type ComponentPropsWithoutRef } from 'react';
3-
import { ArrowLeftIcon, ArrowRightIcon } from '#components/content-presentation/index.js';
4-
import { type AsElementLink } from '#util/types/LinkTypes.js';
2+
import { Children, forwardRef, type ComponentPropsWithoutRef } from 'react';
3+
import { PaginationItem, PaginationLink } from './components/index.js';
4+
import { childIsOfComponentType } from '#util/types/TypeGuards.js';
55

6-
export interface PaginationLinkProps extends AsElementLink<HTMLAnchorElement> {
7-
previous?: boolean;
8-
next?: boolean;
9-
}
6+
export type PaginationProps = ComponentPropsWithoutRef<'nav'>;
7+
8+
const PaginationComponent = forwardRef<HTMLElement, PaginationProps>(
9+
({ className, children, 'aria-label': ariaLabel = 'Pagination', ...rest }, forwardedRef) => {
10+
const items = Children.toArray(children);
11+
12+
// Filter previous and next links
13+
const links = items.filter((child) => childIsOfComponentType(child, PaginationLink));
14+
const linkPrevious = links.find(({ props }) => props.previous);
15+
const linkNext = links.find(({ props }) => props.next);
16+
17+
// Filter numbered list items
18+
const listItems = items.filter((child) => childIsOfComponentType(child, PaginationItem));
19+
const listItemsNumbered = listItems.filter(({ props }) => props.number || props.ellipsis);
1020

11-
export const PaginationLink = forwardRef<HTMLAnchorElement, PaginationLinkProps>(
12-
({ className, children, asElement: Element = 'a', previous, next, ...rest }, forwardedRef) => (
13-
<li
14-
className={classNames(
15-
{ 'nhsuk-pagination-item--previous': previous },
16-
{ 'nhsuk-pagination-item--next': next },
17-
)}
18-
>
19-
<Element
21+
return (
22+
<nav
2023
className={classNames(
21-
'nhsuk-pagination__link',
22-
{ 'nhsuk-pagination__link--prev': previous },
23-
{ 'nhsuk-pagination__link--next': next },
24+
'nhsuk-pagination',
25+
{ 'nhsuk-pagination--numbered': listItemsNumbered.length },
2426
className,
2527
)}
28+
role="navigation"
29+
aria-label={ariaLabel}
2630
ref={forwardedRef}
2731
{...rest}
2832
>
29-
<span className="nhsuk-pagination__title">
30-
{previous ? 'Previous' : null}
31-
{next ? 'Next' : null}
32-
</span>
33-
<span className="nhsuk-u-visually-hidden">:</span>
34-
<span className="nhsuk-pagination__page">{children}</span>
35-
{previous ? <ArrowLeftIcon /> : null}
36-
{next ? <ArrowRightIcon /> : null}
37-
</Element>
38-
</li>
39-
),
40-
);
41-
42-
export type PaginationProps = ComponentPropsWithoutRef<'nav'>;
43-
44-
const PaginationComponent = forwardRef<HTMLElement, PaginationProps>(
45-
({ className, children, 'aria-label': ariaLabel = 'Pagination', ...rest }, forwardedRef) => (
46-
<nav
47-
className={classNames('nhsuk-pagination', className)}
48-
role="navigation"
49-
aria-label={ariaLabel}
50-
ref={forwardedRef}
51-
{...rest}
52-
>
53-
<ul className="nhsuk-list nhsuk-pagination__list">{children}</ul>
54-
</nav>
55-
),
33+
{linkPrevious}
34+
<ul
35+
className={
36+
listItemsNumbered.length
37+
? 'nhsuk-pagination__list' // Standard pagination list class
38+
: 'nhsuk-list nhsuk-pagination__list' // Legacy pagination list class
39+
}
40+
>
41+
{listItems}
42+
</ul>
43+
{linkNext}
44+
</nav>
45+
);
46+
},
5647
);
5748

5849
PaginationComponent.displayName = 'Pagination';
59-
PaginationLink.displayName = 'Pagination.Link';
6050

6151
export const Pagination = Object.assign(PaginationComponent, {
52+
Item: PaginationItem,
6253
Link: PaginationLink,
6354
});

0 commit comments

Comments
 (0)