Skip to content

Commit

Permalink
Fix TextInput focus when inside a FocusZone (#3849)
Browse files Browse the repository at this point in the history
* Fix TextInput focus when inside a FocusZone

When a TextInput is not inside a FocusZone, it properly participates in the key view loop in that Tab\Shift+Tab will place focus on the inner `RCTUITextView`, instead of the outer `RCTBaseTextInputView` subclass. This is due to [overriding](https://github.com/microsoft/react-native-macos/blob/b7033b3513d98b20a08ec20abfe2b9bb712c68d4/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm#L1147C1-L1150C2) the `canBecomeKeyView` in that base class.

However, the FocusZone control does not look at that property -- instead it only considers `canBecomeFirstResponder`, as seen in #2329.  Although it seems like the logical next step to use `canBecomeKeyView` in the FocusZone, that was previously done with #2267 but later reverted in #2322 since it broke things downstream. Instead, we'll make a compromise and leak some of the text input implementation details into the focus zone -- if we're considering focusing a subclass of `RCTBaseTextInputView`, we'll instead use the containing `backedTextInputView`.

Testing:

* TI inside FZ is now properly focused (using the new case added to the FZ page)
* Verified all other cases on the FZ page
* Verified in the downstream scenario that prompted this investigation

Notes:

* I noticed that Shift+Ctrl+Tab does not move focus out of the containing FZ, but that has been determined to be a pre-existing issue, and thus isn't being addressed in this change.
  • Loading branch information
nakambo authored Jan 29, 2025
1 parent 5ca24ac commit d9ebc63
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { View, ScrollView, Pressable } from 'react-native';
import { View, ScrollView, Pressable, TextInput } from 'react-native';

import type { FocusZoneDirection, FocusZoneTabNavigation } from '@fluentui/react-native';
import { FocusZone, MenuButton, Text, useOnPressWithFocus } from '@fluentui/react-native';
Expand Down Expand Up @@ -207,6 +207,19 @@ const FocusZoneGrid: React.FunctionComponent = () => {
);
};

const FocusZoneTextInput: React.FunctionComponent = () => {
return (
<FocusZoneListWrapper>
<>
<Text>FocusZone Grid</Text>
<FocusZone>
<TextInput multiline={true} />
</FocusZone>
</>
</FocusZoneListWrapper>
);
};

const focusZoneSections: TestSection[] = [
{
name: 'Common FocusZone Usage',
Expand Down Expand Up @@ -245,6 +258,10 @@ const focusZoneSections: TestSection[] = [
name: 'FocusZone Grid',
component: FocusZoneGrid,
},
{
name: 'FocusZone with TextInput',
component: FocusZoneTextInput,
},
];

const e2eSections: TestSection[] = [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Fix TextInput focus when inside a FocusZone",
"packageName": "@fluentui-react-native/focus-zone",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Add TextInput inside FocusZone case",
"packageName": "@fluentui-react-native/tester",
"email": "[email protected]",
"dependentChangeType": "patch"
}
10 changes: 9 additions & 1 deletion packages/components/FocusZone/macos/RCTFocusZone.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#import "KeyCodes.h"
#import <React/RCTBaseTextInputView.h>
#import "RCTFocusZone.h"
#import "RCTi18nUtil.h"

Expand Down Expand Up @@ -61,7 +62,14 @@ static inline CGFloat GetMinDistanceBetweenRectVerticesAndPoint(NSRect rect, NSP

for (NSView *view in [parentView subviews]) {
if ([view acceptsFirstResponder]) {
return view;
if ([view isKindOfClass:[RCTBaseTextInputView class]]) {
RCTUIView *backedTextInputView = [(RCTBaseTextInputView *)view backedTextInputView];
if ([backedTextInputView acceptsFirstResponder]) {
return backedTextInputView;
}
} else {
return view;
}
}

NSView *match = GetFirstFocusableViewWithin(view);
Expand Down

0 comments on commit d9ebc63

Please sign in to comment.