diff --git a/docs/upgrade-to-6.0.md b/docs/upgrade-to-6.0.md index 5cc03627..c4d16572 100644 --- a/docs/upgrade-to-6.0.md +++ b/docs/upgrade-to-6.0.md @@ -31,6 +31,17 @@ The [panel](https://service-manual.nhs.uk/design-system/components/panel) compon This replaces the [list panel component](#list-panel) which was removed in NHS.UK frontend v6.0.0. +### Notification banner component + +The [notification banner](https://service-manual.nhs.uk/design-system/components/notification-banner) component from NHS.UK frontend v10 has been added: + +```jsx + + Upcoming maintenance +

The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025.

+
+``` + ### Support for React Server Components (RSC) 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: @@ -440,8 +451,9 @@ You must rename the `Select` prop `selectRef` to `ref` for consistency with othe To align with NHS.UK frontend, the skip link component focuses the main content rather than the first heading on the page: ```html -
+
+
``` For accessibility reasons, you must make the following changes: diff --git a/src/__tests__/index.test.ts b/src/__tests__/index.test.ts index ebc22f7d..5eab14f0 100644 --- a/src/__tests__/index.test.ts +++ b/src/__tests__/index.test.ts @@ -91,6 +91,10 @@ describe('Index', () => { 'NavAZ', 'NavAZDisabledItem', 'NavAZLinkItem', + 'NotificationBanner', + 'NotificationBannerHeading', + 'NotificationBannerLink', + 'NotificationBannerTitle', 'Pagination', 'PaginationLink', 'Panel', diff --git a/src/components/content-presentation/index.ts b/src/components/content-presentation/index.ts index 55a38c07..e410fcc9 100644 --- a/src/components/content-presentation/index.ts +++ b/src/components/content-presentation/index.ts @@ -4,6 +4,7 @@ export * from './hero/index.js'; export * from './icons/index.js'; export * from './images/index.js'; export * from './inset-text/index.js'; +export * from './notification-banner/index.js'; export * from './panel/index.js'; export * from './summary-list/index.js'; export * from './table/index.js'; diff --git a/src/components/content-presentation/notification-banner/NotificationBanner.tsx b/src/components/content-presentation/notification-banner/NotificationBanner.tsx new file mode 100644 index 00000000..33eb003f --- /dev/null +++ b/src/components/content-presentation/notification-banner/NotificationBanner.tsx @@ -0,0 +1,95 @@ +'use client'; + +import { + Children, + createRef, + forwardRef, + useEffect, + useState, + type ComponentPropsWithoutRef, +} from 'react'; +import classNames from 'classnames'; +import { + NotificationBannerHeading, + NotificationBannerLink, + NotificationBannerTitle, +} from './components/index.js'; +import { type NotificationBanner as NotificationBannerModule } from 'nhsuk-frontend'; +import { childIsOfComponentType } from '#util/types/TypeGuards.js'; + +export interface NotificationBannerProps extends ComponentPropsWithoutRef<'div'> { + success?: boolean; + disableAutoFocus?: boolean; + titleId?: string; +} + +const NotificationBannerComponent = forwardRef( + (props, forwardedRef) => { + const { children, className, title, titleId, success, role, disableAutoFocus, ...rest } = props; + + const [moduleRef] = useState(() => forwardedRef || createRef()); + const [instanceError, setInstanceError] = useState(); + const [instance, setInstance] = useState(); + + useEffect(() => { + if (!('current' in moduleRef) || !moduleRef.current || instance) { + return; + } + + import('nhsuk-frontend') + .then(({ NotificationBanner }) => setInstance(new NotificationBanner(moduleRef.current))) + .catch(setInstanceError); + }, [moduleRef, instance]); + + const items = Children.toArray(children); + + const titleElement = items.find((child) => + childIsOfComponentType(child, NotificationBannerTitle, { + className: 'nhsuk-notification-banner__title', + }), + ); + + const titleElementId = titleElement?.props.id || titleId || 'nhsuk-notification-banner-title'; + + const contentItems = items.filter((child) => child !== titleElement); + + if (instanceError) { + throw instanceError; + } + + return ( +
+
+ {titleElement ? ( + <>{titleElement} + ) : ( + + {title} + + )} +
+
{contentItems}
+
+ ); + }, +); + +NotificationBannerComponent.displayName = 'NotificationBanner'; + +export const NotificationBanner = Object.assign(NotificationBannerComponent, { + Title: NotificationBannerTitle, + Heading: NotificationBannerHeading, + Link: NotificationBannerLink, +}); diff --git a/src/components/content-presentation/notification-banner/__tests__/NotificationBanner.test.tsx b/src/components/content-presentation/notification-banner/__tests__/NotificationBanner.test.tsx new file mode 100644 index 00000000..537c5d08 --- /dev/null +++ b/src/components/content-presentation/notification-banner/__tests__/NotificationBanner.test.tsx @@ -0,0 +1,406 @@ +import { renderClient, renderServer } from '#util/components'; +import { NotificationBanner } from '#components/content-presentation/notification-banner'; +import { createRef } from 'react'; +import { NotificationBannerLink } from '#components/content-presentation/notification-banner/components'; + +describe('NotificationBanner', () => { + it('matches snapshot', async () => { + const { container } = await renderClient( + + + The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. + + , + { className: 'nhsuk-notification-banner' }, + ); + + expect(container).toMatchSnapshot(); + }); + + it('matches snapshot success', async () => { + const { container } = await renderClient( + + Patient record updated + , + { className: 'nhsuk-notification-banner' }, + ); + + expect(container).toMatchSnapshot(); + }); + + it('matches snapshot custom title', async () => { + const { container } = await renderClient( + + + The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. + + , + { className: 'nhsuk-notification-banner' }, + ); + + expect(container).toMatchSnapshot(); + }); + + it('matches snapshot custom title html', async () => { + const { container } = await renderClient( + + + Very important information + + + The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. + + , + { className: 'nhsuk-notification-banner' }, + ); + + expect(container).toMatchSnapshot(); + }); + + it('matches snapshot with link in heading', async () => { + const { container } = await renderClient( + + + You have 7 days left to send your application.{' '} + View application. + + , + { className: 'nhsuk-notification-banner' }, + ); + + expect(container).toMatchSnapshot(); + }); + + it('matches snapshot with link in body', async () => { + const { container } = await renderClient( + + Patient record updated +

+ Contact{' '} + example@department.nhs.uk if + you think there's a problem. +

+
, + { className: 'nhsuk-notification-banner' }, + ); + + expect(container).toMatchSnapshot(); + }); + + it('matches snapshot with no sub elements', async () => { + const { container } = await renderClient( + + The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. + , + { className: 'nhsuk-notification-banner' }, + ); + + expect(container).toMatchSnapshot(); + }); + + it('matches snapshot with list', async () => { + const { container } = await renderClient( + + 4 files uploaded +
    +
  • + government-strategy.pdf +
  • +
  • + government-strategy-v2.pdf +
  • +
  • + government-strategy-v3-FINAL.pdf +
  • +
  • + government-strategy-v4-FINAL-v2.pdf +
  • +
+
, + { className: 'nhsuk-notification-banner' }, + ); + + expect(container).toMatchSnapshot(); + }); + + it('matches snapshot with long heading', async () => { + const { container } = await renderClient( + + + The patient record was withdrawn on 7 March 2014, before being sent in, sent back, + queried, lost, found, subjected to public inquiry, lost again, and finally buried in soft + peat for three months and recycled as firelighters. + + , + { className: 'nhsuk-notification-banner' }, + ); + + expect(container).toMatchSnapshot(); + }); + + it('matches snapshot with lots of content', async () => { + const { container } = await renderClient( + + + Check if you need to apply the reverse charge to this application + +

+ You will have to apply the{' '} + reverse charge if the + applicant supplies any of these services: +

+
    +
  • + constructing, altering, repairing, extending, demolishing or dismantling buildings or + structures (whether permanent or not), including offshore installation services +
  • +
  • + constructing, altering, repairing, extending, demolishing of any works forming, or + planned to form, part of the land, including (in particular) walls, roadworks, power + lines, electronic communications equipment, aircraft runways, railways, inland + waterways, docks and harbours +
  • +
+
, + { className: 'nhsuk-notification-banner' }, + ); + + expect(container).toMatchSnapshot(); + }); + + it('matches snapshot (via server)', async () => { + const { container } = await renderServer( + + + The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. + + , + { className: 'nhsuk-notification-banner' }, + ); + + expect(container).toMatchSnapshot(); + }); + + it('matches snapshot success (via server)', async () => { + const { container } = await renderServer( + + Patient record updated + , + { className: 'nhsuk-notification-banner' }, + ); + + expect(container).toMatchSnapshot(); + }); + + it('matches snapshot custom title (via server)', async () => { + const { container } = await renderServer( + + + The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. + + , + { className: 'nhsuk-notification-banner' }, + ); + + expect(container).toMatchSnapshot(); + }); + + it('matches snapshot custom title html (via server)', async () => { + const { container } = await renderServer( + + + Very important information + + + The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. + + , + { className: 'nhsuk-notification-banner' }, + ); + + expect(container).toMatchSnapshot(); + }); + + it('matches snapshot with link in heading (via server)', async () => { + const { container } = await renderServer( + + + You have 7 days left to send your application.{' '} + View application. + + , + { className: 'nhsuk-notification-banner' }, + ); + + expect(container).toMatchSnapshot(); + }); + + it('matches snapshot with link in body (via server)', async () => { + const { container } = await renderServer( + + Patient record updated +

+ Contact{' '} + example@department.nhs.uk if + you think there's a problem. +

+
, + { className: 'nhsuk-notification-banner' }, + ); + + expect(container).toMatchSnapshot(); + }); + + it('matches snapshot with no sub elements (via server)', async () => { + const { container } = await renderServer( + + The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. + , + { className: 'nhsuk-notification-banner' }, + ); + + expect(container).toMatchSnapshot(); + }); + + it('matches snapshot with list (via server)', async () => { + const { container } = await renderServer( + + 4 files uploaded +
    +
  • + file.pdf +
  • +
  • + file-v2.pdf +
  • +
  • + file-v3-FINAL.pdf +
  • +
  • + file-v4-FINAL-v2.pdf +
  • +
+
, + { className: 'nhsuk-notification-banner' }, + ); + + expect(container).toMatchSnapshot(); + }); + + it('matches snapshot with long heading (via server)', async () => { + const { container } = await renderServer( + + + The patient record was withdrawn on 7 March 2014, before being sent in, sent back, + queried, lost, found, subjected to public inquiry, lost again, and finally buried in soft + peat for three months and recycled as firelighters. + + , + { className: 'nhsuk-notification-banner' }, + ); + + expect(container).toMatchSnapshot(); + }); + + it('matches snapshot with lots of content (via server)', async () => { + const { container } = await renderServer( + + + Check if you need to apply the reverse charge to this application + +

+ You will have to apply the{' '} + reverse charge if the + applicant supplies any of these services: +

+
    +
  • + constructing, altering, repairing, extending, demolishing or dismantling buildings or + structures (whether permanent or not), including offshore installation services +
  • +
  • + constructing, altering, repairing, extending, demolishing of any works forming, or + planned to form, part of the land, including (in particular) walls, roadworks, power + lines, electronic communications equipment, aircraft runways, railways, inland + waterways, docks and harbours +
  • +
+
, + { className: 'nhsuk-notification-banner' }, + ); + + expect(container).toMatchSnapshot(); + }); + + it('forwards refs', async () => { + const ref = createRef(); + + const { modules } = await renderClient( + + The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. + , + { className: 'nhsuk-notification-banner' }, + ); + + const [notificationBannerEl] = modules; + + expect(ref.current).toBe(notificationBannerEl); + expect(ref.current).toHaveClass('nhsuk-notification-banner'); + }); + + it('has default role and autofocus', async () => { + const { modules } = await renderClient( + + The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. + , + { className: 'nhsuk-notification-banner' }, + ); + + const [notificationBannerEl] = modules; + + expect(notificationBannerEl?.getAttribute('role')).toBe('region'); + expect(notificationBannerEl?.dataset?.disableAutoFocus).toBe(undefined); + }); + + it('has alert role', async () => { + const { modules } = await renderClient( + + The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. + , + { className: 'nhsuk-notification-banner' }, + ); + + const [notificationBannerEl] = modules; + + expect(notificationBannerEl?.getAttribute('role')).toBe('alert'); + }); + + it('has disabled autofocus', async () => { + const { modules } = await renderClient( + + The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. + , + { className: 'nhsuk-notification-banner' }, + ); + + const [notificationBannerEl] = modules; + + expect(notificationBannerEl?.dataset?.disableAutoFocus).toBe('true'); + }); + + it('prioritises id of title element over provided title id', async () => { + const { modules } = await renderClient( + + Important information + + The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. + + , + { className: 'nhsuk-notification-banner' }, + ); + + const [notificationBannerEl] = modules; + + expect(notificationBannerEl?.getAttribute('aria-labelledby')).toEqual('correct-id'); + }); +}); diff --git a/src/components/content-presentation/notification-banner/__tests__/__snapshots__/NotificationBanner.test.tsx.snap b/src/components/content-presentation/notification-banner/__tests__/__snapshots__/NotificationBanner.test.tsx.snap new file mode 100644 index 00000000..2040b917 --- /dev/null +++ b/src/components/content-presentation/notification-banner/__tests__/__snapshots__/NotificationBanner.test.tsx.snap @@ -0,0 +1,769 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`NotificationBanner matches snapshot (via server) 1`] = ` +
+
+
+

+ Important +

+
+
+

+ The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. +

+
+
+
+`; + +exports[`NotificationBanner matches snapshot 1`] = ` +
+
+
+

+ Important +

+
+
+

+ The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. +

+
+
+
+`; + +exports[`NotificationBanner matches snapshot custom title (via server) 1`] = ` +
+
+
+

+ Upcoming maintenance +

+
+
+

+ The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. +

+
+
+
+`; + +exports[`NotificationBanner matches snapshot custom title 1`] = ` +
+
+
+

+ Upcoming maintenance +

+
+
+

+ The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. +

+
+
+
+`; + +exports[`NotificationBanner matches snapshot custom title html (via server) 1`] = ` +
+
+
+

+ + Very + + important information +

+
+
+

+ The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. +

+
+
+
+`; + +exports[`NotificationBanner matches snapshot custom title html 1`] = ` +
+
+
+

+ + Very + + important information +

+
+
+

+ The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. +

+
+
+
+`; + +exports[`NotificationBanner matches snapshot success (via server) 1`] = ` +
+ +
+`; + +exports[`NotificationBanner matches snapshot success 1`] = ` +
+ +
+`; + +exports[`NotificationBanner matches snapshot with link in body (via server) 1`] = ` +
+
+
+

+ Important +

+
+
+

+ Patient record updated +

+

+ Contact + + + + example@department.nhs.uk + + if you think there's a problem. +

+
+
+
+`; + +exports[`NotificationBanner matches snapshot with link in body 1`] = ` +
+
+
+

+ Important +

+
+
+

+ Patient record updated +

+

+ Contact + + + example@department.nhs.uk + + if you think there's a problem. +

+
+
+
+`; + +exports[`NotificationBanner matches snapshot with link in heading (via server) 1`] = ` +
+
+
+

+ Important +

+
+
+

+ You have 7 days left to send your application. + + + + View application + + . +

+
+
+
+`; + +exports[`NotificationBanner matches snapshot with link in heading 1`] = ` +
+
+
+

+ Important +

+
+
+

+ You have 7 days left to send your application. + + + View application + + . +

+
+
+
+`; + +exports[`NotificationBanner matches snapshot with list (via server) 1`] = ` +
+
+
+

+ Important +

+
+ +
+
+`; + +exports[`NotificationBanner matches snapshot with list 1`] = ` + +`; + +exports[`NotificationBanner matches snapshot with long heading (via server) 1`] = ` +
+
+
+

+ Important +

+
+
+

+ The patient record was withdrawn on 7 March 2014, before being sent in, sent back, queried, lost, found, subjected to public inquiry, lost again, and finally buried in soft peat for three months and recycled as firelighters. +

+
+
+
+`; + +exports[`NotificationBanner matches snapshot with long heading 1`] = ` +
+
+
+

+ Important +

+
+
+

+ The patient record was withdrawn on 7 March 2014, before being sent in, sent back, queried, lost, found, subjected to public inquiry, lost again, and finally buried in soft peat for three months and recycled as firelighters. +

+
+
+
+`; + +exports[`NotificationBanner matches snapshot with lots of content (via server) 1`] = ` +
+
+
+

+ Important +

+
+
+

+ Check if you need to apply the reverse charge to this application +

+

+ You will have to apply the + + + + reverse charge + + if the applicant supplies any of these services: +

+
    +
  • + constructing, altering, repairing, extending, demolishing or dismantling buildings or structures (whether permanent or not), including offshore installation services +
  • +
  • + constructing, altering, repairing, extending, demolishing of any works forming, or planned to form, part of the land, including (in particular) walls, roadworks, power lines, electronic communications equipment, aircraft runways, railways, inland waterways, docks and harbours +
  • +
+
+
+
+`; + +exports[`NotificationBanner matches snapshot with lots of content 1`] = ` +
+
+
+

+ Important +

+
+
+

+ Check if you need to apply the reverse charge to this application +

+

+ You will have to apply the + + + reverse charge + + if the applicant supplies any of these services: +

+
    +
  • + constructing, altering, repairing, extending, demolishing or dismantling buildings or structures (whether permanent or not), including offshore installation services +
  • +
  • + constructing, altering, repairing, extending, demolishing of any works forming, or planned to form, part of the land, including (in particular) walls, roadworks, power lines, electronic communications equipment, aircraft runways, railways, inland waterways, docks and harbours +
  • +
+
+
+
+`; + +exports[`NotificationBanner matches snapshot with no sub elements (via server) 1`] = ` +
+
+
+

+ Important +

+
+
+ The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. +
+
+
+`; + +exports[`NotificationBanner matches snapshot with no sub elements 1`] = ` +
+
+
+

+ Important +

+
+
+ The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. +
+
+
+`; diff --git a/src/components/content-presentation/notification-banner/components/NotificationBannerHeading.tsx b/src/components/content-presentation/notification-banner/components/NotificationBannerHeading.tsx new file mode 100644 index 00000000..a8a30a3b --- /dev/null +++ b/src/components/content-presentation/notification-banner/components/NotificationBannerHeading.tsx @@ -0,0 +1,22 @@ +'use client'; + +import { HeadingLevel, type HeadingLevelProps } from '#components/utils/HeadingLevel.js'; +import type { FC } from 'react'; + +export type NotificationBannerHeadingProps = HeadingLevelProps; + +export const NotificationBannerHeading: FC = ({ + children, + headingLevel = 'h3', + ...rest +}) => ( + + {children} + +); + +NotificationBannerHeading.displayName = 'NotificationBanner.Heading'; diff --git a/src/components/content-presentation/notification-banner/components/NotificationBannerLink.tsx b/src/components/content-presentation/notification-banner/components/NotificationBannerLink.tsx new file mode 100644 index 00000000..bc145aac --- /dev/null +++ b/src/components/content-presentation/notification-banner/components/NotificationBannerLink.tsx @@ -0,0 +1,19 @@ +import type { AsElementLink } from '#util/types/index.js'; +import { forwardRef } from 'react'; +import classNames from 'classnames'; + +export type NotificationBannerLinkProps = AsElementLink; + +export const NotificationBannerLink = forwardRef( + ({ children, className, asElement: Element = 'a', ...rest }, forwardedRef) => ( + + {children} + + ), +); + +NotificationBannerLink.displayName = 'NotificationBanner.Link'; diff --git a/src/components/content-presentation/notification-banner/components/NotificationBannerTitle.tsx b/src/components/content-presentation/notification-banner/components/NotificationBannerTitle.tsx new file mode 100644 index 00000000..7cf033d5 --- /dev/null +++ b/src/components/content-presentation/notification-banner/components/NotificationBannerTitle.tsx @@ -0,0 +1,25 @@ +import { HeadingLevel, type HeadingLevelProps } from '#components/utils/HeadingLevel.js'; +import type { FC } from 'react'; + +export interface NotificationBannerTitleProps extends HeadingLevelProps { + success?: boolean; +} + +export const NotificationBannerTitle: FC = ({ + children, + headingLevel = 'h2', + id = 'nhsuk-notification-banner-title', + success, + ...rest +}) => ( + + {children || (success ? 'Success' : 'Important')} + +); + +NotificationBannerTitle.displayName = 'NotificationBanner.Title'; diff --git a/src/components/content-presentation/notification-banner/components/index.ts b/src/components/content-presentation/notification-banner/components/index.ts new file mode 100644 index 00000000..e9dad42b --- /dev/null +++ b/src/components/content-presentation/notification-banner/components/index.ts @@ -0,0 +1,3 @@ +export * from './NotificationBannerHeading.js'; +export * from './NotificationBannerLink.js'; +export * from './NotificationBannerTitle.js'; diff --git a/src/components/content-presentation/notification-banner/index.ts b/src/components/content-presentation/notification-banner/index.ts new file mode 100644 index 00000000..4dfa21ab --- /dev/null +++ b/src/components/content-presentation/notification-banner/index.ts @@ -0,0 +1,2 @@ +export * from './components/index.js'; +export * from './NotificationBanner.js'; diff --git a/stories/Content Presentation/NotificationBanner.stories.tsx b/stories/Content Presentation/NotificationBanner.stories.tsx new file mode 100644 index 00000000..d247ba9d --- /dev/null +++ b/stories/Content Presentation/NotificationBanner.stories.tsx @@ -0,0 +1,153 @@ +import { type Meta, type StoryObj } from '@storybook/react-vite'; +import { NotificationBanner } from '#components'; +import { NotificationBannerLink } from '#components/content-presentation/notification-banner/components'; + +/** + * This component can be found in the `nhsuk-frontend` repository here. + * + * ## Implementation Notes + * + * The `NotificationBanner` component has three subcomponents: + * + * - `NotificationBanner.Title` + * - `NotificationBanner.Heading` + * - `NotificationBanner.Link` + * + * ## Usage + * + * ### Standard + * + * ```jsx + * import { NotificationBanner } from "nhsuk-react-components"; + * + * const Element = () => { + * return ( + * + * Patient record updated + *

+ * Contact example@department.nhs.uk if you think there's a problem. + *

+ *
+ * ); + * } + * ``` + */ +const meta: Meta = { + title: 'Content Presentation/Notification Banner', + component: NotificationBanner, +}; +export default meta; +type Story = StoryObj; + +export const StandardPanel: Story = { + args: {}, + render: () => ( + + + The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025. + + + ), +}; + +export const SuccessPanel: Story = { + args: {}, + render: () => ( + + Patient record updated +

+ Contact{' '} + example@department.nhs.uk if you + think there's a problem. +

+
+ ), +}; + +export const StandardPanelWithLink: Story = { + args: {}, + render: () => ( + + + You have 7 days left to send your application.{' '} + View application. + + + ), +}; + +export const StandardPanelWithCustomTitle: Story = { + args: { + title: 'Important Message', + }, + render: () => ( + + Upcoming maintenance +

The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025.

+
+ ), +}; + +export const StandardPanelWithCustomTitleElement: Story = { + args: {}, + render: () => ( + + + Maintenance + + Upcoming maintenance +

The service will be unavailable from 8pm to 9pm on Thursday 1 January 2025.

+
+ ), +}; + +export const StandardPanelWithList: Story = { + args: {}, + render: () => ( + + 4 files uploaded +
    +
  • + government-strategy.pdf +
  • +
  • + government-strategy-v2.pdf +
  • +
  • + government-strategy-v3-FINAL.pdf +
  • +
  • + government-strategy-v4-FINAL-v2.pdf +
  • +
+
+ ), +}; + +export const StandardPanelWithLotsOfContent: Story = { + args: {}, + render: () => ( + + + Check if you need to apply the reverse charge to this application + +

+ You will have to apply the{' '} + reverse charge if the applicant + supplies any of these services: +

+
    +
  • + constructing, altering, repairing, extending, demolishing or dismantling buildings or + structures (whether permanent or not), including offshore installation services +
  • +
  • + constructing, altering, repairing, extending, demolishing of any works forming, or planned + to form, part of the land, including (in particular) walls, roadworks, power lines, + electronic communications equipment, aircraft runways, railways, inland waterways, docks + and harbours +
  • +
+
+ ), +};