fix(Combobox): defer handleBlur close to let FocusScope restore focus#2663
Conversation
📝 WalkthroughWalkthroughComboboxInput's handleBlur now delays closing via requestAnimationFrame and re-checks document.activeElement after the frame; tests were added/updated to assert popup stays open when focus is restored inside before the deferred callback and closes when focus remains outside. ChangesCombobox Deferred-Close Implementation and Tests
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
commit: |
When two ComboboxRoot instances are on the same page, clicking the second's trigger while the first is open causes the second to open briefly then immediately close. This happens because handleBlur fires before FocusScope's focus-restoration mechanism can pull focus back inside. Wrap handleBlur's close call in a requestAnimationFrame that re-checks document.activeElement, giving FocusScope's handleFocusOut a chance to restore focus first. If focus ends up back inside, the close is skipped.
5125133 to
5601e74
Compare
🔗 Linked issue
Resolves nuxt/ui#6437, resolves nuxt/ui#6511
❓ Type of change
📚 Description
When two
ComboboxRootinstances are on the same page and the first is open, clicking the second's trigger causes it to open briefly then immediately close. This only happens with a quick click (short pointerdown→pointerup). Holding the mouse for ~200ms before releasing works fine.Regression introduced in v2.9.3 (commit 9102386 —
FocusScope.handleMutationsnull-guard removed an incidental focus-protection that previously masked this race condition).Root cause:
ComboboxInput.handleBlurfires beforeFocusScope'shandleFocusOut(documentfocusoutlistener) has a chance to restore focus inside the scope. Sinceblurfires beforefocusoutin DOM event order,handleBlursees focus outside and synchronousl combobox, unmounting theFocusScopebefore it can act.Fix: wrap
handleBlur's close logic in arequestAnimationFramethat re-checksdocument.activeElementafter givingFocusScope's focus-restoration mechanism a chance to run. If focus ends up back inside the combobox root or content, the close is skipped.📝 Checklist
Summary by CodeRabbit