Skip to content

Commit e35a148

Browse files
committed
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.
1 parent 0034fc1 commit e35a148

File tree

2 files changed

+35
-2
lines changed

2 files changed

+35
-2
lines changed

apps/fluent-tester/src/TestComponents/FocusZone/FocusZoneTest.tsx

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react';
2-
import { View, ScrollView, Pressable } from 'react-native';
2+
import { View, ScrollView, Pressable, TextInput } from 'react-native';
33

44
import type { FocusZoneDirection, FocusZoneTabNavigation } from '@fluentui/react-native';
55
import { FocusZone, MenuButton, Text, useOnPressWithFocus } from '@fluentui/react-native';
@@ -207,6 +207,27 @@ const FocusZoneGrid: React.FunctionComponent = () => {
207207
);
208208
};
209209

210+
const FocusZoneTextInput: React.FunctionComponent = () => {
211+
const textInputRef = React.useRef<TextInput>();
212+
const onFocus = React.useCallback(() => {
213+
textInputRef.current?.focus();
214+
}, []);
215+
const onBlur = React.useCallback(() => {
216+
textInputRef.current?.blur();
217+
}, []);
218+
return (
219+
<FocusZoneListWrapper>
220+
<>
221+
<Text>FocusZone Grid</Text>
222+
<FocusZone>
223+
<TextInput ref={textInputRef} onFocus={onFocus} onBlur={onBlur} multiline={true} />
224+
</FocusZone>
225+
</>
226+
</FocusZoneListWrapper>
227+
);
228+
};
229+
230+
210231
const focusZoneSections: TestSection[] = [
211232
{
212233
name: 'Common FocusZone Usage',
@@ -245,6 +266,10 @@ const focusZoneSections: TestSection[] = [
245266
name: 'FocusZone Grid',
246267
component: FocusZoneGrid,
247268
},
269+
{
270+
name: 'FocusZone with TextInput',
271+
component: FocusZoneTextInput,
272+
},
248273
];
249274

250275
const e2eSections: TestSection[] = [

packages/components/FocusZone/macos/RCTFocusZone.m

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#import "KeyCodes.h"
2+
#import <React/RCTBaseTextInputView.h>
23
#import "RCTFocusZone.h"
34
#import "RCTi18nUtil.h"
45

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

6263
for (NSView *view in [parentView subviews]) {
6364
if ([view acceptsFirstResponder]) {
64-
return view;
65+
if ([view isKindOfClass:[RCTBaseTextInputView class]]) {
66+
RCTUIView *backedTextInputView = [(RCTBaseTextInputView *)view backedTextInputView];
67+
if ([backedTextInputView acceptsFirstResponder]) {
68+
return backedTextInputView;
69+
}
70+
} else {
71+
return view;
72+
}
6573
}
6674

6775
NSView *match = GetFirstFocusableViewWithin(view);

0 commit comments

Comments
 (0)