Skip to content

fix(userEvent): add unfocused tab fallbacks for keyboard(), clear() and blur#210

Merged
kevinccbsg merged 1 commit into
mainfrom
fix/userevent-unfocused-tab-fallbacks
Apr 13, 2026
Merged

fix(userEvent): add unfocused tab fallbacks for keyboard(), clear() and blur#210
kevinccbsg merged 1 commit into
mainfrom
fix/userevent-unfocused-tab-fallbacks

Conversation

@kevinccbsg

Copy link
Copy Markdown
Member

Summary

  • typingFallback now explicitly dispatches focus/focusin after element.focus(). Browsers suppress these events in unfocused tabs so React's event delegation never registered the element as focused — meaning focusout from keyboard("{Tab}") produced no synthetic onBlur and blur-triggered validation (e.g. React Hook Form) never ran.
  • keyboard() fallback now handles the full key string instead of only {Tab}: regular characters are buffered and flushed via typingFallback (native setter + input/change events); arrow keys, Enter, Escape etc. dispatch real keydown/keyup events; {Backspace} trims the value. This fixes combobox patterns like keyboard("Spain{arrowdown}{enter}") that were silently dropped, leaving dropdowns open and blocking subsequent clicks (pointer-events: none cascade).

Test Plan

  • 3 new Vitest unit tests — confirmed RED before fix, GREEN after (npm test src/tests/proxies/userEvent.spec.ts)
  • Full suite clean: 361 tests, 0 failures (npm run test:ci)
  • examples/twd-test-app: new /blur-validation page + test — window.blur()typeclearkeyboard("{Tab}") → validates aria-describedby is set
  • examples/twd-test-app: new /combobox-select page + test — window.blur()click trigger → keyboard("Spain{arrowdown}{enter}") → validates selected country shown

🤖 Generated with Claude Code

…nd blur

In unfocused/hidden browser tabs (common during twd-relay AI runs),
userEvent.keyboard() was a no-op for all non-Tab keys and blur validation
never fired after Tab, causing flaky test failures like the combobox
pointer-events: none cascade.

Two root causes fixed:

1. typingFallback now explicitly dispatches focus/focusin after
   element.focus(). Browsers suppress these events in unfocused tabs, so
   React's event delegation (which relies on bubbling focusin) never
   registered the element as focused — meaning focusout from keyboard("{Tab}")
   produced no synthetic onBlur and validation never ran.

2. keyboard() fallback now handles the full key string instead of only "{Tab}":
   - Regular characters are buffered and flushed via typingFallback
     (native setter + input/change events)
   - Arrow keys, Enter, Escape etc. dispatch real keydown/keyup events
   - Backspace trims the current value
   This fixes combobox patterns like keyboard("Spain{arrowdown}{enter}")
   that were silently dropped, leaving dropdowns open and blocking clicks.

Covered by:
- 3 new Vitest unit tests (verified RED before fix, GREEN after)
- 2 new twd-test-app integration scenarios using window.blur() to reproduce
  the unfocused condition: blur-validation (Tab case) and combobox-select
  (arrow+enter case)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@kevinccbsg kevinccbsg merged commit 6260196 into main Apr 13, 2026
8 checks passed
@kevinccbsg kevinccbsg deleted the fix/userevent-unfocused-tab-fallbacks branch April 13, 2026 17:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant