Skip to content

fix(macos): three Fabric focus regressions (blur, sendAccessibilityEvent, VirtualView)#3015

Merged
Saadnajmi merged 2 commits into
microsoft:mainfrom
Saadnajmi:saadnajmi/macos-fabric-focus-regressions
Jul 8, 2026
Merged

fix(macos): three Fabric focus regressions (blur, sendAccessibilityEvent, VirtualView)#3015
Saadnajmi merged 2 commits into
microsoft:mainfrom
Saadnajmi:saadnajmi/macos-fabric-focus-regressions

Conversation

@Saadnajmi

Copy link
Copy Markdown
Collaborator

Summary

Supersedes #2959 (the original PR's source fork is disabled, so that branch can no longer be updated). This carries the same three macOS Fabric focus/first-responder fixes forward, with @tvinhas's original commit cherry-picked to preserve authorship credit, plus one small refinement.

The three regressions were surfaced while validating the "Verify focus changes" item on #2901.

1. RCTViewComponentView.blur() was a no-op

resignFirstResponder on NSWindow doesn't clear the focused subview, so ref.blur() from JS did nothing. Now clears the first responder via makeFirstResponder:nil, guarded on self so it only clears focus when this view actually holds it — matching iOS [self resignFirstResponder] semantics and avoiding stealing focus from an unrelated view.

2. Fabric setAccessibilityFocus was a silent no-op on macOS

synchronouslyDispatchAccessbilityEventOnUIThread: had a #if !TARGET_OS_OSX guard with no #else, so AccessibilityInfo.setAccessibilityFocus(reactTag) did nothing on the Fabric path. Added the macOS branch (makeFirstResponder: + NSAccessibilityFocusedUIElementChangedNotification), mirroring the old-arch path in RCTAccessibilityManager.mm.

3. RCTVirtualViewComponentView keyboard-nav hooks

Added acceptsFirstResponder and accessibilityHitTest: to the macOS branch so keyboard/Tab and VoiceOver navigation unhide flagged-offscreen views under the hideOffscreenVirtualViews optimization. Partial by design (a fully hidden=YES view is dropped from the responder chain — full fix needs a hide-strategy redesign, tracked as follow-up).

Difference from #2959

  • Added the if ([[self window] firstResponder] == self) guard on blur() for iOS-parity / to avoid focus-stealing.
  • Trimmed the verbose inline comments.

Test plan

  • Builds clean: RNTester-macOS (xcodebuild -scheme RNTester-macOS -destination 'generic/platform=macOS') — BUILD SUCCEEDED.
  • All three changes are macOS-gated (#if TARGET_OS_OSX / #else) and preserve existing iOS/visionOS behavior.

Credit

Original work by @tvinhas in #2959 (commit cherry-picked to retain authorship).

Co-authored-by: Thiago Vinhas thiago@vinhas.net

tvinhas and others added 2 commits July 7, 2026 14:34
Follow-up refinements on top of Thiago Vinhas's original fix:
- RCTViewComponentView.blur(): only clear the window's first responder
  when this view actually holds it (guard on == self), matching iOS
  [self resignFirstResponder] semantics and avoiding stealing focus
  from an unrelated view.
- Trim the verbose inline comments from all three changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Saadnajmi Saadnajmi merged commit d64dcd3 into microsoft:main Jul 8, 2026
31 checks passed
Saadnajmi added a commit that referenced this pull request Jul 8, 2026
…lityEvent, VirtualView) (#3016)

## Summary

Backport of #3015 to `0.81-stable`.

Three macOS-only Fabric focus / first-responder regressions:
1. **`RCTViewComponentView.blur()`** — was a no-op
(`resignFirstResponder` on `NSWindow` doesn't clear the focused
subview). Now clears via `makeFirstResponder:nil`, guarded on `self`.
2. **Fabric `setAccessibilityFocus`** — the macOS branch was empty;
added `makeFirstResponder:` +
`NSAccessibilityFocusedUIElementChangedNotification`.
3. **`RCTVirtualViewComponentView`** — added `acceptsFirstResponder` /
`accessibilityHitTest:` hooks so keyboard/VoiceOver navigation unhides
flagged-offscreen views.

All changes are macOS-gated; iOS/visionOS behavior is unchanged.

## Test Plan

Same as #3015 — builds clean (RNTester-macOS, BUILD SUCCEEDED); every
change is inside a `#if TARGET_OS_OSX` / `#else` branch.

## Credit

Original work by @tvinhas in #2959 (commit cherry-picked to retain
authorship). Supersedes #2959.

Co-authored-by: Thiago Vinhas <thiago@vinhas.net>

---------

Co-authored-by: Thiago Vinhas <thiago@vinhas.net>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

3 participants