22541 compose mapped breadcrumb groups in factory and add null fallback guard

This commit is contained in:
2026-04-09 14:30:03 +01:00
parent 96946ed205
commit 8edd23d621
4 changed files with 153 additions and 118 deletions
+21 -58
View File
@@ -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,
@@ -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
};
};
+33
View File
@@ -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
@@ -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,