Improve the end-user flow preview panel - #4161
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughChangesFlow simulation preview
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant SimulationStepPreview
participant SimulationScreenSketch
participant IframeContent
participant useFlowSimulation
participant ReactFlowViewport
User->>SimulationStepPreview: open simulation step
SimulationStepPreview->>SimulationScreenSketch: render interactive screen options
User->>SimulationScreenSketch: hover or click rich-text option
SimulationScreenSketch->>useFlowSimulation: preview(option) or choose(option)
User->>IframeContent: click wired rich-text anchor
IframeContent->>SimulationStepPreview: submit action ref
useFlowSimulation->>ReactFlowViewport: focus current and target nodes
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/apps/console/src/features/flows/hooks/useFlowSimulation.ts`:
- Around line 263-276: Guard the deferred restore callback in the
hasActivePreviewRef flow by re-checking followCameraRef.current before calling
fitView. If follow-camera mode was disabled while the PREVIEW_RESTORE_DELAY_MS
timer was pending, clear the timer state and return without moving the camera;
preserve the existing restore behavior when follow-camera remains enabled.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 749d16e6-cdef-4efc-981c-f29f685a9274
📒 Files selected for processing (10)
frontend/apps/console/src/components/GatePreview/mocks/__tests__/buildPreviewMock.test.tsfrontend/apps/console/src/components/GatePreview/mocks/buildPreviewMock.tsfrontend/apps/console/src/features/flows/components/visual-flow/SimulationOptionsFooter.tsxfrontend/apps/console/src/features/flows/components/visual-flow/SimulationScreenSketch.tsxfrontend/apps/console/src/features/flows/components/visual-flow/SimulationStepPreview.tsxfrontend/apps/console/src/features/flows/components/visual-flow/__tests__/SimulationScreenSketch.test.tsxfrontend/apps/console/src/features/flows/components/visual-flow/__tests__/SimulationStepPreview.test.tsxfrontend/apps/console/src/features/flows/hooks/__tests__/useFlowSimulation.test.tsfrontend/apps/console/src/features/flows/hooks/useFlowSimulation.tsfrontend/packages/i18n/src/locales/en-US.ts
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
43a5184 to
0b30ff2
Compare
0b30ff2 to
6a16d9f
Compare
There was a problem hiding this comment.
Pull request overview
This PR improves the ThunderID Console’s end-user flow preview panel UX by enhancing camera behavior, handling background (no-screen) steps more appropriately, reducing duplicated action affordances, and adding persistence + accessibility support.
Changes:
- Enhance simulation preview camera behavior to fit both current + hovered transition targets, with a grace-period restore and cancellation semantics.
- Improve preview rendering and interaction fidelity: background-step layout (no preview chrome), dedup footer options that are triggerable in-screen, and make rich-text links actionable in both sketch + themed (iframe) previews.
- Persist preview preferences (device + application) and add keyboard navigation for the preview panel.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/packages/i18n/src/locales/en-US.ts | Adds new preview-panel strings for named background steps and call-step labeling. |
| frontend/apps/console/src/features/flows/utils/gatePreviewTransforms.ts | Adds a transform to re-attach rich-text action.ref from simulation wiring so gate link clicks work reliably. |
| frontend/apps/console/src/features/flows/utils/tests/gatePreviewTransforms.test.ts | Adds unit tests for rich-text action.ref re-attachment, including nested components. |
| frontend/apps/console/src/features/flows/hooks/useFlowSimulation.ts | Implements hover preview camera fit + delayed restore behavior with cancellation and follow-camera gating. |
| frontend/apps/console/src/features/flows/hooks/tests/useFlowSimulation.test.ts | Adds fake-timer tests covering camera fit/restore/cancel behavior and follow-camera off behavior. |
| frontend/apps/console/src/features/flows/constants/simulationPreviewConstants.ts | Introduces localStorage keys for persisted preview device/app selections. |
| frontend/apps/console/src/features/flows/components/visual-flow/SimulationStepPreview.tsx | Adds preference persistence, background-step rendering, footer de-dup logic, richer gate wiring resolution, and keyboard navigation. |
| frontend/apps/console/src/features/flows/components/visual-flow/SimulationScreenSketch.tsx | Makes rich-text links clickable/hover-previewable in sketch mode without allowing navigation away. |
| frontend/apps/console/src/features/flows/components/visual-flow/SimulationOptionsFooter.tsx | Adds top/bottom placement styling and attempts to expose a DOM ref for keyboard navigation. |
| frontend/apps/console/src/features/flows/components/visual-flow/tests/SimulationStepPreview.test.tsx | Expands tests for nested gate submit resolution, background-step UI, footer de-dup, keyboard behavior, and persistence. |
| frontend/apps/console/src/features/flows/components/visual-flow/tests/SimulationScreenSketch.test.tsx | Adds tests for sketch rich-text link click/hover preview behavior. |
| frontend/apps/console/src/components/GatePreview/mocks/buildPreviewMock.ts | Wraps the app logo in a container so alignment matches the real gate. |
| frontend/apps/console/src/components/GatePreview/mocks/tests/buildPreviewMock.test.ts | Updates component lookup to search nested mock components. |
| frontend/apps/console/src/components/GatePreview/IframeContent.tsx | Adds iframe-safe click handling for wired rich-text links via data-action-ref. |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
frontend/apps/console/src/features/flows/utils/gatePreviewTransforms.ts (1)
172-190: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winPreserve referential equality when the component tree is unchanged.
The current implementation maps over
listandnext.componentsunconditionally, returning new array and object instances on every call—even if no rich-text actions required updates. Since this transform is invoked during React renders (e.g., inSimulationStepPreview), generating fresh references for unchanged subtrees can break memoization and trigger unnecessary deep re-renders of the preview container.Consider tracking changes and returning the original references when no modifications are made.
⚡ Proposed optimization
export function withRichTextActionRefs( list: PreviewComponent[], optionsByComponentId: Map<string, string>, ): PreviewComponent[] { - return list.map((component: PreviewComponent) => { + let hasChanges = false; + const nextList = list.map((component: PreviewComponent) => { let next = component; const ref = component.type === ElementTypes.RichText ? optionsByComponentId.get(component.id) : undefined; if (ref && (component as {action?: {ref?: string}}).action?.ref !== ref) { next = {...component, action: {...(component as {action?: object}).action, ref}} as PreviewComponent; } if (next.components?.length) { - next = { - ...next, - components: withRichTextActionRefs(next.components as PreviewComponent[], optionsByComponentId), - }; + const nextComponents = withRichTextActionRefs(next.components as PreviewComponent[], optionsByComponentId); + if (nextComponents !== next.components) { + next = { + ...next, + components: nextComponents, + }; + } } + if (next !== component) { + hasChanges = true; + } return next; }); + return hasChanges ? nextList : list; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/apps/console/src/features/flows/utils/gatePreviewTransforms.ts` around lines 172 - 190, Update withRichTextActionRefs to preserve referential equality: track whether any component or descendant changes, reuse unchanged component and components-array references, and return the original list when the entire tree is unchanged. Only create new arrays and component objects when a rich-text action reference or nested subtree is modified.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@frontend/apps/console/src/features/flows/utils/gatePreviewTransforms.ts`:
- Around line 172-190: Update withRichTextActionRefs to preserve referential
equality: track whether any component or descendant changes, reuse unchanged
component and components-array references, and return the original list when the
entire tree is unchanged. Only create new arrays and component objects when a
rich-text action reference or nested subtree is modified.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 7bc0c25b-c1c3-45a4-bfaf-d19b17360edd
📒 Files selected for processing (14)
frontend/apps/console/src/components/GatePreview/IframeContent.tsxfrontend/apps/console/src/components/GatePreview/mocks/__tests__/buildPreviewMock.test.tsfrontend/apps/console/src/components/GatePreview/mocks/buildPreviewMock.tsfrontend/apps/console/src/features/flows/components/visual-flow/SimulationOptionsFooter.tsxfrontend/apps/console/src/features/flows/components/visual-flow/SimulationScreenSketch.tsxfrontend/apps/console/src/features/flows/components/visual-flow/SimulationStepPreview.tsxfrontend/apps/console/src/features/flows/components/visual-flow/__tests__/SimulationScreenSketch.test.tsxfrontend/apps/console/src/features/flows/components/visual-flow/__tests__/SimulationStepPreview.test.tsxfrontend/apps/console/src/features/flows/constants/simulationPreviewConstants.tsfrontend/apps/console/src/features/flows/hooks/__tests__/useFlowSimulation.test.tsfrontend/apps/console/src/features/flows/hooks/useFlowSimulation.tsfrontend/apps/console/src/features/flows/utils/__tests__/gatePreviewTransforms.test.tsfrontend/apps/console/src/features/flows/utils/gatePreviewTransforms.tsfrontend/packages/i18n/src/locales/en-US.ts
🚧 Files skipped from review as they are similar to previous changes (11)
- frontend/packages/i18n/src/locales/en-US.ts
- frontend/apps/console/src/features/flows/components/visual-flow/tests/SimulationScreenSketch.test.tsx
- frontend/apps/console/src/features/flows/constants/simulationPreviewConstants.ts
- frontend/apps/console/src/components/GatePreview/mocks/tests/buildPreviewMock.test.ts
- frontend/apps/console/src/components/GatePreview/mocks/buildPreviewMock.ts
- frontend/apps/console/src/features/flows/components/visual-flow/SimulationOptionsFooter.tsx
- frontend/apps/console/src/features/flows/hooks/tests/useFlowSimulation.test.ts
- frontend/apps/console/src/features/flows/components/visual-flow/SimulationScreenSketch.tsx
- frontend/apps/console/src/features/flows/hooks/useFlowSimulation.ts
- frontend/apps/console/src/features/flows/components/visual-flow/tests/SimulationStepPreview.test.tsx
- frontend/apps/console/src/features/flows/components/visual-flow/SimulationStepPreview.tsx
d8ece21 to
a31c4ad
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@frontend/apps/console/src/features/flows/components/visual-flow/SimulationScreenSketch.tsx`:
- Around line 91-111: Refine the rich-text link interactions in the rendered
Typography block: replace root-level onMouseEnter/onMouseLeave behavior with
delegated onMouseOver/onMouseOut that previews only when the related target is
an embedded anchor, and add onFocus/onBlur handlers using React FocusEvent to
preview only focused links. Preserve onChoose for wired link clicks, explicitly
use cursor 'default' when option is absent, and update the related test to hover
the link text rather than the surrounding block.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: d278eb86-5dcf-4929-96dd-b46c687e018b
📒 Files selected for processing (14)
frontend/apps/console/src/components/GatePreview/IframeContent.tsxfrontend/apps/console/src/components/GatePreview/mocks/__tests__/buildPreviewMock.test.tsfrontend/apps/console/src/components/GatePreview/mocks/buildPreviewMock.tsfrontend/apps/console/src/features/flows/components/visual-flow/SimulationOptionsFooter.tsxfrontend/apps/console/src/features/flows/components/visual-flow/SimulationScreenSketch.tsxfrontend/apps/console/src/features/flows/components/visual-flow/SimulationStepPreview.tsxfrontend/apps/console/src/features/flows/components/visual-flow/__tests__/SimulationScreenSketch.test.tsxfrontend/apps/console/src/features/flows/components/visual-flow/__tests__/SimulationStepPreview.test.tsxfrontend/apps/console/src/features/flows/constants/simulationPreviewConstants.tsfrontend/apps/console/src/features/flows/hooks/__tests__/useFlowSimulation.test.tsfrontend/apps/console/src/features/flows/hooks/useFlowSimulation.tsfrontend/apps/console/src/features/flows/utils/__tests__/gatePreviewTransforms.test.tsfrontend/apps/console/src/features/flows/utils/gatePreviewTransforms.tsfrontend/packages/i18n/src/locales/en-US.ts
🚧 Files skipped from review as they are similar to previous changes (12)
- frontend/apps/console/src/features/flows/components/visual-flow/tests/SimulationScreenSketch.test.tsx
- frontend/apps/console/src/features/flows/constants/simulationPreviewConstants.ts
- frontend/apps/console/src/components/GatePreview/mocks/tests/buildPreviewMock.test.ts
- frontend/apps/console/src/features/flows/utils/gatePreviewTransforms.ts
- frontend/packages/i18n/src/locales/en-US.ts
- frontend/apps/console/src/features/flows/utils/tests/gatePreviewTransforms.test.ts
- frontend/apps/console/src/features/flows/components/visual-flow/SimulationOptionsFooter.tsx
- frontend/apps/console/src/features/flows/hooks/tests/useFlowSimulation.test.ts
- frontend/apps/console/src/components/GatePreview/IframeContent.tsx
- frontend/apps/console/src/features/flows/hooks/useFlowSimulation.ts
- frontend/apps/console/src/features/flows/components/visual-flow/tests/SimulationStepPreview.test.tsx
- frontend/apps/console/src/features/flows/components/visual-flow/SimulationStepPreview.tsx
a31c4ad to
4aa6926
Compare
4aa6926 to
6e58879
Compare
Hovering a transition option brings its target into view alongside the current step (with a short grace period so sweeping the list does not bounce the camera), honoring the static-view toggle. Background steps drop the preview chrome: options come first with a hint card naming the step. Device and application picks persist across sessions, and the panel gains keyboard support (Escape, Backspace, arrow keys). Refs thunder-id#3871
6e58879 to
a7ad20e
Compare
Purpose
A set of usability improvements for the end-user flow preview panel, addressing gaps found while using it: hovered transitions could point at off-screen nodes, background (no-screen) steps wasted the whole panel on empty preview chrome, options triggerable from the screen were duplicated in the footer, preferences reset on every session, and the panel was mouse-only. Refs #3871.
Approach
Hover focus. Hovering a transition option pans/zooms the canvas so both the current step and the option's target are visible (
useFlowSimulation.preview). When the hover ends, the camera restores the single-step focus after a short grace period, and moving to another option cancels the pending restore, so sweeping the options list reads as one motion instead of the camera bouncing per row. Explicit navigation (choose, back, restart) also cancels any pending restore. The whole behavior honors the existing follow-camera/static-view toggle: in static view the camera never moves.Background steps. Steps without a screen no longer render preview chrome (device toggles, application selector, empty device frame). The transition options come first, directly under the header, followed by a compact hint card that names the step: its display label (or executor name) with " runs in the background before the flow continues" as the caption. Start and End nodes suppress the card entirely - they are structural markers, not background work. Screen steps are unchanged.
No duplicated options. Options wired to components rendered inside the screen (buttons, resend actions, and now rich-text links such as "Forgot password? Reset") are excluded from the footer - clicking the screen is the way to take them, with the existing "or select an option on the preview screen" hint. To make that safe in the unthemed sketch renderer, rich-text links there are now clickable (and hoverable for edge preview), matching the themed gate's behavior.
Persisted preferences. The device pick and "Preview as application" selection persist in localStorage across sessions. A stored application that no longer exists falls back to the default; an unrecognized stored device falls back to mobile; storage access is guarded for restricted environments.
Keyboard support. While the preview is open: Escape exits, Backspace steps back, ArrowUp/ArrowDown walk the transition options with roving focus, and Enter activates the focused option. Focused options reuse the existing focus-to-preview wiring, so keyboard navigation drives the hover-focus camera and edge highlight exactly like the mouse. Keys are ignored while typing in inputs.
Preview mock fidelity. The app logo in the gate preview mock is wrapped in a STACK so it centers through the SDK's container-driven alignment, matching the real gate's rendering.
Tests cover the camera fit/restore/cancel behavior (fake-timer based), the background-step layout, named hint card and its Start/End suppression, footer dedup for screen-triggerable options, sketch link clicking, keyboard handling (including the input guard), and preference persistence.
Related Issues
Related PRs
Checklist
breaking changelabel added.Security checks
Summary by CodeRabbit