diff --git a/components/breadcrumbs.js b/components/breadcrumbs.js
index 9d6ffd51..0e4d513a 100644
--- a/components/breadcrumbs.js
+++ b/components/breadcrumbs.js
@@ -133,6 +133,39 @@ const Breadcrumbs = (props) => {
return simpleRouteRenderersByPath[path] || null;
};
+ const getSimpleLinkTextPairRenderer = (path) => {
+ const simpleLinkTextPairRenderersByPath = {
+ "/addresssearchresults": () => (
+ <>
+ {renderLinkCrumb(
+ isWelsh
+ ? router.locale + "/chwiliadcyfeiriadau"
+ : "/addresssearch",
+ t("common:breadcrumb-address-search")
+ )}
+ {renderTextCrumb(
+ t("common:breadcrumb-address-search-results")
+ )}
+ >
+ ),
+ "/advancedsearchresults": () => (
+ <>
+ {renderLinkCrumb(
+ isWelsh
+ ? router.locale + "/advancedsearch"
+ : "/advancedsearch",
+ t("common:breadcrumb-advanced-search")
+ )}
+ {renderTextCrumb(
+ t("common:breadcrumb-advanced-search-results")
+ )}
+ >
+ )
+ };
+
+ return simpleLinkTextPairRenderersByPath[path] || null;
+ };
+
const renderMyPortalCrumb = () => (
@@ -235,6 +268,13 @@ const Breadcrumbs = (props) => {
? renderSimpleRoute()
: null;
})()}
+ {(() => {
+ const renderSimpleLinkTextPair =
+ getSimpleLinkTextPairRenderer(pathname);
+ return renderSimpleLinkTextPair
+ ? renderSimpleLinkTextPair()
+ : null;
+ })()}
{isPath("/myportal/searchresults") && (
<>
{renderAnchorCrumb(
@@ -258,38 +298,6 @@ const Breadcrumbs = (props) => {
renderMyPortalSectionCrumbs(
t("common:footer-contact-us-link-label")
)}
- {isPath("/addresssearchresults") && (
- <>
- {" "}
- {renderLinkCrumb(
- isWelsh
- ? router.locale + "/chwiliadcyfeiriadau"
- : "/addresssearch",
- t("common:breadcrumb-address-search")
- )}
- {renderTextCrumb(
- t(
- "common:breadcrumb-address-search-results"
- )
- )}
- >
- )}
- {isPath("/advancedsearchresults") && (
- <>
- {" "}
- {renderLinkCrumb(
- isWelsh
- ? router.locale + "/advancedsearch"
- : "/advancedsearch",
- t("common:breadcrumb-advanced-search")
- )}
- {renderTextCrumb(
- t(
- "common:breadcrumb-advanced-search-results"
- )
- )}
- >
- )}
{isPath("/myportal/advancedsearchresults") && (
<>
{renderMyPortalCrumb()}
diff --git a/memory-bank/change-log.md b/memory-bank/change-log.md
index 90f5a707..be7d909b 100644
--- a/memory-bank/change-log.md
+++ b/memory-bank/change-log.md
@@ -361,6 +361,37 @@ Follow-ups:
- Next slice recommendation: extract a second tiny map for simple "link + text" pair routes (no callbacks, no side effects) to further shrink the conditional chain while keeping callback/dynamic branches explicit.
+### CL-22541-M: breadcrumbs simple link+text pair map + focused structure coverage
+
+date: 2026-04-09
+author: Cline
+scope: `components/breadcrumbs.js`, `tests/phase22/{breadcrumbs-route-map-structure,index}.test.cjs`
+type: change
+rationale: Execute the next bounded slice by extracting simple link+text pair route branches into a tiny renderer map and adding focused regression checks that mapped and dynamic callback branches remain in the intended structure.
+impact: Refactor-only structural deduplication/readability improvement plus focused test coverage; no intended route/auth/session/API/EN-CY/a11y behavior change.
+status: completed
+
+Summary:
+
+- Added `getSimpleLinkTextPairRenderer(path)` in `components/breadcrumbs.js` for simple no-callback "link + text" pair routes.
+- Migrated these routes into the new pair map:
+ - `/addresssearchresults`
+ - `/advancedsearchresults`
+- Kept dynamic/callback-driven branches explicit and untouched (e.g., `/myportal/case/id/[incident]` with `router.back()`).
+- Added focused phase22 suite `tests/phase22/breadcrumbs-route-map-structure.test.cjs` asserting:
+ - mapped simple pair routes exist in the new map
+ - explicit dynamic callback branch remains present.
+- Registered the new suite in `tests/phase22/index.test.cjs`.
+
+Validation:
+
+- `npx eslint components/breadcrumbs.js tests/phase22/index.test.cjs tests/phase22/breadcrumbs-route-map-structure.test.cjs` -> pass.
+- `node tests/phase22/index.test.cjs` -> pass (combined suite, including new breadcrumbs-route-map tests 2/2).
+
+Follow-ups:
+
+- Next slice recommendation: introduce a tiny local `renderMappedRoute(path, resolver)` helper to remove repeated inline IIFE map invocations, then optionally merge simple maps under one top-level resolver while preserving explicit ordering and side-effect-free constraints.
+
### CL-00X: 22500 `components/elements/index.js` Phase 1 helper extraction
date: 2026-04-07
diff --git a/tests/phase22/breadcrumbs-route-map-structure.test.cjs b/tests/phase22/breadcrumbs-route-map-structure.test.cjs
new file mode 100644
index 00000000..c3a6f99c
--- /dev/null
+++ b/tests/phase22/breadcrumbs-route-map-structure.test.cjs
@@ -0,0 +1,71 @@
+const assert = require("assert");
+const fs = require("fs");
+const path = require("path");
+
+const tests = [];
+const test = (name, fn) => tests.push({ name, fn });
+
+const loadBreadcrumbSource = () => {
+ const filePath = path.join(
+ __dirname,
+ "..",
+ "..",
+ "components",
+ "breadcrumbs.js"
+ );
+ return fs.readFileSync(filePath, "utf8");
+};
+
+test("breadcrumbs/simple-link-text map includes address and advanced search result routes", async () => {
+ const source = loadBreadcrumbSource();
+
+ assert.strictEqual(
+ source.includes('"/addresssearchresults": () => ('),
+ true,
+ "Expected /addresssearchresults to be mapped in getSimpleLinkTextPairRenderer"
+ );
+
+ assert.strictEqual(
+ source.includes('"/advancedsearchresults": () => ('),
+ true,
+ "Expected /advancedsearchresults to be mapped in getSimpleLinkTextPairRenderer"
+ );
+});
+
+test("breadcrumbs/dynamic callback branch remains explicit for myportal case incident route", async () => {
+ const source = loadBreadcrumbSource();
+
+ assert.strictEqual(
+ source.includes('isPath("/myportal/case/id/[incident]")'),
+ true,
+ "Expected dynamic /myportal/case/id/[incident] breadcrumb branch to remain explicit"
+ );
+
+ assert.strictEqual(
+ source.includes("router.back();"),
+ true,
+ "Expected callback-driven router.back() behavior to remain explicit"
+ );
+});
+
+const run = async () => {
+ let passed = 0;
+
+ for (const currentTest of tests) {
+ await currentTest.fn();
+ passed += 1;
+ }
+
+ console.log(
+ `Phase 22 breadcrumbs-route-map tests passed (${passed}/${tests.length}).`
+ );
+};
+
+module.exports = run;
+
+if (require.main === module) {
+ run().catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
+}
diff --git a/tests/phase22/index.test.cjs b/tests/phase22/index.test.cjs
index a8e2ea0f..b3996055 100644
--- a/tests/phase22/index.test.cjs
+++ b/tests/phase22/index.test.cjs
@@ -7,6 +7,7 @@ const runAuthRedirectSafetyTests = require("./auth-redirect-safety.test.cjs");
const runI18nRouteParityTests = require("./i18n-route-parity.test.cjs");
const runAzurestorageHelperTests = require("./azurestorage-helper-behaviour.test.cjs");
const runRouteStateHelperTests = require("./route-state-helper.test.cjs");
+const runBreadcrumbsRouteMapStructureTests = require("./breadcrumbs-route-map-structure.test.cjs");
const run = async () => {
await runCoreTokenTests();
@@ -18,6 +19,7 @@ const run = async () => {
await runI18nRouteParityTests();
await runAzurestorageHelperTests();
await runRouteStateHelperTests();
+ await runBreadcrumbsRouteMapStructureTests();
console.log("Phase 22 combined suite passed.");
};