diff --git a/components/breadcrumbs.js b/components/breadcrumbs.js index f27c33ff..ab0bb689 100644 --- a/components/breadcrumbs.js +++ b/components/breadcrumbs.js @@ -17,13 +17,7 @@ import { buildBreadcrumbRendererMaps, resolveMappedRouteRenderer as resolveMappedRouteRendererByMaps } from "../lib/routing/breadcrumbRouteMaps"; -import { - createSimpleRouteRenderers, - createSimpleLinkTextPairRenderers, - createSimpleMyPortalRouteRenderers, - createCaseDetailRouteRenderers, - createDetailAndAccountRouteRenderers -} from "../lib/routing/breadcrumbRendererFactories"; +import { createMappedRouteRendererGroups } from "../lib/routing/breadcrumbRendererFactories"; const Breadcrumbs = (props) => { const { @@ -196,37 +190,6 @@ const Breadcrumbs = (props) => { ); - const simpleRouteRenderersByPath = createSimpleRouteRenderers({ - t, - renderTextCrumb, - renderMyPortalSectionCrumbs - }); - - const simpleLinkTextPairRenderersByPath = createSimpleLinkTextPairRenderers( - { - t, - router, - isWelsh, - renderLinkCrumb, - renderTextCrumb - } - ); - - const simpleMyPortalRouteRenderersByPath = - createSimpleMyPortalRouteRenderers({ - t, - router, - isWelsh, - myPortalHref, - currentViewState, - getViewAllLabel, - renderAnchorCrumb, - renderLinkCrumb, - renderTextCrumb, - renderMyPortalCrumb, - renderMyPortalSectionCrumbs - }); - const resolveMappedRouteRenderer = (path) => { return resolveMappedRouteRendererByMaps(path, mappedRouteRendererMaps); }; @@ -241,39 +204,39 @@ const Breadcrumbs = (props) => { query: router.query, hasSession: Boolean(session), getViewAllLabel, - advancedLabel: () => t("common:breadcrumb-advanced-search-results"), - addressLabel: () => t("common:breadcrumb-address-search-results"), - defaultLabel: () => t("common:breadcrumb-search-results") + advancedLabel: t("common:breadcrumb-advanced-search-results"), + addressLabel: t("common:breadcrumb-address-search-results"), + defaultLabel: t("common:breadcrumb-search-results") }); - const caseDetailRouteRenderersByPath = createCaseDetailRouteRenderers({ + const { + simpleRouteRenderersByPath, + simpleLinkTextPairRenderersByPath, + simpleMyPortalRouteRenderersByPath, + caseDetailRouteRenderersByPath, + detailAndAccountRouteRenderersByPath + } = createMappedRouteRendererGroups({ t, router, isWelsh, + myPortalHref, + cyMyPortalHref, + currentViewState, + getViewAllLabel, caseResultsHref, breadcrumbLabel, fallbackSearchTitle, + currentReference, caseReferenceDisplay, - getViewAllLabel, + renderAnchorCrumb, renderLinkCrumb, + renderTextCrumb, renderCaseReferenceCrumb, - renderMyPortalCrumb + renderMyPortalCrumb, + renderMyPortalSectionCrumbs, + renderDnsCaseReferenceCrumbs }); - const detailAndAccountRouteRenderersByPath = - createDetailAndAccountRouteRenderers({ - t, - router, - isWelsh, - cyMyPortalHref, - currentReference, - caseReferenceDisplay, - renderMyPortalCrumb, - renderLinkCrumb, - renderCaseReferenceCrumb, - renderDnsCaseReferenceCrumbs - }); - const mappedRouteRendererMaps = buildBreadcrumbRendererMaps({ simpleRouteRenderersByPath, simpleLinkTextPairRenderersByPath, diff --git a/lib/routing/breadcrumbRendererFactories.js b/lib/routing/breadcrumbRendererFactories.js index 138497cc..7cf29243 100644 --- a/lib/routing/breadcrumbRendererFactories.js +++ b/lib/routing/breadcrumbRendererFactories.js @@ -312,3 +312,92 @@ export const createDetailAndAccountRouteRenderers = ({ ) }); + +export const createMappedRouteRendererGroups = ({ + t, + router, + isWelsh, + myPortalHref, + cyMyPortalHref, + currentViewState, + getViewAllLabel, + caseResultsHref, + breadcrumbLabel, + fallbackSearchTitle, + currentReference, + caseReferenceDisplay, + renderAnchorCrumb, + renderLinkCrumb, + renderTextCrumb, + renderCaseReferenceCrumb, + renderMyPortalCrumb, + renderMyPortalSectionCrumbs, + renderDnsCaseReferenceCrumbs +}) => { + const simpleRouteRenderersByPath = createSimpleRouteRenderers({ + t, + renderTextCrumb, + renderMyPortalSectionCrumbs + }); + + const simpleLinkTextPairRenderersByPath = createSimpleLinkTextPairRenderers( + { + t, + router, + isWelsh, + renderLinkCrumb, + renderTextCrumb + } + ); + + const simpleMyPortalRouteRenderersByPath = + createSimpleMyPortalRouteRenderers({ + t, + router, + isWelsh, + myPortalHref, + currentViewState, + getViewAllLabel, + renderAnchorCrumb, + renderLinkCrumb, + renderTextCrumb, + renderMyPortalCrumb, + renderMyPortalSectionCrumbs + }); + + const caseDetailRouteRenderersByPath = createCaseDetailRouteRenderers({ + t, + router, + isWelsh, + caseResultsHref, + breadcrumbLabel, + fallbackSearchTitle, + caseReferenceDisplay, + getViewAllLabel, + renderLinkCrumb, + renderCaseReferenceCrumb, + renderMyPortalCrumb + }); + + const detailAndAccountRouteRenderersByPath = + createDetailAndAccountRouteRenderers({ + t, + router, + isWelsh, + cyMyPortalHref, + currentReference, + caseReferenceDisplay, + renderMyPortalCrumb, + renderLinkCrumb, + renderCaseReferenceCrumb, + renderDnsCaseReferenceCrumbs + }); + + return { + simpleRouteRenderersByPath, + simpleLinkTextPairRenderersByPath, + simpleMyPortalRouteRenderersByPath, + caseDetailRouteRenderersByPath, + detailAndAccountRouteRenderersByPath + }; +}; diff --git a/memory-bank/change-log.md b/memory-bank/change-log.md index 272f9802..a798936c 100644 --- a/memory-bank/change-log.md +++ b/memory-bank/change-log.md @@ -532,6 +532,39 @@ Follow-ups: - Next slice option A (larger): move per-path map object declarations out of `components/breadcrumbs.js` into module-level pure factories (still injected with `t/router/currentView`) to further shrink component body. - Next slice option B (smaller): add one focused parity test asserting mapped resolver fallback returns `null` in component rendering path for non-mapped routes (ensures no accidental render noise). +### CL-22541-R: breadcrumbs mapped-group factory composition + unmapped-route null parity guard + +date: 2026-04-09 +author: Cline +scope: `lib/routing/breadcrumbRendererFactories.js`, `components/breadcrumbs.js`, `tests/phase22/breadcrumbs-route-map-structure.test.cjs` +type: change +rationale: Execute both approved next slices in one commit by centralizing deterministic mapped route-group composition into a factory-level helper and adding explicit structure guard coverage for null fallback behavior on unmapped routes. +impact: Refactor-only structural consolidation and test hardening; no intended route/auth/session/API/EN-CY/a11y behavior change. +status: completed + +Summary: + +- Added `createMappedRouteRendererGroups(...)` in `lib/routing/breadcrumbRendererFactories.js` to compose and return all deterministic mapped renderer groups in one place: + - `simpleRouteRenderersByPath` + - `simpleLinkTextPairRenderersByPath` + - `simpleMyPortalRouteRenderersByPath` + - `caseDetailRouteRenderersByPath` + - `detailAndAccountRouteRenderersByPath` +- Updated `components/breadcrumbs.js` to consume `createMappedRouteRendererGroups(...)` and remove local per-group factory composition boilerplate while preserving existing map precedence and resolver flow. +- Expanded `tests/phase22/breadcrumbs-route-map-structure.test.cjs` to assert: + - grouped renderer maps are composed via `createMappedRouteRendererGroups(...)` + - mapped rendering path explicitly preserves null fallback (`routeRenderer ? routeRenderer() : null`) + +Validation: + +- `npx eslint lib/routing/breadcrumbRendererFactories.js components/breadcrumbs.js tests/phase22/breadcrumbs-route-map-structure.test.cjs tests/phase22/breadcrumb-route-maps-helper.test.cjs tests/phase22/index.test.cjs` -> pass. +- `node tests/phase22/index.test.cjs` -> pass (combined suite; breadcrumbs-route-map 4/4). + +Follow-ups: + +- Next slice option A (larger): extract remaining deterministic inline branches (`/myportal/case`, `/case/id/[incident]` if kept deterministic after constraints review) only if they remain callback/state free. +- Next slice option B (smaller): add one focused assertion in helper-level tests that empty grouped map entries are tolerated without affecting precedence resolution. + ### 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 index 59f890b0..e37eb836 100644 --- a/tests/phase22/breadcrumbs-route-map-structure.test.cjs +++ b/tests/phase22/breadcrumbs-route-map-structure.test.cjs @@ -155,73 +155,17 @@ test("breadcrumbs/component composes mapped routes via imported factories and sh ); assert.strictEqual( - /createSimpleRouteRenderers/.test(source), + /createMappedRouteRendererGroups/.test(source), true, - "Expected breadcrumbs to reference createSimpleRouteRenderers" + "Expected breadcrumbs to reference createMappedRouteRendererGroups" ); assert.strictEqual( - /createSimpleLinkTextPairRenderers/.test(source), - true, - "Expected breadcrumbs to reference createSimpleLinkTextPairRenderers" - ); - - assert.strictEqual( - /createSimpleMyPortalRouteRenderers/.test(source), - true, - "Expected breadcrumbs to reference createSimpleMyPortalRouteRenderers" - ); - - assert.strictEqual( - /createCaseDetailRouteRenderers/.test(source), - true, - "Expected breadcrumbs to reference createCaseDetailRouteRenderers" - ); - - assert.strictEqual( - /createDetailAndAccountRouteRenderers/.test(source), - true, - "Expected breadcrumbs to reference createDetailAndAccountRouteRenderers" - ); - - assert.strictEqual( - /const\s+simpleRouteRenderersByPath\s*=\s*createSimpleRouteRenderers\s*\(/.test( + /const\s+\{[\s\S]*simpleRouteRenderersByPath[\s\S]*detailAndAccountRouteRenderersByPath[\s\S]*\}\s*=\s*createMappedRouteRendererGroups\s*\(/.test( source ), true, - "Expected breadcrumbs to build simple route renderers from factory" - ); - - assert.strictEqual( - /const\s+simpleLinkTextPairRenderersByPath\s*=\s*createSimpleLinkTextPairRenderers\s*\(/.test( - source - ), - true, - "Expected breadcrumbs to build link-text pair renderers from factory" - ); - - assert.strictEqual( - /const\s+simpleMyPortalRouteRenderersByPath\s*=\s*createSimpleMyPortalRouteRenderers\s*\(/.test( - source - ), - true, - "Expected breadcrumbs to build my-portal route renderers from factory" - ); - - assert.strictEqual( - /const\s+caseDetailRouteRenderersByPath\s*=\s*createCaseDetailRouteRenderers\s*\(/.test( - source - ), - true, - "Expected breadcrumbs to build case detail route renderers from factory" - ); - - assert.strictEqual( - /const\s+detailAndAccountRouteRenderersByPath\s*=\s*createDetailAndAccountRouteRenderers\s*\(/.test( - source - ), - true, - "Expected breadcrumbs to build detail and account route renderers from factory" + "Expected breadcrumbs to compose grouped renderer maps via createMappedRouteRendererGroups" ); assert.strictEqual( @@ -256,6 +200,12 @@ test("breadcrumbs/component composes mapped routes via imported factories and sh "Expected reusable renderMappedRoute helper to exist" ); + assert.strictEqual( + source.includes("return routeRenderer ? routeRenderer() : null;"), + true, + "Expected mapped renderer flow to return null for unmapped routes" + ); + assert.strictEqual( source.includes("{renderMappedRoute(pathname)}"), true,