Skip to content

Commit 5b8c8dd

Browse files
VIA-627 SB & EO Show how-to-get below recommendation.
1 parent 41d87cc commit 5b8c8dd

File tree

9 files changed

+85
-26
lines changed

9 files changed

+85
-26
lines changed

src/app/_components/content/Overview.test.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,25 @@ import {
88
import { render, screen } from "@testing-library/react";
99
import React from "react";
1010

11+
jest.mock("cheerio", () => ({
12+
load: jest.fn(() => {
13+
const selectorImpl = jest.fn(() => ({
14+
attr: jest.fn(),
15+
}));
16+
17+
const $ = Object.assign(selectorImpl, {
18+
html: jest.fn(() => "Overview <b>text</b>"),
19+
});
20+
21+
return $;
22+
}),
23+
}));
24+
1125
describe("Overview component", () => {
1226
it("renders overview without HTML correctly", () => {
1327
const vaccineType = VaccineType.SHINGLES;
1428

15-
render(<Overview styledVaccineContent={mockStyledContent} vaccineType={vaccineType} />);
29+
render(<Overview overview={mockStyledContent.overview} vaccineType={vaccineType} />);
1630

1731
const overviewText: HTMLElement = screen.getByTestId("overview-text");
1832

@@ -22,7 +36,7 @@ describe("Overview component", () => {
2236
it("renders overview with HTML correctly", () => {
2337
const vaccineType = VaccineType.SHINGLES;
2438

25-
render(<Overview styledVaccineContent={mockStyledContentWithHtmlOverview} vaccineType={vaccineType} />);
39+
render(<Overview overview={mockStyledContentWithHtmlOverview.overview} vaccineType={vaccineType} />);
2640

2741
const overviewText: HTMLElement = screen.getByTestId("overview-text");
2842

@@ -38,7 +52,7 @@ describe("Overview component", () => {
3852
it("does not render missing overview", () => {
3953
const vaccineType = VaccineType.FLU_IN_PREGNANCY;
4054

41-
render(<Overview styledVaccineContent={mockStyledContentWithMissingOverview} vaccineType={vaccineType} />);
55+
render(<Overview overview={mockStyledContentWithMissingOverview.overview} vaccineType={vaccineType} />);
4256

4357
const overviewText: HTMLElement | null = screen.queryByTestId("overview-text");
4458

src/app/_components/content/Overview.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
import { VaccineType } from "@src/models/vaccine";
2-
import { StyledVaccineContent } from "@src/services/content-api/types";
2+
import { Overview as StyledOverview } from "@src/services/content-api/types";
3+
import { fixupHtmlFragment } from "@src/utils/html";
34
import { JSX } from "react";
45

5-
const Overview = (props: { styledVaccineContent: StyledVaccineContent; vaccineType: VaccineType }): JSX.Element => {
6-
const element = props.styledVaccineContent.overview ? (
7-
props.styledVaccineContent.overview.containsHtml ? (
6+
const Overview = (props: { overview: StyledOverview | undefined; vaccineType: VaccineType }): JSX.Element => {
7+
const element = props.overview ? (
8+
props.overview.containsHtml ? (
89
<div
910
data-testid="overview-text"
10-
dangerouslySetInnerHTML={{ __html: props.styledVaccineContent.overview.content || "" }}
11+
dangerouslySetInnerHTML={{ __html: fixupHtmlFragment(props.overview.content) || "" }}
1112
/>
1213
) : (
13-
<p data-testid="overview-text">{props.styledVaccineContent.overview.content}</p>
14+
<p data-testid="overview-text">{props.overview.content}</p>
1415
)
1516
) : (
1617
<></>

src/app/_components/vaccine/Vaccine.test.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,19 @@ jest.mock("next/headers", () => ({
5555
headers: jest.fn(),
5656
}));
5757
jest.mock("sanitize-data", () => ({ sanitize: jest.fn() }));
58+
jest.mock("cheerio", () => ({
59+
load: jest.fn(() => {
60+
const selectorImpl = jest.fn(() => ({
61+
attr: jest.fn(),
62+
}));
63+
64+
const $ = Object.assign(selectorImpl, {
65+
html: jest.fn(() => "<p>HTML fragment</p>"),
66+
});
67+
68+
return $;
69+
}),
70+
}));
5871

5972
const nhsNumber = "5123456789";
6073

src/app/_components/vaccine/Vaccine.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,10 @@ const VaccineComponent = async ({ vaccineType }: VaccineProps): Promise<JSX.Elem
6868
<div className={styles.tableCellSpanHide}>
6969
{contentError != ContentErrorTypes.CONTENT_LOADING_ERROR && styledVaccineContent != undefined && (
7070
<>
71-
<Overview styledVaccineContent={styledVaccineContent} vaccineType={vaccineType} />
71+
<Overview overview={styledVaccineContent.overview} vaccineType={vaccineType} />
7272
<Recommendation styledVaccineContent={styledVaccineContent} />
7373
<WarningCallout styledVaccineContent={styledVaccineContent} vaccineType={vaccineType} />
74+
<Overview overview={styledVaccineContent.overviewConclusion} vaccineType={vaccineType} />
7475
</>
7576
)}
7677

src/services/content-api/parsers/content-styling-service.tsx

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ import {
1616
VaccinePageSection,
1717
VaccinePageSubsection,
1818
} from "@src/services/content-api/types";
19+
import { fixupHtmlFragment } from "@src/utils/html";
1920
import sanitiseHtml from "@src/utils/sanitise-html";
20-
import { load as loadHtml } from "cheerio";
2121
import { InsetText, WarningCallout } from "nhsuk-react-components";
2222
import React, { JSX } from "react";
2323

@@ -179,11 +179,14 @@ function styleCallout(callout: HeadingWithTypedContent | undefined): StyledPageS
179179
component: <MarkdownWithStyling content={callout.content} delineator={false} />,
180180
};
181181
case "html":
182-
const $ = loadHtml(callout.content, null, false);
183-
$("a").attr("target", "_blank");
184182
return {
185183
heading: callout.heading,
186-
component: <div data-testid="callout-html" dangerouslySetInnerHTML={{ __html: $.html() || "" }} />,
184+
component: (
185+
<div
186+
data-testid="callout-html"
187+
dangerouslySetInnerHTML={{ __html: fixupHtmlFragment(callout.content) || "" }}
188+
/>
189+
),
187190
};
188191
case "string":
189192
return {
@@ -215,26 +218,28 @@ const getStyledContentForVaccine = async (
215218
fragile: boolean,
216219
): Promise<StyledVaccineContent> => {
217220
const overview: Overview | undefined = filteredContent.overview;
221+
const callout: StyledPageSection | undefined = styleCallout(filteredContent.callout);
222+
const recommendation: StyledPageSection | undefined = styleRecommendation(filteredContent.recommendation);
223+
const overviewConclusion: Overview | undefined = filteredContent.overviewConclusion;
218224
let whatVaccineIsFor;
219225
if (filteredContent.whatVaccineIsFor) {
220226
whatVaccineIsFor = styleSection(filteredContent.whatVaccineIsFor);
221227
}
222228
const whoVaccineIsFor: StyledPageSection = styleSection(filteredContent.whoVaccineIsFor);
223229
const howToGetVaccine: StyledPageSection = styleHowToGetSection(vaccine, filteredContent.howToGetVaccine, fragile);
224230
const vaccineSideEffects: StyledPageSection = styleSection(filteredContent.vaccineSideEffects);
225-
const callout: StyledPageSection | undefined = styleCallout(filteredContent.callout);
226-
const recommendation: StyledPageSection | undefined = styleRecommendation(filteredContent.recommendation);
227231
const webpageLink: URL = filteredContent.webpageLink;
228232

229233
return {
230234
overview,
235+
callout,
236+
recommendation,
237+
overviewConclusion,
231238
whatVaccineIsFor,
232239
whoVaccineIsFor,
233240
howToGetVaccine,
234241
vaccineSideEffects,
235242
webpageLink,
236-
callout,
237-
recommendation,
238243
};
239244
};
240245

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,29 @@
11
import { buildFilteredContentForStandardVaccine } from "@src/services/content-api/parsers/content-filter-service";
2-
import { HeadingWithContent, VaccinePageContent } from "@src/services/content-api/types";
2+
import {
3+
ExpanderSubsection,
4+
HeadingWithContent,
5+
VaccinePageContent,
6+
VaccinePageSubsection,
7+
} from "@src/services/content-api/types";
38

49
export const buildFilteredContentForFluForSchoolAgedChildrenVaccine = (apiContent: string): VaccinePageContent => {
510
const standardFilteredContent = buildFilteredContentForStandardVaccine(apiContent);
611

12+
const overviewConclusion: VaccinePageSubsection | undefined =
13+
standardFilteredContent.howToGetVaccine.subsections.find((subsection: VaccinePageSubsection) => {
14+
return subsection.type == "expanderElement" && subsection.headline.startsWith("School");
15+
}) as ExpanderSubsection | undefined;
16+
717
const recommendation: HeadingWithContent = {
818
heading: "The flu vaccine is recommended for children who:",
919
content: ["* are of school age (Reception to Year 1)"].join("\n"),
1020
};
11-
return { ...standardFilteredContent, recommendation };
21+
22+
return {
23+
...standardFilteredContent,
24+
recommendation,
25+
overviewConclusion: overviewConclusion?.mainEntity
26+
? { content: overviewConclusion.mainEntity, containsHtml: true }
27+
: undefined,
28+
};
1229
};

src/services/content-api/types.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ type TableSubsection = {
8585
mainEntity: string;
8686
};
8787

88-
type ExpanderSubsection = {
88+
export type ExpanderSubsection = {
8989
type: "expanderElement";
9090
name: string;
9191
mainEntity: string;
@@ -104,13 +104,14 @@ export type Overview = { content: string; containsHtml: boolean };
104104

105105
export type VaccinePageContent = {
106106
overview?: Overview;
107+
callout?: HeadingWithTypedContent;
108+
recommendation?: HeadingWithContent;
109+
overviewConclusion?: Overview;
107110
whatVaccineIsFor?: VaccinePageSection;
108111
whoVaccineIsFor: VaccinePageSection;
109112
howToGetVaccine: VaccinePageSection;
110113
vaccineSideEffects: VaccinePageSection;
111114
webpageLink: URL;
112-
callout?: HeadingWithTypedContent;
113-
recommendation?: HeadingWithContent;
114115
};
115116

116117
export type StyledPageSection = {
@@ -134,13 +135,14 @@ export type HeadingWithTypedContent = {
134135

135136
export type StyledVaccineContent = {
136137
overview?: Overview;
138+
callout?: StyledPageSection;
139+
recommendation?: StyledPageSection;
140+
overviewConclusion?: Overview;
137141
whatVaccineIsFor?: StyledPageSection;
138142
whoVaccineIsFor: StyledPageSection;
139143
howToGetVaccine: StyledPageSection;
140144
vaccineSideEffects: StyledPageSection;
141145
webpageLink: URL;
142-
callout?: StyledPageSection;
143-
recommendation?: StyledPageSection;
144146
};
145147

146148
export type ContentApiVaccinationsResponse = {

src/utils/html.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { load as loadHtml } from "cheerio";
2+
3+
export function fixupHtmlFragment(fragment: string) {
4+
const $ = loadHtml(fragment, null, false);
5+
$("a").attr("target", "_blank");
6+
return $.html();
7+
}

wiremock/__files/child-flu-vaccine.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
"url": "https://int.api.service.nhs.uk/nhs-website-content/vaccinations/child-flu-vaccine/",
1717
"genre": ["Vaccine"],
1818
"keywords": "",
19-
"dateModified": "2025-10-10T11:00:56+00:00",
2019
"lastReviewed": ["2023-12-13T08:33:00+00:00", "2026-12-13T08:33:00+00:00"],
2120
"breadcrumb": {
2221
"@context": "http://schema.org",

0 commit comments

Comments
 (0)