refactor(breadcrumbs): extract representation route-state renderer group

This commit is contained in:
2026-04-09 15:16:01 +01:00
parent 7a60e64a84
commit d3f4dda8af
6 changed files with 160 additions and 38 deletions
+16 -31
View File
@@ -216,6 +216,7 @@ const Breadcrumbs = (props) => {
newAppealRouteRenderersByPath,
callbackRouteRenderersByPath,
stepBackRouteRenderersByPath,
representationRouteRenderersByPath,
caseDetailRouteRenderersByPath,
detailAndAccountRouteRenderersByPath
} = createMappedRouteRendererGroups({
@@ -242,6 +243,20 @@ const Breadcrumbs = (props) => {
onStepBackWithInlineClass: () => {
setCurrentSection(appealType.currentSection - 1);
},
currentView,
hasRepDetails,
repDetails,
showQuestionnaireSection,
onRepresentationSubmitBackFromConfirmation: () => {
setRepresentationSubmit(qcount);
},
onRepresentationSubmitBackToDetails: () => {
setRepresentationSubmit(false);
},
onRepresentationQuestionnaireBack: () => {
setShowQuestionnaireSection(showQuestionnaireSection - 1);
setRepresentationSubmit(false);
},
renderAnchorCrumb,
renderLinkCrumb,
renderTextCrumb,
@@ -259,6 +274,7 @@ const Breadcrumbs = (props) => {
newAppealRouteRenderersByPath,
callbackRouteRenderersByPath,
stepBackRouteRenderersByPath,
representationRouteRenderersByPath,
caseDetailRouteRenderersByPath,
detailAndAccountRouteRenderersByPath
});
@@ -284,37 +300,6 @@ const Breadcrumbs = (props) => {
{isPath("/newappeal") && <></>}
{isPath("/myportal/representation") && (
<>
{currentView.representationSubmit === true &&
currentView.representationSubmitConfirmation !==
true &&
renderBackCrumb(() => {
setRepresentationSubmit(qcount);
})}
{currentView.representationSubmit !== true &&
hasRepDetails &&
repDetails?.representationType !==
"Questionnaire" &&
renderBackCrumb(() => {
setRepresentationSubmit(false);
})}
{currentView.representationSubmit !== true &&
hasRepDetails &&
repDetails?.representationType ===
"Questionnaire" &&
showQuestionnaireSection > 1 &&
renderBackCrumb(() => {
setShowQuestionnaireSection(
showQuestionnaireSection - 1
);
setRepresentationSubmit(false);
})}
</>
)}
{staticTextCrumbLabel &&
renderTextCrumb(staticTextCrumbLabel)}
</ol>
@@ -203,6 +203,36 @@ export const createStepBackRouteRenderers = ({
}
});
export const createRepresentationRouteRenderers = ({
currentView,
hasRepDetails,
repDetails,
showQuestionnaireSection,
onRepresentationSubmitBackFromConfirmation,
onRepresentationSubmitBackToDetails,
onRepresentationQuestionnaireBack,
renderBackCrumb
}) => ({
"/myportal/representation": () => (
<>
{currentView.representationSubmit === true &&
currentView.representationSubmitConfirmation !== true &&
renderBackCrumb(onRepresentationSubmitBackFromConfirmation)}
{currentView.representationSubmit !== true &&
hasRepDetails &&
repDetails?.representationType !== "Questionnaire" &&
renderBackCrumb(onRepresentationSubmitBackToDetails)}
{currentView.representationSubmit !== true &&
hasRepDetails &&
repDetails?.representationType === "Questionnaire" &&
showQuestionnaireSection > 1 &&
renderBackCrumb(onRepresentationQuestionnaireBack)}
</>
)
});
export const createCaseDetailRouteRenderers = ({
t,
router,
@@ -457,6 +487,13 @@ export const createMappedRouteRendererGroups = ({
currentSection,
onStepBack,
onStepBackWithInlineClass,
currentView,
hasRepDetails,
repDetails,
showQuestionnaireSection,
onRepresentationSubmitBackFromConfirmation,
onRepresentationSubmitBackToDetails,
onRepresentationQuestionnaireBack,
renderAnchorCrumb,
renderLinkCrumb,
renderTextCrumb,
@@ -525,6 +562,18 @@ export const createMappedRouteRendererGroups = ({
renderBackCrumb
});
const representationRouteRenderersByPath =
createRepresentationRouteRenderers({
currentView,
hasRepDetails,
repDetails,
showQuestionnaireSection,
onRepresentationSubmitBackFromConfirmation,
onRepresentationSubmitBackToDetails,
onRepresentationQuestionnaireBack,
renderBackCrumb
});
const caseDetailRouteRenderersByPath = createCaseDetailRouteRenderers({
t,
router,
@@ -562,6 +611,7 @@ export const createMappedRouteRendererGroups = ({
newAppealRouteRenderersByPath,
callbackRouteRenderersByPath,
stepBackRouteRenderersByPath,
representationRouteRenderersByPath,
caseDetailRouteRenderersByPath,
detailAndAccountRouteRenderersByPath
};
+2
View File
@@ -25,6 +25,7 @@ const buildBreadcrumbRendererMaps = ({
newAppealRouteRenderersByPath,
callbackRouteRenderersByPath,
stepBackRouteRenderersByPath,
representationRouteRenderersByPath,
caseDetailRouteRenderersByPath,
detailAndAccountRouteRenderersByPath
}) => [
@@ -34,6 +35,7 @@ const buildBreadcrumbRendererMaps = ({
newAppealRouteRenderersByPath,
callbackRouteRenderersByPath,
stepBackRouteRenderersByPath,
representationRouteRenderersByPath,
caseDetailRouteRenderersByPath,
detailAndAccountRouteRenderersByPath
];
+49
View File
@@ -784,6 +784,55 @@ Follow-ups:
- Next larger slice candidate remains `/myportal/representation` callback/state branch extraction only if side-effects can be injected/parity-preserved cleanly.
### CL-22541-Y: breadcrumbs larger slice — representation route-group extraction (`/myportal/representation`)
date: 2026-04-09
author: Cline
scope: `lib/routing/{breadcrumbRendererFactories,breadcrumbRouteMaps}.js`, `components/breadcrumbs.js`, `tests/phase22/{breadcrumb-route-maps-helper,breadcrumbs-route-map-structure}.test.cjs`
type: change
rationale: Execute the queued larger follow-up slice by extracting `/myportal/representation` callback/state breadcrumb branches into a dedicated mapped route group with explicit callback injection while preserving questionnaire/submit side-effects.
impact: Structural refactor with preserved representation back-link state behavior; no intended route/auth/session/API/EN-CY/a11y behavior change.
status: completed
Summary:
- Added `createRepresentationRouteRenderers(...)` in `lib/routing/breadcrumbRendererFactories.js` mapping:
- `/myportal/representation`
- Preserved existing representation-state semantics in mapped handler:
- submit/back from confirmation (`setRepresentationSubmit(qcount)`)
- non-questionnaire back path (`setRepresentationSubmit(false)`)
- questionnaire step-back path (`setShowQuestionnaireSection(showQuestionnaireSection - 1)` + `setRepresentationSubmit(false)`)
- Introduced explicit injected state/callback dependencies into grouped composition:
- `currentView`, `hasRepDetails`, `repDetails`, `showQuestionnaireSection`
- `onRepresentationSubmitBackFromConfirmation`
- `onRepresentationSubmitBackToDetails`
- `onRepresentationQuestionnaireBack`
- Extended `buildBreadcrumbRendererMaps(...)` in `lib/routing/breadcrumbRouteMaps.js` with `representationRouteRenderersByPath` and updated precedence order:
- simple -> link-text -> myportal -> new-appeal -> callback -> step-back -> representation -> case detail -> detail/account.
- Updated `components/breadcrumbs.js`:
- pass representation state/callback injections into `createMappedRouteRendererGroups(...)`
- include `representationRouteRenderersByPath` in map builder call
- remove inline `isPath("/myportal/representation")` branch block.
- Expanded tests:
- `tests/phase22/breadcrumb-route-maps-helper.test.cjs`
- updated map-count/order assertions to include representation group (9 total)
- updated map build fixtures to include `representationRouteRenderersByPath`
- `tests/phase22/breadcrumbs-route-map-structure.test.cjs`
- assert representation factory export + mapped route presence
- assert grouped destructuring includes representation map
- assert inline representation branch removal
- assert updated map-order invariant includes representation group.
Validation:
- `npx eslint lib/routing/breadcrumbRendererFactories.js lib/routing/breadcrumbRouteMaps.js components/breadcrumbs.js tests/phase22/breadcrumb-route-maps-helper.test.cjs tests/phase22/breadcrumbs-route-map-structure.test.cjs tests/phase22/index.test.cjs` -> pass.
- `node tests/phase22/index.test.cjs` -> pass (combined suite).
Follow-ups:
- Next smaller slice candidate: helper-level assertion for missing/undefined representation dependency values yielding no renderer output regressions.
- Next larger slice candidate: evaluate whether remaining `/newappeal` edge branch can be absorbed into mapped grouping without reducing readability or explicitness.
### CL-00X: 22500 `components/elements/index.js` Phase 1 helper extraction
date: 2026-04-07
@@ -66,6 +66,9 @@ test("buildBreadcrumbRendererMaps returns grouped maps in explicit precedence or
const stepBackRouteRenderersByPath = {
"/iota": () => "iota"
};
const representationRouteRenderersByPath = {
"/kappa": () => "kappa"
};
const caseDetailRouteRenderersByPath = {
"/delta": () => "delta"
};
@@ -80,12 +83,13 @@ test("buildBreadcrumbRendererMaps returns grouped maps in explicit precedence or
newAppealRouteRenderersByPath,
callbackRouteRenderersByPath,
stepBackRouteRenderersByPath,
representationRouteRenderersByPath,
caseDetailRouteRenderersByPath,
detailAndAccountRouteRenderersByPath
});
assert.ok(Array.isArray(result), "Expected grouped maps array");
assert.strictEqual(result.length, 8, "Expected eight grouped route maps");
assert.strictEqual(result.length, 9, "Expected nine grouped route maps");
assert.strictEqual(
result[0],
simpleRouteRenderersByPath,
@@ -118,13 +122,18 @@ test("buildBreadcrumbRendererMaps returns grouped maps in explicit precedence or
);
assert.strictEqual(
result[6],
caseDetailRouteRenderersByPath,
"Expected case detail route renderers seventh"
representationRouteRenderersByPath,
"Expected representation route renderers seventh"
);
assert.strictEqual(
result[7],
caseDetailRouteRenderersByPath,
"Expected case detail route renderers eighth"
);
assert.strictEqual(
result[8],
detailAndAccountRouteRenderersByPath,
"Expected detail and account route renderers eighth"
"Expected detail and account route renderers ninth"
);
});
@@ -146,6 +155,7 @@ test("resolveMappedRouteRenderer returns renderer from first matching grouped ma
newAppealRouteRenderersByPath: {},
callbackRouteRenderersByPath: {},
stepBackRouteRenderersByPath: {},
representationRouteRenderersByPath: {},
caseDetailRouteRenderersByPath: {},
detailAndAccountRouteRenderersByPath: {}
});
@@ -175,6 +185,7 @@ test("resolveMappedRouteRenderer returns renderer from later grouped map when ea
caseDetailRouteRenderersByPath: {},
callbackRouteRenderersByPath: {},
stepBackRouteRenderersByPath: {},
representationRouteRenderersByPath: {},
detailAndAccountRouteRenderersByPath: {
"/account/personaldetails": detailRenderer
}
@@ -202,6 +213,7 @@ test("resolveMappedRouteRenderer returns null for unmapped path", () => {
newAppealRouteRenderersByPath: {},
callbackRouteRenderersByPath: {},
stepBackRouteRenderersByPath: {},
representationRouteRenderersByPath: {},
caseDetailRouteRenderersByPath: {},
detailAndAccountRouteRenderersByPath: {}
});
@@ -230,6 +242,7 @@ test("resolveMappedRouteRenderer returns null for empty, whitespace, and non-exa
newAppealRouteRenderersByPath: {},
callbackRouteRenderersByPath: {},
stepBackRouteRenderersByPath: {},
representationRouteRenderersByPath: {},
caseDetailRouteRenderersByPath: {},
detailAndAccountRouteRenderersByPath: {}
});
@@ -282,6 +295,7 @@ test("resolveMappedRouteRenderer skips invalid map entries and non-function rend
newAppealRouteRenderersByPath: undefined,
callbackRouteRenderersByPath: {},
stepBackRouteRenderersByPath: {},
representationRouteRenderersByPath: {},
caseDetailRouteRenderersByPath: {},
detailAndAccountRouteRenderersByPath: {}
});
@@ -54,6 +54,12 @@ test("breadcrumbs/factory module includes expected mapped route definitions", as
"Expected step-back route renderer factory export"
);
assert.strictEqual(
source.includes("export const createRepresentationRouteRenderers ="),
true,
"Expected representation route renderer factory export"
);
assert.strictEqual(
source.includes("export const createCaseDetailRouteRenderers ="),
true,
@@ -132,6 +138,12 @@ test("breadcrumbs/factory module includes expected mapped route definitions", as
"Expected /myportal/[appealtypes] to be mapped in createStepBackRouteRenderers"
);
assert.strictEqual(
source.includes('"/myportal/representation": () => ('),
true,
"Expected /myportal/representation to be mapped in createRepresentationRouteRenderers"
);
assert.strictEqual(
source.includes('"/case/[ticketnumber]": () =>'),
true,
@@ -221,7 +233,7 @@ test("breadcrumbs/component composes mapped routes via imported factories and sh
);
assert.strictEqual(
/const\s+\{[\s\S]*simpleRouteRenderersByPath[\s\S]*newAppealRouteRenderersByPath[\s\S]*callbackRouteRenderersByPath[\s\S]*stepBackRouteRenderersByPath[\s\S]*detailAndAccountRouteRenderersByPath[\s\S]*\}\s*=\s*createMappedRouteRendererGroups\s*\(/.test(
/const\s+\{[\s\S]*simpleRouteRenderersByPath[\s\S]*newAppealRouteRenderersByPath[\s\S]*callbackRouteRenderersByPath[\s\S]*stepBackRouteRenderersByPath[\s\S]*representationRouteRenderersByPath[\s\S]*detailAndAccountRouteRenderersByPath[\s\S]*\}\s*=\s*createMappedRouteRendererGroups\s*\(/.test(
source
),
true,
@@ -320,6 +332,12 @@ test("breadcrumbs/component composes mapped routes via imported factories and sh
"Expected /myportal/[appealtypes] explicit branch to be removed after step-back mapping"
);
assert.strictEqual(
source.includes('isPath("/myportal/representation")'),
false,
"Expected /myportal/representation explicit branch to be removed after representation mapping"
);
assert.strictEqual(
source.includes('isPath("/myportal/case/[ticketnumber]")'),
false,
@@ -405,6 +423,9 @@ test("breadcrumbs/map builder preserves route map ordering", async () => {
const stepBackOrderIndex = mapBuilderSlice.indexOf(
"stepBackRouteRenderersByPath"
);
const representationOrderIndex = mapBuilderSlice.indexOf(
"representationRouteRenderersByPath"
);
const caseDetailOrderIndex = mapBuilderSlice.indexOf(
"caseDetailRouteRenderersByPath"
);
@@ -419,10 +440,11 @@ test("breadcrumbs/map builder preserves route map ordering", async () => {
newAppealOrderIndex > simpleMyPortalOrderIndex &&
callbackOrderIndex > newAppealOrderIndex &&
stepBackOrderIndex > callbackOrderIndex &&
caseDetailOrderIndex > stepBackOrderIndex &&
representationOrderIndex > stepBackOrderIndex &&
caseDetailOrderIndex > representationOrderIndex &&
detailAndAccountOrderIndex > caseDetailOrderIndex,
true,
"Expected map builder call to preserve explicit route-map ordering (simple -> link-text -> myportal -> new-appeal -> callback -> step-back -> case detail -> detail/account)"
"Expected map builder call to preserve explicit route-map ordering (simple -> link-text -> myportal -> new-appeal -> callback -> step-back -> representation -> case detail -> detail/account)"
);
});