Fixed Escape key closing both AlertDialog and Dialog in EmailDesignModal#27331
Fixed Escape key closing both AlertDialog and Dialog in EmailDesignModal#27331
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
WalkthroughThis change extracts the unsaved changes confirmation modal logic into a new reusable 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 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 |
a4970bb to
ecd2ff0
Compare
The welcome email customize flow was flaky because the unsaved-changes confirmation lived as a nested Radix dismissable layer inside the parent customize dialog. When Escape dismissed the child confirmation, the parent dialog could also react to the same keypress and close unexpectedly, which caused the skipped e2e tests to fail intermittently. Fixed it by moving the dirty-confirm flow into a separate NiceModal-managed Shade alert dialog instead of rendering it inside the customize dialog. That keeps the confirmation as a sibling modal instance, preserves the expected alertdialog semantics for the tests, and isolates Escape handling so the topmost layer closes without dismissing the parent modal. Also re-enabled the skipped welcome email e2e tests that cover the confirmation, color picker, and font select Escape flows.
ecd2ff0 to
bf035ff
Compare
|



The Bug
EmailDesignModalrenders two Radix UI components as siblings:Both are portaled to the document body independently. Radix registers Escape key listeners at the document level for each one.
When the user has unsaved changes and presses Escape:
onEscapeKeyDownfires, callshandleClose(), seesdirty === true, and opens the AlertDialog.stopPropagationbetween them has no effect).handleClose()again.handleClose()checksdirty, still true, callssetShowDirtyConfirm(true).setShowDirtyConfirm(false)(from step 4) andsetShowDirtyConfirm(true)(from step 5) race — and in practice the Dialog just closes entirely.The net result: pressing Escape on the "Are you sure?" dialog closes everything instead of just the dialog.
The Fix
A
useRef(dirtyConfirmOpenRef) tracks synchronously whether the AlertDialog is open. Unlike React state, refs update immediately in the same tick with no batching delay.handleClose()opens the AlertDialog, it setsdirtyConfirmOpenRef.current = true.onOpenChangefires (open or close), it syncs the ref:dirtyConfirmOpenRef.current = isOpen.onEscapeKeyDownandonOpenChangeboth check the ref before acting — if the confirm dialog is open, they do nothing.This way, when Escape fires on the AlertDialog, the Dialog's handler sees
dirtyConfirmOpenRef.current === trueand skips, even though the state update from the AlertDialog closing hasn't committed yet.