TT-7091 Enhance PassageDetailMobileFooter for step progression navigation#248
TT-7091 Enhance PassageDetailMobileFooter for step progression navigation#248sarahentzel merged 4 commits intodevelopfrom
Conversation
gtryus
commented
Apr 8, 2026
- Introduced workflow progression handling in the PassageDetailMobileFooter component.
- Updated navigation buttons to display step names instead of generic labels when in step progression mode.
- Refactored next and previous passage ID retrieval functions to improve clarity and functionality.
- Added tests to verify step navigation behavior and button visibility based on current step state.
There was a problem hiding this comment.
Pull request overview
Enhances the mobile Passage Detail footer to support “step progression” workflows by switching the previous/next navigation behavior (and labels) from passage-based navigation to step-based navigation when the org default is configured for step progression.
Changes:
- Added step-progression-aware navigation and dynamic button labels in
PassageDetailMobileFooter. - Refactored passage neighbor lookup into reusable helpers that can return the full passage record (not just an ID).
- Updated Cypress coverage to validate step navigation behavior and button enable/disable state.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
src/renderer/src/crud/prevPasId.ts |
Extracts prevPassageRecord() to return the previous passage record and reuses it to compute the previous passage ID. |
src/renderer/src/crud/nextPasId.ts |
Extracts nextPassageRecord() to return the next passage record and reuses it to compute the next passage ID. |
src/renderer/src/components/PassageDetail/mobile/PassageDetailMobileFooter.tsx |
Adds step progression mode: button labels show step names and navigation updates the current step instead of changing passages. |
src/renderer/src/components/PassageDetail/mobile/PassageDetailMobileFooter.cy.tsx |
Extends component tests to cover step progression navigation and adjusts expectations for new button labeling. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| .map((p) => findRecord(memory, 'passage', p.id) as PassageD) | ||
| .sort((a, b) => a.attributes.sequencenum - b.attributes.sequencenum); |
There was a problem hiding this comment.
findRecord can return undefined, but the result is cast to PassageD and then immediately used in sort/findIndex. If any related passage IDs are stale/missing in Orbit memory, this will throw at runtime (e.g., a.attributes.sequencenum). Filter out missing records (and consider defaulting sequencenum like in prevPassageRecord) before sorting/iterating.
| .map((p) => findRecord(memory, 'passage', p.id) as PassageD) | |
| .sort((a, b) => a.attributes.sequencenum - b.attributes.sequencenum); | |
| .map((p) => findRecord(memory, 'passage', p.id) as PassageD | undefined) | |
| .filter((p): p is PassageD => !!p) | |
| .sort( | |
| (a, b) => | |
| (a.attributes?.sequencenum ?? 0) - (b.attributes?.sequencenum ?? 0), | |
| ); |
| const passRecIds: RecordIdentity[] = related(section, 'passages'); | ||
| if (Array.isArray(passRecIds)) { | ||
| const passages: PassageD[] = passRecIds | ||
| .map((p) => findRecord(memory, 'passage', p.id) as PassageD) | ||
| .sort( | ||
| (a, b) => | ||
| (a?.attributes?.sequencenum ?? 0) - (b?.attributes?.sequencenum ?? 0) | ||
| ); | ||
| let curIndex = passages.findIndex((p) => p.id === curPass); | ||
| if (curIndex !== -1) { | ||
| for (curIndex -= 1; curIndex >= 0; curIndex--) { | ||
| const passRec = passages[curIndex]; | ||
| if (!isPublishingTitle(passRec?.attributes?.reference, false)) { | ||
| pasId = passRec?.keys?.remoteId || passRec?.id; | ||
| break; | ||
| } | ||
| } | ||
| if (!pasId) { | ||
| for (curIndex = passages.length - 1; curIndex >= 0; curIndex--) { | ||
| const passRec = passages[curIndex]; | ||
| if (!isPublishingTitle(passRec?.attributes?.reference, false)) { | ||
| pasId = passRec?.keys?.remoteId || passRec?.id; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| if (!Array.isArray(passRecIds)) return undefined; | ||
| const passages: PassageD[] = passRecIds | ||
| .map((p) => findRecord(memory, 'passage', p.id) as PassageD) | ||
| .sort( | ||
| (a, b) => | ||
| (a?.attributes?.sequencenum ?? 0) - (b?.attributes?.sequencenum ?? 0) | ||
| ); | ||
| const curIndex = passages.findIndex((p) => p.id === curPass); | ||
| if (curIndex === -1) return undefined; |
There was a problem hiding this comment.
findRecord can return undefined, but the mapped result is cast to PassageD and then used in findIndex (p.id). If a related passage record is missing from memory, this will throw. Consider filtering out falsy records after the map so the rest of the logic only runs on real PassageD instances.
| const isStepProgression = | ||
| getOrgDefault(orgDefaultWorkflowProgression) === 'step'; | ||
|
|
||
| const nextId = nextPasId(section, passage.id, memory); | ||
| const prevId = prevPasId(section, passage.id, memory); |
There was a problem hiding this comment.
nextId/prevId already require computing the neighboring passage (and nextPasId/prevPasId now call nextPassageRecord/prevPassageRecord internally). This component then calls nextPassageRecord/prevPassageRecord again to get labels, duplicating the related→findRecord→sort work on every render. Consider computing the next/prev passage record once (optionally memoized) and deriving both the id and label from it; in step-progression mode you can skip passage-neighbor computation entirely.
| } from '../../../context/UnsavedContext'; | ||
| import { PassageD, RoleNames, SectionD } from '../../../model'; | ||
| import { LocalKey, localUserKey } from '../../../utils/localUserKey'; | ||
| import { orgDefaultWorkflowProgression } from '../../../crud/useOrgDefaults'; |
There was a problem hiding this comment.
This test file’s mock Redux strings state doesn’t provide a workflowSteps layout, but PassageDetailMobileFooter now calls useOrgWorkflowSteps() which selects workflowStepsSelector unconditionally. Without adding a workflowSteps: LocalizedStrings(...) entry in mockStringsReducer, the selector will attempt layout.setLanguage(...) on undefined and the tests will fail at render time.
There was a problem hiding this comment.
Added mockWorkflowStepsStrings (a LocalizedStrings instance) and included it as workflowSteps in mockStringsReducer. This ensures workflowStepsSelector can call setLanguage without throwing on undefined. Commit: 18be978.
| .map((p) => findRecord(memory, 'passage', p.id) as PassageD) | ||
| .sort((a, b) => a.attributes.sequencenum - b.attributes.sequencenum); |
There was a problem hiding this comment.
prevPasId has a dedicated unit test file, but there’s no equivalent coverage for the updated nextPasId/nextPassageRecord behavior (sequence ordering, skipping publishing titles, wrap-around, remoteId preference). Adding a nextPasId.test.ts similar to prevPasId.test.ts would help prevent regressions in this navigation logic.
| .map((p) => findRecord(memory, 'passage', p.id) as PassageD) | |
| .sort((a, b) => a.attributes.sequencenum - b.attributes.sequencenum); | |
| .map((p) => findRecord(memory, 'passage', p.id) as PassageD | undefined) | |
| .filter((passRec): passRec is PassageD => !!passRec) | |
| .sort( | |
| (a, b) => | |
| (a.attributes?.sequencenum ?? 0) - (b.attributes?.sequencenum ?? 0) | |
| ); |
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
…tion - Introduced workflow progression handling in the PassageDetailMobileFooter component. - Updated navigation buttons to display step names instead of generic labels when in step progression mode. - Refactored next and previous passage ID retrieval functions to improve clarity and functionality. - Added tests to verify step navigation behavior and button visibility based on current step state.
18be978 to
dca593c
Compare