diff --git a/components/breadcrumbs.js b/components/breadcrumbs.js index 0e4d513a..672f9a0e 100644 --- a/components/breadcrumbs.js +++ b/components/breadcrumbs.js @@ -166,6 +166,62 @@ const Breadcrumbs = (props) => { return simpleLinkTextPairRenderersByPath[path] || null; }; + const getSimpleMyPortalRouteRenderer = (path) => { + const simpleMyPortalRouteRenderersByPath = { + "/myportal/searchresults": () => ( + <> + {renderAnchorCrumb( + myPortalHref, + t("common:breadcrumb-my-portal") + )} + {renderTextCrumb(t("common:breadcrumb-search-results"))} + + ), + "/myportal/addresssearch": () => + renderMyPortalSectionCrumbs( + t("common:breadcrumb-address-search") + ), + "/myportal/advancedsearch": () => + renderMyPortalSectionCrumbs( + t("common:breadcrumb-advanced-search") + ), + "/myportal/contactus": () => + renderMyPortalSectionCrumbs( + t("common:footer-contact-us-link-label") + ), + "/myportal/viewall": () => + renderMyPortalSectionCrumbs( + getViewAllLabel( + currentViewState.viewKey || router.query.key + ) + ) + }; + + return simpleMyPortalRouteRenderersByPath[path] || null; + }; + + const resolveMappedRouteRenderer = (path) => { + const mappedResolvers = [ + getSimpleRouteRenderer, + getSimpleLinkTextPairRenderer, + getSimpleMyPortalRouteRenderer + ]; + + for (const resolveRenderer of mappedResolvers) { + const routeRenderer = resolveRenderer(path); + if (routeRenderer) { + return routeRenderer; + } + } + + return null; + }; + + const renderMappedRoute = (path, resolver = resolveMappedRouteRenderer) => { + const routeRenderer = resolver(path); + return routeRenderer ? routeRenderer() : null; + }; + const renderMyPortalCrumb = () => (
  • @@ -261,43 +317,7 @@ const Breadcrumbs = (props) => { t("common:service-name-breadcrumb") )} - {(() => { - const renderSimpleRoute = - getSimpleRouteRenderer(pathname); - return renderSimpleRoute - ? renderSimpleRoute() - : null; - })()} - {(() => { - const renderSimpleLinkTextPair = - getSimpleLinkTextPairRenderer(pathname); - return renderSimpleLinkTextPair - ? renderSimpleLinkTextPair() - : null; - })()} - {isPath("/myportal/searchresults") && ( - <> - {renderAnchorCrumb( - myPortalHref, - t("common:breadcrumb-my-portal") - )} -
  • - {t("common:breadcrumb-search-results")} -
  • - - )} - {isPath("/myportal/addresssearch") && - renderMyPortalSectionCrumbs( - t("common:breadcrumb-address-search") - )} - {isPath("/myportal/advancedsearch") && - renderMyPortalSectionCrumbs( - t("common:breadcrumb-advanced-search") - )} - {isPath("/myportal/contactus") && - renderMyPortalSectionCrumbs( - t("common:footer-contact-us-link-label") - )} + {renderMappedRoute(pathname)} {isPath("/myportal/advancedsearchresults") && ( <> {renderMyPortalCrumb()} @@ -386,17 +406,6 @@ const Breadcrumbs = (props) => { )} - {isPath("/myportal/viewall") && ( - <> - {renderMyPortalCrumb()} -
  • - {getViewAllLabel( - currentViewState.viewKey || - router.query.key - )} -
  • - - )} {isPath("/myportal/case") && ( <> {renderLinkCrumb( diff --git a/memory-bank/change-log.md b/memory-bank/change-log.md index be7d909b..1fab1697 100644 --- a/memory-bank/change-log.md +++ b/memory-bank/change-log.md @@ -392,6 +392,43 @@ 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-22541-N: breadcrumbs unified mapped-route resolver + widened structural guardrails + +date: 2026-04-09 +author: Cline +scope: `components/breadcrumbs.js`, `tests/phase22/breadcrumbs-route-map-structure.test.cjs` +type: change +rationale: Execute the requested wider next slice by removing duplicated mapped-route invocation patterns, introducing a single top-level mapped resolver with explicit precedence, and migrating an additional safe my-portal route group into map-driven rendering while preserving explicit dynamic/callback branches. +impact: Refactor-only structural consolidation/readability improvement with widened mapped-route coverage and stronger structure guardrail tests; no intended route/auth/session/API/EN-CY/a11y behavior change. +status: completed + +Summary: + +- Added `getSimpleMyPortalRouteRenderer(path)` in `components/breadcrumbs.js` and migrated additional safe map-friendly routes: + - `/myportal/searchresults` + - `/myportal/addresssearch` + - `/myportal/advancedsearch` + - `/myportal/contactus` + - `/myportal/viewall` +- Added `resolveMappedRouteRenderer(path)` to unify map precedence under one resolver. +- Added `renderMappedRoute(path, resolver?)` helper and replaced duplicated inline map IIFE invocations with a single call: + - `{renderMappedRoute(pathname)}` +- Kept dynamic/stateful callback branches explicit and unchanged, especially `/myportal/case/id/[incident]` (`router.back()`) and other callback/state driven branches. +- Expanded `tests/phase22/breadcrumbs-route-map-structure.test.cjs` to assert: + - new my-portal route-map entries are present + - unified resolver/helper signatures are present + - single mapped-route invocation is used in JSX + - dynamic callback branch remains explicit. + +Validation: + +- `npx eslint components/breadcrumbs.js tests/phase22/breadcrumbs-route-map-structure.test.cjs tests/phase22/index.test.cjs` -> pass. +- `node tests/phase22/index.test.cjs` -> pass (combined suite; breadcrumbs-route-map tests 3/3). + +Follow-ups: + +- Next wider slice candidate: move a second safe myportal results pair group (`/myportal/advancedsearchresults`, `/myportal/addresssearchresults`) into the unified mapped resolver where callback-free, then consider splitting resolver maps into a dedicated local constant section for improved scanability. + ### 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 c3a6f99c..9727ca6c 100644 --- a/tests/phase22/breadcrumbs-route-map-structure.test.cjs +++ b/tests/phase22/breadcrumbs-route-map-structure.test.cjs @@ -30,6 +30,42 @@ test("breadcrumbs/simple-link-text map includes address and advanced search resu true, "Expected /advancedsearchresults to be mapped in getSimpleLinkTextPairRenderer" ); + + assert.strictEqual( + source.includes('"/myportal/searchresults": () => ('), + true, + "Expected /myportal/searchresults to be mapped in getSimpleMyPortalRouteRenderer" + ); + + assert.strictEqual( + source.includes('"/myportal/viewall": () =>'), + true, + "Expected /myportal/viewall to be mapped in getSimpleMyPortalRouteRenderer" + ); +}); + +test("breadcrumbs/merged resolver renders mapped routes through single helper invocation", async () => { + const source = loadBreadcrumbSource(); + + assert.strictEqual( + source.includes("const resolveMappedRouteRenderer = (path) =>"), + true, + "Expected unified mapped route resolver helper to exist" + ); + + assert.strictEqual( + source.includes( + "const renderMappedRoute = (path, resolver = resolveMappedRouteRenderer) =>" + ), + true, + "Expected reusable renderMappedRoute helper to exist" + ); + + assert.strictEqual( + source.includes("{renderMappedRoute(pathname)}"), + true, + "Expected breadcrumb JSX to use single mapped route render invocation" + ); }); test("breadcrumbs/dynamic callback branch remains explicit for myportal case incident route", async () => {