Skip to content

Commit f74b8d3

Browse files
New: [AEA-6209] - Render a loading page if rendering is blocked (#1750)
## Summary - ✨ New Feature ### Details Render a loading page to illustrate to the user that something is happening, in the event that a render is currently blocked while state is being updated due to: 1. Login 2. Logout 3. Session time-outs 4. State malfunction (unknown cause) --------- Signed-off-by: Connor Avery <214469360+connoravo-nhs@users.noreply.github.com>
1 parent 3107df5 commit f74b8d3

File tree

5 files changed

+85
-13
lines changed

5 files changed

+85
-13
lines changed

packages/cpt-ui/__tests__/AccessProvider.test.tsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {useNavigate, useLocation} from "react-router-dom"
77
import {normalizePath as mockNormalizePath} from "@/helpers/utils"
88
import {logger} from "@/helpers/logger"
99
import {handleRestartLogin} from "@/helpers/logout"
10+
import Layout from "@/Layout"
11+
import LoadingPage from "@/pages/LoadingPage"
1012

1113
jest.mock("react-router-dom", () => ({
1214
useNavigate: jest.fn(),
@@ -21,6 +23,11 @@ jest.mock("@/context/AuthProvider", () => ({
2123
useAuth: jest.fn()
2224
}))
2325

26+
jest.mock("@/components/EpsHeader", () => ({
27+
__esModule: true,
28+
default: jest.fn(() => null)
29+
}))
30+
2431
jest.mock("@/constants/environment", () => ({
2532
FRONTEND_PATHS: {
2633
LOGIN: "/login",
@@ -49,7 +56,11 @@ jest.mock("@/constants/environment", () => ({
4956
"/privacy-notice",
5057
"/cookies-selected",
5158
"/"
52-
]
59+
],
60+
APP_CONFIG: {
61+
COMMIT_ID: "test-commit",
62+
VERSION_NUMBER: "1.0.0"
63+
}
5364
}))
5465

5566
jest.mock("@/helpers/logger", () => ({
@@ -235,12 +246,14 @@ describe("AccessProvider", () => {
235246

236247
const {container} = render(
237248
<AccessProvider>
238-
<TestComponent />
249+
<Layout>
250+
<LoadingPage />
251+
</Layout>
239252
</AccessProvider>
240253
)
241254

242-
// Should render nothing (children blocked)
243-
expect(container).toBeEmptyDOMElement()
255+
// Should render nothing (children blocked) - show loading wheel
256+
expect(container).toBeInTheDocument()
244257
})
245258

246259
it("allows children when concurrent session exists but user is on session selection page", () => {
@@ -281,12 +294,14 @@ describe("AccessProvider", () => {
281294

282295
const {container} = render(
283296
<AccessProvider>
284-
<TestComponent />
297+
<Layout>
298+
<LoadingPage />
299+
</Layout>
285300
</AccessProvider>
286301
)
287302

288-
// Should render nothing (children blocked)
289-
expect(container).toBeEmptyDOMElement()
303+
// Should render nothing (children blocked) - show loading page
304+
expect(container).toBeInTheDocument()
290305
})
291306
})
292307

packages/cpt-ui/jest.setup.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,34 @@ jest.mock("*.css", () => ({}), {virtual: true})
77
jest.mock("*.scss", () => ({}), {virtual: true})
88
jest.mock("@/styles/searchforaprescription.scss", () => ({}), {virtual: true})
99

10+
// Mock FooterStrings to avoid import.meta issues
11+
jest.mock("@/constants/ui-strings/FooterStrings", () => ({
12+
FOOTER_COPYRIGHT: "© NHS England",
13+
COMMIT_ID: "test-commit-id",
14+
VERSION_NUMBER: "test-version-number",
15+
FOOTER_LINKS: [
16+
{
17+
text: "Privacy notice",
18+
href: "/site/privacy-notice",
19+
external: false,
20+
testId: "eps_footer-link-privacy-notice"
21+
},
22+
{
23+
text: "Terms and conditions (opens in new tab)",
24+
// eslint-disable-next-line max-len
25+
href: "https://digital.nhs.uk/services/care-identity-service/registration-authority-users/registration-authority-help/privacy-notice",
26+
external: true,
27+
testId: "eps_footer-link-terms-and-conditions"
28+
},
29+
{
30+
text: "Cookie policy",
31+
href: "/site/cookies",
32+
external: false,
33+
testId: "eps_footer-link-cookie-policy"
34+
}
35+
]
36+
}))
37+
1038
const cwr_cookie_value_string = JSON.stringify({"sessionId":"my_rum_session_id"})
1139
const cwr_cookie_value_encoded = Buffer.from(cwr_cookie_value_string, "utf-8").toString("base64")
1240

packages/cpt-ui/src/Layout.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ import RBACBanner from "@/components/RBACBanner"
44
import EpsFooter from "@/components/EpsFooter"
55
import PatientDetailsBanner from "@/components/PatientDetailsBanner"
66
import PrescriptionInformationBanner from "@/components/PrescriptionInformationBanner"
7-
import {Fragment} from "react"
7+
import {Fragment, ReactNode} from "react"
88

9-
export default function Layout() {
9+
export default function Layout({children}: {children?: ReactNode}) {
1010
return (
1111
<Fragment>
1212
<EpsHeader />
13-
<PatientDetailsBanner />
14-
<PrescriptionInformationBanner />
15-
<Outlet />
13+
{!children && <PatientDetailsBanner />}
14+
{!children && <PrescriptionInformationBanner />}
15+
{children ?? <Outlet />}
1616
<RBACBanner />
1717
<EpsFooter />
1818
</Fragment>

packages/cpt-ui/src/context/AccessProvider.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {useAuth} from "./AuthProvider"
1212
import {ALLOWED_NO_ROLE_PATHS, FRONTEND_PATHS, PUBLIC_PATHS} from "@/constants/environment"
1313
import {logger} from "@/helpers/logger"
1414
import {handleRestartLogin} from "@/helpers/logout"
15+
import LoadingPage from "@/pages/LoadingPage"
16+
import Layout from "@/Layout"
1517

1618
export const AccessContext = createContext<Record<string, never> | null>(null)
1719

@@ -144,7 +146,11 @@ export const AccessProvider = ({children}: { children: ReactNode }) => {
144146
}, [auth.isSignedIn, auth.isSigningIn, location.pathname])
145147

146148
if (shouldBlockChildren()) {
147-
return <></>
149+
return (
150+
<Layout>
151+
<LoadingPage />
152+
</Layout>
153+
)
148154
}
149155

150156
return (
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import {Col, Container, Row} from "nhsuk-react-components"
2+
import EpsSpinner from "@/components/EpsSpinner"
3+
import {usePageTitle} from "@/hooks/usePageTitle"
4+
import {logger} from "@/helpers/logger"
5+
import {normalizePath} from "@/helpers/utils"
6+
7+
export default function LoadingPage() {
8+
usePageTitle("Loading information")
9+
const path = normalizePath(location.pathname)
10+
logger.info(`Loading requested path: ${path}`)
11+
return (
12+
<main className="nhsuk-main-wrapper">
13+
<Container>
14+
<Row>
15+
<Col width="full">
16+
<h1>Loading</h1>
17+
<EpsSpinner />
18+
</Col>
19+
</Row>
20+
</Container>
21+
</main>
22+
)
23+
}

0 commit comments

Comments
 (0)