Skip to content

Commit f497ac8

Browse files
marie-dedikova-nhsanoop-surej-nhs
authored andcommitted
VIA-2 DB/MD Improve UI of error page and render error page when there is error on flu vaccine page
1 parent b51be85 commit f497ac8

File tree

6 files changed

+86
-54
lines changed

6 files changed

+86
-54
lines changed

package-lock.json

Lines changed: 14 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@
6565
"openid-client": "^6.4.2",
6666
"pino": "^9.6.0",
6767
"react": "^19.0.0",
68-
"react-dom": "^19.0.0"
68+
"react-dom": "^19.0.0",
69+
"react-error-boundary": "^5.0.0"
6970
},
7071
"overrides": {
7172
"jsdom": "^26.0.0"

src/app/vaccines/flu/page.test.tsx

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,59 @@
1-
import { configProvider } from "@src/utils/config";
21
import { render, screen } from "@testing-library/react";
32
import VaccineFlu from "@src/app/vaccines/flu/page";
43
import Vaccine from "@src/app/_components/vaccine/Vaccine";
4+
import { getContentForVaccine } from "@src/services/content-api/gateway/content-reader-service";
5+
import { mockStyledContent } from "@test-data/content-api/data";
56

6-
jest.mock("@src/utils/config");
7-
jest.mock("@src/app/_components/vaccine/Vaccine", () => jest.fn(() => <div />));
7+
jest.mock("@src/services/content-api/gateway/content-reader-service");
8+
jest.mock("@src/app/_components/vaccine/Vaccine");
89

910
describe("Flu vaccine page", () => {
10-
(configProvider as jest.Mock).mockImplementation(() => ({
11-
CONTENT_CACHE_PATH: "wiremock/__files/",
12-
PINO_LOG_LEVEL: "info",
13-
}));
14-
15-
it("should contain back link to vaccination schedule page", () => {
16-
const pathToSchedulePage = "/schedule";
17-
18-
render(VaccineFlu());
11+
describe("when content loaded successfully", () => {
12+
beforeEach(() => {
13+
(getContentForVaccine as jest.Mock).mockResolvedValue(
14+
() => mockStyledContent,
15+
);
16+
(Vaccine as jest.Mock).mockImplementation(() => <div />);
17+
});
18+
19+
it("should contain back link to vaccination schedule page", () => {
20+
const pathToSchedulePage = "/schedule";
21+
22+
render(VaccineFlu());
23+
24+
const linkToSchedulePage = screen.getByRole("link", { name: "Go back" });
25+
26+
expect(linkToSchedulePage.getAttribute("href")).toBe(pathToSchedulePage);
27+
});
28+
29+
it("should contain vaccine component", () => {
30+
render(VaccineFlu());
31+
32+
expect(Vaccine).toHaveBeenCalledWith(
33+
{
34+
name: "Flu",
35+
},
36+
undefined,
37+
);
38+
});
39+
});
1940

20-
const linkToSchedulePage = screen.getByRole("link", { name: "Go back" });
41+
describe("when content fails to load with errors", () => {
42+
beforeEach(() => {
43+
(Vaccine as jest.Mock).mockImplementation(() => {
44+
throw new Error("error");
45+
});
46+
});
2147

22-
expect(linkToSchedulePage.getAttribute("href")).toBe(pathToSchedulePage);
23-
});
48+
it("should display error page", () => {
49+
render(VaccineFlu());
2450

25-
it("should contain vaccine component", () => {
26-
render(VaccineFlu());
51+
const errorHeading: HTMLElement = screen.getByRole("heading", {
52+
level: 2,
53+
name: "Vaccine content is unavailable",
54+
});
2755

28-
expect(Vaccine).toHaveBeenCalledWith(
29-
{
30-
name: "Flu",
31-
},
32-
undefined,
33-
);
56+
expect(errorHeading).toBeInTheDocument();
57+
});
3458
});
3559
});

src/app/vaccines/flu/page.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import Vaccine from "@src/app/_components/vaccine/Vaccine";
66
import { getContentForVaccine } from "@src/services/content-api/gateway/content-reader-service";
77
import { VaccineContentProvider } from "@src/app/_components/providers/VaccineContentProvider";
88
import { StyledVaccineContent } from "@src/services/content-api/parsers/content-styling-service";
9+
import { ErrorBoundary } from "react-error-boundary";
10+
import VaccineError from "@src/app/vaccines/vaccine-error/page";
911

1012
export const dynamic = "force-dynamic";
1113

@@ -18,9 +20,11 @@ const VaccineFlu = (): JSX.Element => {
1820
<div>
1921
<title>Flu Vaccine - NHS App</title>
2022
<BackLink link="/schedule" />
21-
<VaccineContentProvider contentPromise={contentPromise}>
22-
<Vaccine name={VaccineDisplayNames.FLU} />
23-
</VaccineContentProvider>
23+
<ErrorBoundary fallback={<VaccineError vaccineType={VaccineTypes.FLU} />}>
24+
<VaccineContentProvider contentPromise={contentPromise}>
25+
<Vaccine name={VaccineDisplayNames.FLU} />
26+
</VaccineContentProvider>
27+
</ErrorBoundary>
2428
</div>
2529
);
2630
};

src/app/vaccines/vaccine-error/page.test.tsx

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,6 @@ import VaccineError from "@src/app/vaccines/vaccine-error/page";
33
import { VaccineDisplayNames, VaccineTypes } from "@src/models/vaccine";
44

55
describe("VaccineError", () => {
6-
it("should display back button", () => {
7-
const pathToSchedulePage: string = "/schedule";
8-
9-
render(VaccineError({ vaccineType: VaccineTypes.FLU }));
10-
11-
const linkToSchedulePage: HTMLElement = screen.getByRole("link", {
12-
name: "Go back",
13-
});
14-
expect(linkToSchedulePage.getAttribute("href")).toBe(pathToSchedulePage);
15-
});
16-
176
it.each([VaccineTypes.FLU, VaccineTypes.RSV, VaccineTypes.SIX_IN_ONE])(
187
`should display vaccine name in error heading`,
198
(vaccineType: VaccineTypes) => {

src/app/vaccines/vaccine-error/page.tsx

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
"use client";
2+
13
import { JSX } from "react";
2-
import BackLink from "@src/app/_components/nhs-frontend/BackLink";
34
import { VaccineDisplayNames, VaccineTypes } from "@src/models/vaccine";
4-
import styles from "@src/app/styles.module.css";
5+
import { ErrorSummary } from "nhsuk-react-components";
56

67
interface VaccineProps {
78
vaccineType: VaccineTypes;
@@ -10,18 +11,20 @@ interface VaccineProps {
1011
const VaccineError = ({ vaccineType }: VaccineProps): JSX.Element => {
1112
return (
1213
<div>
13-
<BackLink link="/schedule" />
1414
<h1 className="app-dynamic-page-title__heading">{`${VaccineDisplayNames[vaccineType]} vaccine`}</h1>
15-
<div className={styles.subheading}>
16-
<div>
17-
<h2 className="nhsuk-heading-s">Vaccine content is unavailable</h2>
18-
</div>
19-
<p>
20-
Sorry, there is a problem showing vaccine information. Come back
21-
later, or read about{" "}
22-
<a href="https://www.nhs.uk/vaccinations/">vaccinations on NHS.uk</a>.
23-
</p>
24-
</div>
15+
<ErrorSummary>
16+
<ErrorSummary.Title>Vaccine content is unavailable</ErrorSummary.Title>
17+
<ErrorSummary.Body>
18+
<p>
19+
Sorry, there is a problem showing vaccine information. Come back
20+
later, or read about{" "}
21+
<a href="https://www.nhs.uk/vaccinations/">
22+
vaccinations on NHS.uk
23+
</a>
24+
.
25+
</p>
26+
</ErrorSummary.Body>
27+
</ErrorSummary>
2528
</div>
2629
);
2730
};

0 commit comments

Comments
 (0)