From daa4b637d1fecf006e1aa87b1261098d9a2d74e2 Mon Sep 17 00:00:00 2001 From: Connor Avery <214469360+connoravo-nhs@users.noreply.github.com> Date: Mon, 23 Mar 2026 16:45:29 +0000 Subject: [PATCH 1/6] Accessibility statement page, footer placement and public path allowance Signed-off-by: Connor Avery <214469360+connoravo-nhs@users.noreply.github.com> --- packages/cpt-ui/src/App.tsx | 2 + packages/cpt-ui/src/constants/environment.ts | 2 + .../ui-strings/AccessibilityStatement.ts | 13 +++++++ .../src/constants/ui-strings/FooterStrings.ts | 6 +++ .../src/pages/AccessibilityStatementPage.tsx | 39 +++++++++++++++++++ 5 files changed, 62 insertions(+) create mode 100644 packages/cpt-ui/src/constants/ui-strings/AccessibilityStatement.ts create mode 100644 packages/cpt-ui/src/pages/AccessibilityStatementPage.tsx diff --git a/packages/cpt-ui/src/App.tsx b/packages/cpt-ui/src/App.tsx index b77cdf1595..c2ed056e29 100644 --- a/packages/cpt-ui/src/App.tsx +++ b/packages/cpt-ui/src/App.tsx @@ -27,6 +27,7 @@ import PrivacyNoticePage from "./pages/PrivacyNoticePage" import SessionSelectionPage from "./pages/SessionSelection" import NoPrescriptionsFoundPage from "@/pages/NoPrescriptionsFoundPage" import NoPatientsFoundPage from "@/pages/NoPatientsFoundPage" +import AccessibilityStatementPage from "./pages/AccessibilityStatementPage" import {FRONTEND_PATHS} from "@/constants/environment" import SessionLoggedOutPage from "./pages/SessionLoggedOut" @@ -106,6 +107,7 @@ function AppContent() { } /> } /> } /> + } /> diff --git a/packages/cpt-ui/src/constants/environment.ts b/packages/cpt-ui/src/constants/environment.ts index 6121ddea5f..81109af8cd 100644 --- a/packages/cpt-ui/src/constants/environment.ts +++ b/packages/cpt-ui/src/constants/environment.ts @@ -75,6 +75,7 @@ export const FRONTEND_PATHS = { NO_PATIENT_FOUND: "/no-patient-found", NO_PRESCRIPTIONS_FOUND: "/no-prescriptions-found", PRIVACY_NOTICE: "/privacy-notice", + ACCESSIBILITY_STATEMENT: "/accessibility-statement", COOKIES_SELECTED: "/cookies-selected", SESSION_SELECTION: "/select-active-session", NOT_FOUND: "/notfound" @@ -89,6 +90,7 @@ export const PUBLIC_PATHS = [ FRONTEND_PATHS.PRIVACY_NOTICE, FRONTEND_PATHS.COOKIES_SELECTED, FRONTEND_PATHS.NOT_FOUND, + FRONTEND_PATHS.ACCESSIBILITY_STATEMENT, "/" ] as const diff --git a/packages/cpt-ui/src/constants/ui-strings/AccessibilityStatement.ts b/packages/cpt-ui/src/constants/ui-strings/AccessibilityStatement.ts new file mode 100644 index 0000000000..1512991564 --- /dev/null +++ b/packages/cpt-ui/src/constants/ui-strings/AccessibilityStatement.ts @@ -0,0 +1,13 @@ +export const AccessibilityStatementStrings = { + pageTitle: "Accessibility statement – Prescription tracker", + home: "Home", + accessibilityStatement: "Accessibility Statement", + header: "Accessibility statement for the Prescription Tracker", + contact: { + header: "Contact information and right to complain" + }, + intro: { + header: "Header", + welcome: "Welcome" + } +} diff --git a/packages/cpt-ui/src/constants/ui-strings/FooterStrings.ts b/packages/cpt-ui/src/constants/ui-strings/FooterStrings.ts index 5f19416b31..af8d926fd1 100644 --- a/packages/cpt-ui/src/constants/ui-strings/FooterStrings.ts +++ b/packages/cpt-ui/src/constants/ui-strings/FooterStrings.ts @@ -21,5 +21,11 @@ export const FOOTER_LINKS = [ href: "/site/cookies", external: false, testId: "eps_footer-link-cookie-policy" + }, + { + text: "Accessibility statement", + href: "/site/accessibility-statement", + external: false, + testId: "eps_footer-link-accessibility-statement" } ] diff --git a/packages/cpt-ui/src/pages/AccessibilityStatementPage.tsx b/packages/cpt-ui/src/pages/AccessibilityStatementPage.tsx new file mode 100644 index 0000000000..e1ff9728e9 --- /dev/null +++ b/packages/cpt-ui/src/pages/AccessibilityStatementPage.tsx @@ -0,0 +1,39 @@ +import React from "react" +import {Link} from "react-router-dom" +import {getHomeLink} from "@/helpers/loginFunctions" +import {Breadcrumb} from "nhsuk-react-components" +import {useAuth} from "@/context/AuthProvider" +import {usePageTitle} from "@/hooks/usePageTitle" +import {AccessibilityStatementStrings} from "@/constants/ui-strings/AccessibilityStatement" + +export default function AccessibilityStatementPage() { + const auth = useAuth() + + usePageTitle(AccessibilityStatementStrings.pageTitle) + + return ( +
+
+ + + + {AccessibilityStatementStrings.home} + + + + +
+
+

{AccessibilityStatementStrings.header}

+ +

{AccessibilityStatementStrings.intro.header}

+

{AccessibilityStatementStrings.intro.welcome}

+ +

+ {AccessibilityStatementStrings.contact.header} +

+
+
+
+ ) +} From d2dd3ca1a3d518abd78672dcb183cb5b56729e79 Mon Sep 17 00:00:00 2001 From: Connor Avery <214469360+connoravo-nhs@users.noreply.github.com> Date: Tue, 24 Mar 2026 14:06:47 +0000 Subject: [PATCH 2/6] Strings WIP Signed-off-by: Connor Avery <214469360+connoravo-nhs@users.noreply.github.com> --- .../ui-strings/AccessibilityStatement.ts | 13 ------- .../src/pages/AccessibilityStatementPage.tsx | 38 +++++++++++++++++-- 2 files changed, 35 insertions(+), 16 deletions(-) delete mode 100644 packages/cpt-ui/src/constants/ui-strings/AccessibilityStatement.ts diff --git a/packages/cpt-ui/src/constants/ui-strings/AccessibilityStatement.ts b/packages/cpt-ui/src/constants/ui-strings/AccessibilityStatement.ts deleted file mode 100644 index 1512991564..0000000000 --- a/packages/cpt-ui/src/constants/ui-strings/AccessibilityStatement.ts +++ /dev/null @@ -1,13 +0,0 @@ -export const AccessibilityStatementStrings = { - pageTitle: "Accessibility statement – Prescription tracker", - home: "Home", - accessibilityStatement: "Accessibility Statement", - header: "Accessibility statement for the Prescription Tracker", - contact: { - header: "Contact information and right to complain" - }, - intro: { - header: "Header", - welcome: "Welcome" - } -} diff --git a/packages/cpt-ui/src/pages/AccessibilityStatementPage.tsx b/packages/cpt-ui/src/pages/AccessibilityStatementPage.tsx index e1ff9728e9..ea1b5e5c87 100644 --- a/packages/cpt-ui/src/pages/AccessibilityStatementPage.tsx +++ b/packages/cpt-ui/src/pages/AccessibilityStatementPage.tsx @@ -4,7 +4,7 @@ import {getHomeLink} from "@/helpers/loginFunctions" import {Breadcrumb} from "nhsuk-react-components" import {useAuth} from "@/context/AuthProvider" import {usePageTitle} from "@/hooks/usePageTitle" -import {AccessibilityStatementStrings} from "@/constants/ui-strings/AccessibilityStatement" +import {AccessibilityStatementStrings} from "@/constants/ui-strings/AccessibilityStatementStrings" export default function AccessibilityStatementPage() { const auth = useAuth() @@ -27,11 +27,43 @@ export default function AccessibilityStatementPage() {

{AccessibilityStatementStrings.header}

{AccessibilityStatementStrings.intro.header}

-

{AccessibilityStatementStrings.intro.welcome}

+

This accessibility statement applies to the Prescription Tracker. +This website is run by NHS England. We want as many people as possible to be able to use this website. +For example, that means you should be able to:

+
    +
  • change colours, contrast levels and fonts using browser or device settings
  • +
  • zoom in up to 400% without the text spilling off the screen
  • +
  • navigate most of the website using a keyboard or speech recognition software
  • +
  • + listen to most of the website using a screen reader (including the most recent versions of JAWS, NVDA + and VoiceOver) +
  • +
+ +

+ We’ve also made the website text as simple as possible to understand. + + AbilityNet (opens in new tab) + has advice on making your device easier to use if you have a disability. +

- {AccessibilityStatementStrings.contact.header} + How accessible this website is

+

+ We know some parts of this website are not fully accessible: +

+
    +
  • screenreaders do not continue to read content when you select a new tab on the ‘Search for a prescription’ page
  • +
  • using the tab key does not focus the ‘Skip to main content’ link after a page is loaded
  • +
  • if you click into a page after it is loaded, using the tab key does not focus the next element of the page
  • +
  • the hint text is not associated with the search field on the prescription ID tab on the ‘Search for a prescription’ page
  • +
  • the cards on the ‘Select your role’ page should stay white when focused or hovered over
  • +
  • on smaller screens, there is no aria-expanded attribute on the menu icon
  • +
  • when you enter information into a field on the ‘Search for a prescription’ page and search, the page refreshes and focus is moved to the footer if you are navigating using arrow keys
  • +
  • the tabs on the ‘Prescription List’ page are separate URLs with the same headings and page titles, instead of the same URL with different fragment identifiers
  • +
  • selecting the tabs on the ‘Search for a prescription’ page should show you unique headings and page titles because each tab is a separate URL
  • +
From a6c8100930d1c872dbaafa2edbd6eab3228acf95 Mon Sep 17 00:00:00 2001 From: Connor Avery <214469360+connoravo-nhs@users.noreply.github.com> Date: Tue, 24 Mar 2026 16:08:00 +0000 Subject: [PATCH 3/6] Accessibility statement page, with tests and link rich content component to make handling links easier Signed-off-by: Connor Avery <214469360+connoravo-nhs@users.noreply.github.com> --- .../AccessibilityStatementPage.test.tsx | 216 ++++++++++++++++++ .../cpt-ui/__tests__/EpsRichText.test.tsx | 93 ++++++++ .../cpt-ui/src/components/EpsRichText.tsx | 35 +++ .../AccessibilityStatementStrings.ts | 131 +++++++++++ .../src/pages/AccessibilityStatementPage.tsx | 111 +++++++-- 5 files changed, 561 insertions(+), 25 deletions(-) create mode 100644 packages/cpt-ui/__tests__/AccessibilityStatementPage.test.tsx create mode 100644 packages/cpt-ui/__tests__/EpsRichText.test.tsx create mode 100644 packages/cpt-ui/src/components/EpsRichText.tsx create mode 100644 packages/cpt-ui/src/constants/ui-strings/AccessibilityStatementStrings.ts diff --git a/packages/cpt-ui/__tests__/AccessibilityStatementPage.test.tsx b/packages/cpt-ui/__tests__/AccessibilityStatementPage.test.tsx new file mode 100644 index 0000000000..74e19f4472 --- /dev/null +++ b/packages/cpt-ui/__tests__/AccessibilityStatementPage.test.tsx @@ -0,0 +1,216 @@ +import "@testing-library/jest-dom" +import React, {useState} from "react" +import {render, screen} from "@testing-library/react" +import {MemoryRouter} from "react-router-dom" +import {AuthContext, type AuthContextType} from "@/context/AuthProvider" +import AccessibilityStatementPage from "@/pages/AccessibilityStatementPage" +import {AccessibilityStatementStrings} from "@/constants/ui-strings/AccessibilityStatementStrings" +import {mockAuthState} from "./mocks/AuthStateMock" + +jest.mock("@/helpers/awsRum") +jest.mock("@/context/configureAmplify") + +jest.mock("@/constants/environment", () => ({ + AUTH_CONFIG: { + USER_POOL_ID: "test-pool-id", + USER_POOL_CLIENT_ID: "test-client-id", + HOSTED_LOGIN_DOMAIN: "test.domain", + REDIRECT_SIGN_IN: "http://localhost:3000", + REDIRECT_SIGN_OUT: "http://localhost:3000/logout" + }, + APP_CONFIG: { + REACT_LOG_LEVEL: "debug" + }, + API_ENDPOINTS: { + CIS2_SIGNOUT_ENDPOINT: "/api/cis2-signout" + }, + FRONTEND_PATHS: { + LOGIN: "/login", + SEARCH_BY_PRESCRIPTION_ID: "/search-by-prescription-id" + } +})) + +const signedOutAuthState: AuthContextType = { + ...mockAuthState, + isSignedIn: false +} + +const signedInAuthState: AuthContextType = { + ...mockAuthState, + isSignedIn: true +} + +const MockAuthProvider = ({ + children, + authState +}: { + children: React.ReactNode; + authState: AuthContextType; +}) => { + const [state] = useState(authState) + return {children} +} + +const renderPage = (authState: AuthContextType = signedOutAuthState) => { + return render( + + + + + + ) +} + +describe("AccessibilityStatementPage", () => { + describe("page structure", () => { + it("renders the main container", () => { + renderPage() + expect(screen.getByRole("main")).toBeInTheDocument() + }) + + it("renders the page heading", () => { + renderPage() + expect( + screen.getByRole("heading", {level: 1, name: AccessibilityStatementStrings.header}) + ).toBeInTheDocument() + }) + + it("renders the breadcrumb Home link", () => { + renderPage() + expect(screen.getByRole("link", {name: AccessibilityStatementStrings.home})).toBeInTheDocument() + }) + }) + + describe("section headings", () => { + beforeEach(() => { + renderPage() + }) + + it("renders the known issues heading", () => { + expect( + screen.getByRole("heading", {name: AccessibilityStatementStrings.knownIssues.header}) + ).toBeInTheDocument() + }) + + it("renders the feedback and contact information heading", () => { + expect( + screen.getByRole("heading", {name: AccessibilityStatementStrings.feedbackContactInformation.header}) + ).toBeInTheDocument() + }) + + it("renders the enforcement procedure heading", () => { + expect( + screen.getByRole("heading", {name: AccessibilityStatementStrings.enforcementProcedure.header}) + ).toBeInTheDocument() + }) + + it("renders the technical information heading", () => { + expect( + screen.getByRole("heading", {name: AccessibilityStatementStrings.technicalInformation.header}) + ).toBeInTheDocument() + }) + + it("renders the compliance status heading", () => { + expect( + screen.getByRole("heading", {name: AccessibilityStatementStrings.complianceStatus.header}) + ).toBeInTheDocument() + }) + + it("renders the non-accessible content heading", () => { + expect( + screen.getByRole("heading", {name: AccessibilityStatementStrings.nonaccessibleContent.header}) + ).toBeInTheDocument() + }) + + it("renders the non-compliance subheading", () => { + expect( + screen.getByRole("heading", {name: AccessibilityStatementStrings.nonaccessibleContent.subheader}) + ).toBeInTheDocument() + }) + + it("renders the improving accessibility heading", () => { + expect( + screen.getByRole("heading", {name: AccessibilityStatementStrings.improvingAccessibility.header}) + ).toBeInTheDocument() + }) + + it("renders the preparation subheading", () => { + expect( + screen.getByRole("heading", {name: AccessibilityStatementStrings.improvingAccessibility.subheader}) + ).toBeInTheDocument() + }) + }) + + describe("links rendered via EpsRichText", () => { + beforeEach(() => { + renderPage() + }) + + it("renders the Prescription Tracker link in the opening paragraph", () => { + const link = screen.getByRole("link", {name: "Prescription Tracker"}) + expect(link).toHaveAttribute("href", "/site") + expect(link).not.toHaveAttribute("target") + }) + + it("renders the AbilityNet external link", () => { + const link = screen.getByRole("link", {name: "AbilityNet (opens in new tab)"}) + expect(link).toHaveAttribute("href", "https://www.abilitynet.org.uk/") + expect(link).toHaveAttribute("target", "_blank") + expect(link).toHaveAttribute("rel", "noreferrer") + }) + + it("renders the email link in the feedback section", () => { + const links = screen.getAllByRole("link", {name: "epssupport@nhs.net"}) + expect(links).toHaveLength(2) + links.forEach((link) => { + expect(link).toHaveAttribute("href", "mailto:epssupport@nhs.net") + }) + }) + + it("renders the Equality Advisory Service external link", () => { + const link = screen.getByRole("link", { + name: "Equality Advisory and Support Service (opens in new tab)" + }) + expect(link).toHaveAttribute("href", "https://www.equalityadvisoryservice.com/") + expect(link).toHaveAttribute("target", "_blank") + expect(link).toHaveAttribute("rel", "noreferrer") + }) + }) + + describe("list content", () => { + it("renders the opening section accessibility features list", () => { + renderPage() + AccessibilityStatementStrings.openingSection.listItems.forEach((item) => { + expect(screen.getByText(item)).toBeInTheDocument() + }) + }) + + it("renders the known issues list", () => { + renderPage() + AccessibilityStatementStrings.knownIssues.listItems.forEach((item) => { + expect(screen.getByText(item)).toBeInTheDocument() + }) + }) + + it("renders the non-compliance sub-list", () => { + renderPage() + AccessibilityStatementStrings.nonaccessibleContent.subListItems.forEach((item) => { + expect(screen.getByText(item.trim())).toBeInTheDocument() + }) + }) + }) + + describe("breadcrumb home link destination", () => { + it("links to the login page when signed out", () => { + renderPage(signedOutAuthState) + const homeLink = screen.getByRole("link", {name: AccessibilityStatementStrings.home}) + expect(homeLink).toHaveAttribute("href", "/login") + }) + + it("links to the search page when signed in", () => { + renderPage(signedInAuthState) + const homeLink = screen.getByRole("link", {name: AccessibilityStatementStrings.home}) + expect(homeLink).toHaveAttribute("href", "/search-by-prescription-id") + }) + }) +}) diff --git a/packages/cpt-ui/__tests__/EpsRichText.test.tsx b/packages/cpt-ui/__tests__/EpsRichText.test.tsx new file mode 100644 index 0000000000..09cfd1a4e0 --- /dev/null +++ b/packages/cpt-ui/__tests__/EpsRichText.test.tsx @@ -0,0 +1,93 @@ +import "@testing-library/jest-dom" +import React from "react" +import {render, screen} from "@testing-library/react" +import EpsRichText from "@/components/EpsRichText" +import type {RichTextNode} from "@/components/EpsRichText" + +describe("EpsRichText", () => { + describe("plain string content", () => { + it("renders a single string", () => { + render() + expect(screen.getByText("Hello world")).toBeInTheDocument() + }) + + it("renders multiple strings from an array", () => { + const {container} = render() + expect(container).toHaveTextContent("Hello world") + }) + }) + + describe("link node content", () => { + it("renders a single internal link node", () => { + const node: RichTextNode = {text: "Prescription Tracker", href: "/site"} + render() + const link = screen.getByRole("link", {name: "Prescription Tracker"}) + expect(link).toBeInTheDocument() + expect(link).toHaveAttribute("href", "/site") + expect(link).not.toHaveAttribute("target") + expect(link).not.toHaveAttribute("rel") + }) + + it("renders an external link with target and rel attributes", () => { + const node: RichTextNode = { + text: "AbilityNet (opens in new tab)", + href: "https://www.abilitynet.org.uk/", + external: true + } + render() + const link = screen.getByRole("link", {name: "AbilityNet (opens in new tab)"}) + expect(link).toBeInTheDocument() + expect(link).toHaveAttribute("href", "https://www.abilitynet.org.uk/") + expect(link).toHaveAttribute("target", "_blank") + expect(link).toHaveAttribute("rel", "noreferrer") + }) + + it("does not add target/rel when external is false", () => { + const node: RichTextNode = {text: "Click here", href: "/somewhere", external: false} + render() + const link = screen.getByRole("link", {name: "Click here"}) + expect(link).not.toHaveAttribute("target") + expect(link).not.toHaveAttribute("rel") + }) + }) + + describe("mixed array content", () => { + it("renders a mix of strings and link nodes", () => { + const content: Array = [ + "This applies to the ", + {text: "Prescription Tracker", href: "/site"}, + "." + ] + const {container} = render() + expect(container).toHaveTextContent(/This applies to the/) + expect(container).toHaveTextContent(/\.$/) + const link = screen.getByRole("link", {name: "Prescription Tracker"}) + expect(link).toHaveAttribute("href", "/site") + }) + + it("renders multiple links in one array", () => { + const content: Array = [ + {text: "First link", href: "/first"}, + " and ", + {text: "Second link", href: "/second", external: true} + ] + render() + const first = screen.getByRole("link", {name: "First link"}) + const second = screen.getByRole("link", {name: "Second link"}) + expect(first).toHaveAttribute("href", "/first") + expect(first).not.toHaveAttribute("target") + expect(second).toHaveAttribute("href", "/second") + expect(second).toHaveAttribute("target", "_blank") + }) + + it("renders a mailto link correctly", () => { + const content: Array = [ + "Contact us at ", + {text: "epssupport@nhs.net", href: "mailto:epssupport@nhs.net"} + ] + render() + const link = screen.getByRole("link", {name: "epssupport@nhs.net"}) + expect(link).toHaveAttribute("href", "mailto:epssupport@nhs.net") + }) + }) +}) diff --git a/packages/cpt-ui/src/components/EpsRichText.tsx b/packages/cpt-ui/src/components/EpsRichText.tsx new file mode 100644 index 0000000000..346f7167f0 --- /dev/null +++ b/packages/cpt-ui/src/components/EpsRichText.tsx @@ -0,0 +1,35 @@ +import React from "react" + +export type RichTextLinkNode = { + text: string + href: string + external?: boolean +} + +export type RichTextNode = string | RichTextLinkNode +export type RichTextContent = RichTextNode | Array + +interface EpsRichTextProps { + content: RichTextContent +} + +export default function EpsRichText({content}: EpsRichTextProps) { + const nodes = Array.isArray(content) ? content : [content] + return ( + <> + {nodes.map((node, i) => + typeof node === "string" ? ( + {node} + ) : ( + + {node.text} + + ) + )} + + ) +} diff --git a/packages/cpt-ui/src/constants/ui-strings/AccessibilityStatementStrings.ts b/packages/cpt-ui/src/constants/ui-strings/AccessibilityStatementStrings.ts new file mode 100644 index 0000000000..50fb24003f --- /dev/null +++ b/packages/cpt-ui/src/constants/ui-strings/AccessibilityStatementStrings.ts @@ -0,0 +1,131 @@ +import type {RichTextNode} from "@/components/EpsRichText" + +export const AccessibilityStatementStrings = { + pageTitle: "Accessibility statement – Prescription tracker", + home: "Home", + header: "Accessibility statement for the Prescription Tracker", + openingSection: { + p1: [ + "This accessibility statement applies to the ", + {text: "Prescription Tracker", href: "/site"} satisfies RichTextNode, + "." + ] satisfies Array, + p2: "This website is run by NHS England. We want as many people as possible to be able to use this website. " + + "For example, that means you should be able to:", + listItems: [ + "change colours, contrast levels and fonts using browser or device settings", + "zoom in up to 400% without the text spilling off the screen", + "navigate most of the website using a keyboard or speech recognition software", + "listen to most of the website using a screen reader (including the most recent versions of JAWS, " + + "NVDA and VoiceOver)" + ], + p3: "We’ve also made the website text as simple as possible to understand.", + p4: [ + { + text: "AbilityNet (opens in new tab)", + href: "https://www.abilitynet.org.uk/", + external: true + } satisfies RichTextNode, + " has advice on making your device easier to use if you have a disability." + ] satisfies Array + }, + knownIssues: { + header: "How accessible this website is", + p1: "We know some parts of this website are not fully accessible:", + listItems: [ + "screenreaders do not continue to read content when you select a new tab on the ‘Search for a prescription’ page", + "using the tab key does not focus the ‘Skip to main content’ link after a page is loaded", + "if you click into a page after it is loaded, using the tab key does not focus the next element of the page", + "the hint text is not associated with the search field on the prescription ID tab on the " + + "‘Search for a prescription’ page", + "the cards on the ‘Select your role’ page should stay white when focused or hovered over", + "on smaller screens, there is no aria-expanded attribute on the menu icon", + "when you enter information into a field on the ‘Search for a prescription’ page and search, the " + + "page refreshes and focus is moved to the footer if you are navigating using arrow keys", + "the tabs on the ‘Prescription List’ page are separate URLs with the same headings and page titles, " + + "instead of the same URL with different fragment identifiers", + "selecting the tabs on the ‘Search for a prescription’ page should show you unique headings and page " + + "titles because each tab is a separate URL" + ] + }, + feedbackContactInformation: { + header: "Feedback and contact information", + p1: [ + "If you find any problems not listed on this page or think we're not meeting accessibility requirements, email ", + {text: "epssupport@nhs.net", href: "mailto:epssupport@nhs.net"} satisfies RichTextNode, + "." + ] satisfies Array, + p2: [ + "If you need information on this website in a different format, email ", + {text: "epssupport@nhs.net", href: "mailto:epssupport@nhs.net"} satisfies RichTextNode, + "." + ] satisfies Array, + p3: "We’ll consider your request and get back to you within 5 working days." + }, + enforcementProcedure: { + header: "Enforcement procedure", + p1: [ + "The Equality and Human Rights Commission (EHRC) is responsible for enforcing the Public Sector Bodies " + + "(Websites and Mobile Applications) (No. 2) Accessibility Regulations 2018 " + + "(the 'accessibility regulations'). If you're not happy with how we respond to your complaint, contact the ", + { + text: "Equality Advisory and Support Service (opens in new tab)", + href: "https://www.equalityadvisoryservice.com/", + external: true + } satisfies RichTextNode, + "." + ] satisfies Array + }, + technicalInformation: { + header: "Technical information about this website’s accessibility", + p1: "NHS England is committed to making its website accessible, in accordance with the Public Sector Bodies " + + "(Websites and Mobile Applications) (No. 2) Accessibility Regulations 2018." + }, + complianceStatus: { + header: "Compliance status", + p1: "The website has been tested against the Web Content Accessibility Guidelines (WCAG) 2.2 AA standard.", + p2: "This website is partially compliant with the Web Content Accessibility Guidelines version 2.2 AA standard, " + + "due to the non-compliances listed below." + }, + nonaccessibleContent: { + header: "Non-accessible content", + p1: "The content listed in the ‘Non-compliance with the accessibility regulations’ section explains the WCAG 2.2" + + " criteria that the Prescription Tracker is not compliant with and why.", + subheader: "Non-compliance with the accessibility regulations", + subListItems: [ + "Screenreaders do not continue to read content when you select a new tab on the ‘Search for a prescription’ " + + "page. This fails WCAG 2.2 success criterion 2.4.3 (focus order).", + "Using the tab key does not focus the ‘Skip to main content’ link after a page is loaded. This fails " + + "WCAG 2.2 success criterion 2.4.3 (focus order).", + "If you click into a page after it is loaded, using the tab key does not focus the next element of the page. " + + "This fails WCAG 2.2 success criterion 2.4.3 (focus order).", + "The hint text is not associated with the search field on the prescription ID tab on the " + + "‘Search for a prescription’ page. This fails WCAG 2.2 success criterion 3.3.1 (error identification). ", + "The cards on the ‘Select your role’ page should stay white when focused or hovered over. This fails WCAG 2.2 " + + "success criteria 1.4.11 (minimum contrast and non-text contrast).", + "On smaller screens, there is no aria-expanded attribute on the menu icon. This fails WCAG 2.2 success " + + "criterion 4.1.2 (name, role, value).", + "When you enter information into a field on the ‘Search for a prescription’ page and search, the page refreshes " + + "and focus is moved to the footer if you are navigating using arrow keys. " + + "This fails WCAG 2.2 success criterion 3.2.2 (on input).", + "The tabs on the ‘Prescription List’ page are separate URLs with the same headings and page titles, instead of " + + "the same URL with different fragment identifiers. " + + "This fails WCAG 2.2 success criterion 2.4.2 (page titled).", + "Selecting the tabs on the ‘Search for a prescription’ page should show you unique headings and page titles " + + "because each tab is a separate URL. " + + "This fails WCAG 2.2 success criterion 2.4.2 (page titled)." + ], + p2: "We plan on fixing these issues before the Prescription Tracker is available to use nationally." + }, + improvingAccessibility: { + header: "What we’re doing to improve accessibility", + p1: "An external company will audit the Prescription Tracker’s accessibility again in the next few months. " + + "We plan on fixing all accessibility issues before the Prescription Tracker is available to use nationally.", + subheader: "Preparation of this accessibility statement", + subheaderP1: "This statement was prepared on 9 March 2026. It was last reviewed on 9 March 2026.", + subheaderP2: "This website was last tested on 11 February 2026 against the WCAG 2.2 AA standard.", + subheaderP3: "The test was carried out by Dig Inclusion and the Prescription Tracker team also do their own " + + "accessibility testing of new features. This is done with a mixture of automated and manual testing.", + subheaderP4: "We are planning another external audit of the Prescription Tracker against the WCAG 2.2 AA standard." + } +} diff --git a/packages/cpt-ui/src/pages/AccessibilityStatementPage.tsx b/packages/cpt-ui/src/pages/AccessibilityStatementPage.tsx index ea1b5e5c87..decf891e33 100644 --- a/packages/cpt-ui/src/pages/AccessibilityStatementPage.tsx +++ b/packages/cpt-ui/src/pages/AccessibilityStatementPage.tsx @@ -5,6 +5,7 @@ import {Breadcrumb} from "nhsuk-react-components" import {useAuth} from "@/context/AuthProvider" import {usePageTitle} from "@/hooks/usePageTitle" import {AccessibilityStatementStrings} from "@/constants/ui-strings/AccessibilityStatementStrings" +import EpsRichText from "@/components/EpsRichText" export default function AccessibilityStatementPage() { const auth = useAuth() @@ -25,45 +26,105 @@ export default function AccessibilityStatementPage() {

{AccessibilityStatementStrings.header}

+

+

{AccessibilityStatementStrings.openingSection.p2}

+
    + {AccessibilityStatementStrings.openingSection.listItems.map((listItem, index) => ( +
  • {listItem}
  • + ))} +
+ +

{AccessibilityStatementStrings.openingSection.p3}

+

-

{AccessibilityStatementStrings.intro.header}

-

This accessibility statement applies to the Prescription Tracker. -This website is run by NHS England. We want as many people as possible to be able to use this website. -For example, that means you should be able to:

+

+ {AccessibilityStatementStrings.knownIssues.header} +

+

+ {AccessibilityStatementStrings.knownIssues.p1} +

    -
  • change colours, contrast levels and fonts using browser or device settings
  • -
  • zoom in up to 400% without the text spilling off the screen
  • -
  • navigate most of the website using a keyboard or speech recognition software
  • -
  • - listen to most of the website using a screen reader (including the most recent versions of JAWS, NVDA - and VoiceOver) -
  • + {AccessibilityStatementStrings.knownIssues.listItems.map((listItem, index) => ( +
  • {listItem}
  • + ))}
+

+ {AccessibilityStatementStrings.feedbackContactInformation.header} +

+

+ +

+

+ +

+

+ {AccessibilityStatementStrings.feedbackContactInformation.p3} +

+ +

+ {AccessibilityStatementStrings.enforcementProcedure.header} +

+

+ +

+ +

+ {AccessibilityStatementStrings.technicalInformation.header} +

- We’ve also made the website text as simple as possible to understand. + {AccessibilityStatementStrings.technicalInformation.p1} +

- AbilityNet (opens in new tab) - has advice on making your device easier to use if you have a disability. +

+ {AccessibilityStatementStrings.complianceStatus.header} +

+

+ {AccessibilityStatementStrings.complianceStatus.p1} +

+

+ {AccessibilityStatementStrings.complianceStatus.p2}

- How accessible this website is + {AccessibilityStatementStrings.nonaccessibleContent.header}

- We know some parts of this website are not fully accessible: + {AccessibilityStatementStrings.nonaccessibleContent.p1}

+

+ {AccessibilityStatementStrings.nonaccessibleContent.subheader} +

    -
  • screenreaders do not continue to read content when you select a new tab on the ‘Search for a prescription’ page
  • -
  • using the tab key does not focus the ‘Skip to main content’ link after a page is loaded
  • -
  • if you click into a page after it is loaded, using the tab key does not focus the next element of the page
  • -
  • the hint text is not associated with the search field on the prescription ID tab on the ‘Search for a prescription’ page
  • -
  • the cards on the ‘Select your role’ page should stay white when focused or hovered over
  • -
  • on smaller screens, there is no aria-expanded attribute on the menu icon
  • -
  • when you enter information into a field on the ‘Search for a prescription’ page and search, the page refreshes and focus is moved to the footer if you are navigating using arrow keys
  • -
  • the tabs on the ‘Prescription List’ page are separate URLs with the same headings and page titles, instead of the same URL with different fragment identifiers
  • -
  • selecting the tabs on the ‘Search for a prescription’ page should show you unique headings and page titles because each tab is a separate URL
  • + {AccessibilityStatementStrings.nonaccessibleContent.subListItems.map((listItem, index) => ( +
  • {listItem}
  • + ))}
+

+ {AccessibilityStatementStrings.nonaccessibleContent.p2} +

+ +

+ {AccessibilityStatementStrings.improvingAccessibility.header} +

+

+ {AccessibilityStatementStrings.improvingAccessibility.p1} +

+

+ {AccessibilityStatementStrings.improvingAccessibility.subheader} +

+

+ {AccessibilityStatementStrings.improvingAccessibility.subheaderP1} +

+

+ {AccessibilityStatementStrings.improvingAccessibility.subheaderP2} +

+

+ {AccessibilityStatementStrings.improvingAccessibility.subheaderP3} +

+

+ {AccessibilityStatementStrings.improvingAccessibility.subheaderP4} +

From 20721c9af8be0391ebb0ec3a5912e1397d8b6386 Mon Sep 17 00:00:00 2001 From: Connor Avery <214469360+connoravo-nhs@users.noreply.github.com> Date: Tue, 24 Mar 2026 16:22:30 +0000 Subject: [PATCH 4/6] Fix testid for the page Signed-off-by: Connor Avery <214469360+connoravo-nhs@users.noreply.github.com> --- packages/cpt-ui/src/pages/AccessibilityStatementPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cpt-ui/src/pages/AccessibilityStatementPage.tsx b/packages/cpt-ui/src/pages/AccessibilityStatementPage.tsx index decf891e33..d04cdfa7a2 100644 --- a/packages/cpt-ui/src/pages/AccessibilityStatementPage.tsx +++ b/packages/cpt-ui/src/pages/AccessibilityStatementPage.tsx @@ -24,7 +24,7 @@ export default function AccessibilityStatementPage() {
-
+

{AccessibilityStatementStrings.header}

{AccessibilityStatementStrings.openingSection.p2}

From 958241eea637cf35381af766c2272ec1354c386c Mon Sep 17 00:00:00 2001 From: Connor Avery <214469360+connoravo-nhs@users.noreply.github.com> Date: Wed, 25 Mar 2026 11:39:08 +0000 Subject: [PATCH 5/6] Alphabetical ordering of footer links Signed-off-by: Connor Avery <214469360+connoravo-nhs@users.noreply.github.com> --- .../src/constants/ui-strings/FooterStrings.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/cpt-ui/src/constants/ui-strings/FooterStrings.ts b/packages/cpt-ui/src/constants/ui-strings/FooterStrings.ts index af8d926fd1..bc7741954c 100644 --- a/packages/cpt-ui/src/constants/ui-strings/FooterStrings.ts +++ b/packages/cpt-ui/src/constants/ui-strings/FooterStrings.ts @@ -3,6 +3,18 @@ export const COMMIT_ID = import.meta.env.VITE_COMMIT_ID export const VERSION_NUMBER = import.meta.env.VITE_VERSION_NUMBER export const FOOTER_LINKS = [ + { + text: "Accessibility statement", + href: "/site/accessibility-statement", + external: false, + testId: "eps_footer-link-accessibility-statement" + }, + { + text: "Cookie policy", + href: "/site/cookies", + external: false, + testId: "eps_footer-link-cookie-policy" + }, { text: "Privacy notice", href: "/site/privacy-notice", @@ -15,17 +27,5 @@ export const FOOTER_LINKS = [ href: "https://digital.nhs.uk/services/care-identity-service/registration-authority-users/registration-authority-help/privacy-notice", external: true, testId: "eps_footer-link-terms-and-conditions" - }, - { - text: "Cookie policy", - href: "/site/cookies", - external: false, - testId: "eps_footer-link-cookie-policy" - }, - { - text: "Accessibility statement", - href: "/site/accessibility-statement", - external: false, - testId: "eps_footer-link-accessibility-statement" } ] From b9a9d8c91294d0e7717c60e297739a4a830818ae Mon Sep 17 00:00:00 2001 From: Connor Avery <214469360+connoravo-nhs@users.noreply.github.com> Date: Wed, 25 Mar 2026 11:54:42 +0000 Subject: [PATCH 6/6] Typo on screen readers Signed-off-by: Connor Avery <214469360+connoravo-nhs@users.noreply.github.com> --- .../constants/ui-strings/AccessibilityStatementStrings.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/cpt-ui/src/constants/ui-strings/AccessibilityStatementStrings.ts b/packages/cpt-ui/src/constants/ui-strings/AccessibilityStatementStrings.ts index 50fb24003f..c280fa12a4 100644 --- a/packages/cpt-ui/src/constants/ui-strings/AccessibilityStatementStrings.ts +++ b/packages/cpt-ui/src/constants/ui-strings/AccessibilityStatementStrings.ts @@ -33,7 +33,8 @@ export const AccessibilityStatementStrings = { header: "How accessible this website is", p1: "We know some parts of this website are not fully accessible:", listItems: [ - "screenreaders do not continue to read content when you select a new tab on the ‘Search for a prescription’ page", + "screen readers do not continue to read content when you select a new tab on the " + + "‘Search for a prescription’ page", "using the tab key does not focus the ‘Skip to main content’ link after a page is loaded", "if you click into a page after it is loaded, using the tab key does not focus the next element of the page", "the hint text is not associated with the search field on the prescription ID tab on the " @@ -93,7 +94,8 @@ export const AccessibilityStatementStrings = { + " criteria that the Prescription Tracker is not compliant with and why.", subheader: "Non-compliance with the accessibility regulations", subListItems: [ - "Screenreaders do not continue to read content when you select a new tab on the ‘Search for a prescription’ " + "screen readers do not continue to read content when you select a new tab on the " + + "‘Search for a prescription’ " + "page. This fails WCAG 2.2 success criterion 2.4.3 (focus order).", "Using the tab key does not focus the ‘Skip to main content’ link after a page is loaded. This fails " + "WCAG 2.2 success criterion 2.4.3 (focus order).",