Skip to content

Commit 2700c85

Browse files
committed
Add the Notification Banner introduced in NHS Frontend v10 (#283)
1 parent ee8bb81 commit 2700c85

File tree

11 files changed

+661
-0
lines changed

11 files changed

+661
-0
lines changed

docs/upgrade-to-6.0.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,17 @@ The [panel](https://service-manual.nhs.uk/design-system/components/panel) compon
3131

3232
This replaces the [list panel component](#list-panel) which was removed in NHS.UK frontend v6.0.0.
3333

34+
### Notification banner component
35+
36+
The [notification banner](https://service-manual.nhs.uk/design-system/components/notification-banner) component from NHS.UK frontend v10 has been added:
37+
38+
```jsx
39+
<NotificationBanner title="Important Message">
40+
<NotificationBanner.Heading>Upcoming Maintenance</NotificationBanner.Heading>
41+
<p>The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025.</p>
42+
</NotificationBanner>
43+
```
44+
3445
## Breaking changes
3546

3647
### Update the JavaScript supported script snippet

src/__tests__/index.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ describe('Index', () => {
5151
'LedeText',
5252
'Legend',
5353
'NavAZ',
54+
'NotificationBanner',
5455
'Pagination',
5556
'Panel',
5657
'Radios',

src/components/content-presentation/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export * from './hero/index.js';
44
export * from './icons/index.js';
55
export * from './images/index.js';
66
export * from './inset-text/index.js';
7+
export * from './notification-banner/index.js';
78
export * from './panel/index.js';
89
export * from './summary-list/index.js';
910
export * from './table/index.js';
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { type ComponentPropsWithoutRef, forwardRef } from 'react';
2+
import classNames from 'classnames';
3+
import { HeadingLevel } from '#components';
4+
import { NotificationBannerHeading, NotificationBannerLink } from './components/index.js';
5+
6+
export interface NotificationBannerProps extends ComponentPropsWithoutRef<'div'> {
7+
success?: boolean;
8+
}
9+
10+
const NotificationBannerComponent = forwardRef<HTMLDivElement, NotificationBannerProps>(
11+
({ children, className, title, success, ...rest }, forwardedRef) => {
12+
return (
13+
<section
14+
className={classNames(
15+
'nhsuk-notification-banner',
16+
{ 'nhsuk-notification-banner--success': success },
17+
className,
18+
)}
19+
aria-labelledby="nhsuk-notification-banner-title"
20+
data-module="nhsuk-notification-banner"
21+
>
22+
<div className="nhsuk-notification-banner__header">
23+
<HeadingLevel
24+
className="nhsuk-notification-banner__title"
25+
headingLevel={'h2'}
26+
id="nhsuk-notification-banner-title"
27+
>
28+
{title || (success ? 'Success' : 'Important')}
29+
</HeadingLevel>
30+
</div>
31+
<div className={'nhsuk-notification-banner__content'} ref={forwardedRef} {...rest}>
32+
{children}
33+
</div>
34+
</section>
35+
);
36+
},
37+
);
38+
39+
NotificationBannerComponent.displayName = 'NotificationBanner';
40+
41+
export const NotificationBanner = Object.assign(NotificationBannerComponent, {
42+
Heading: NotificationBannerHeading,
43+
Link: NotificationBannerLink,
44+
});
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { renderClient, renderServer } from '#util/components';
2+
import { NotificationBanner } from '#components/content-presentation/notification-banner';
3+
4+
describe('NotificationBanner', () => {
5+
it('matches snapshot', async () => {
6+
const { container } = await renderClient(
7+
<NotificationBanner>
8+
<NotificationBanner.Heading>
9+
The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025.
10+
</NotificationBanner.Heading>
11+
</NotificationBanner>,
12+
{ className: 'nhsuk-notification-banner' },
13+
);
14+
15+
expect(container).toMatchSnapshot();
16+
});
17+
it('matches snapshot success', async () => {
18+
const { container } = await renderClient(
19+
<NotificationBanner success>
20+
<NotificationBanner.Heading>Patient record updated</NotificationBanner.Heading>
21+
</NotificationBanner>,
22+
{ className: 'nhsuk-notification-banner' },
23+
);
24+
25+
expect(container).toMatchSnapshot();
26+
});
27+
it('matches snapshot custom title', async () => {
28+
const { container } = await renderClient(
29+
<NotificationBanner title={'Upcoming Maintenance'}>
30+
<NotificationBanner.Heading>
31+
The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025.
32+
</NotificationBanner.Heading>
33+
</NotificationBanner>,
34+
{ className: 'nhsuk-notification-banner' },
35+
);
36+
37+
expect(container).toMatchSnapshot();
38+
});
39+
it('matches snapshot with link in heading', async () => {
40+
const { container } = await renderClient(
41+
<NotificationBanner>
42+
<NotificationBanner.Heading>
43+
You have 7 days left to send your application.{' '}
44+
<NotificationBanner.Link href={'#'}>View application</NotificationBanner.Link>.
45+
</NotificationBanner.Heading>
46+
</NotificationBanner>,
47+
{ className: 'nhsuk-notification-banner' },
48+
);
49+
50+
expect(container).toMatchSnapshot();
51+
});
52+
it('matches snapshot with link in body', async () => {
53+
const { container } = await renderClient(
54+
<NotificationBanner>
55+
<NotificationBanner.Heading>Patient record updated</NotificationBanner.Heading>
56+
<p>
57+
Contact{' '}
58+
<NotificationBanner.Link href={'#'}>[email protected]</NotificationBanner.Link> if
59+
you think there&#39;s a problem.
60+
</p>
61+
</NotificationBanner>,
62+
{ className: 'nhsuk-notification-banner' },
63+
);
64+
65+
expect(container).toMatchSnapshot();
66+
});
67+
68+
it('matches snapshot (via server)', async () => {
69+
const { container } = await renderServer(
70+
<NotificationBanner>
71+
<NotificationBanner.Heading>
72+
The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025.
73+
</NotificationBanner.Heading>
74+
</NotificationBanner>,
75+
{ className: 'nhsuk-notification-banner' },
76+
);
77+
78+
expect(container).toMatchSnapshot();
79+
});
80+
it('matches snapshot success (via server)', async () => {
81+
const { container } = await renderServer(
82+
<NotificationBanner success>
83+
<NotificationBanner.Heading>Patient record updated</NotificationBanner.Heading>
84+
</NotificationBanner>,
85+
{ className: 'nhsuk-notification-banner' },
86+
);
87+
88+
expect(container).toMatchSnapshot();
89+
});
90+
it('matches snapshot custom title (via server)', async () => {
91+
const { container } = await renderServer(
92+
<NotificationBanner title={'Upcoming Maintenance'}>
93+
<NotificationBanner.Heading>
94+
The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025.
95+
</NotificationBanner.Heading>
96+
</NotificationBanner>,
97+
{ className: 'nhsuk-notification-banner' },
98+
);
99+
100+
expect(container).toMatchSnapshot();
101+
});
102+
it('matches snapshot with link in heading (via server)', async () => {
103+
const { container } = await renderServer(
104+
<NotificationBanner>
105+
<NotificationBanner.Heading>
106+
You have 7 days left to send your application.{' '}
107+
<NotificationBanner.Link href={'#'}>View application</NotificationBanner.Link>.
108+
</NotificationBanner.Heading>
109+
</NotificationBanner>,
110+
{ className: 'nhsuk-notification-banner' },
111+
);
112+
113+
expect(container).toMatchSnapshot();
114+
});
115+
it('matches snapshot with link in body (via server)', async () => {
116+
const { container } = await renderServer(
117+
<NotificationBanner>
118+
<NotificationBanner.Heading>Patient record updated</NotificationBanner.Heading>
119+
<p>
120+
Contact{' '}
121+
<NotificationBanner.Link href={'#'}>[email protected]</NotificationBanner.Link> if
122+
you think there&#39;s a problem.
123+
</p>
124+
</NotificationBanner>,
125+
{ className: 'nhsuk-notification-banner' },
126+
);
127+
128+
expect(container).toMatchSnapshot();
129+
});
130+
});

0 commit comments

Comments
 (0)