Skip to content

Commit d36c36d

Browse files
authored
Expand defaultTabbableElement to accept a string corresponding a child element's nativeID (#3863)
* Expand defaultTabbableElement to accept a string corresponding a child element's nativeID * Change files
1 parent 8225ebc commit d36c36d

File tree

5 files changed

+69
-6
lines changed

5 files changed

+69
-6
lines changed

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,54 @@ const FocusZoneTextInput: React.FunctionComponent = () => {
220220
);
221221
};
222222

223+
export const FirstFocusTest = () => {
224+
const outerNormalRef = React.useRef(null);
225+
const innerNormalRef = React.useRef(null);
226+
const innerDirectRef = React.useRef(null);
227+
const innerFZRef = React.useRef(null);
228+
const outerNativeID = 'nativeIDToOuterButton';
229+
const innerNativeID = 'nativeIDToInnerButton';
230+
const innerDirectNativeID = 'nativeIDDirectToInnerButton';
231+
const [outerFocusZoneTabbableElement, setOuterFocusZoneTabbableElement] = React.useState<React.RefObject<View> | string>();
232+
const [innerFocusZoneTabbableElement, setInnerFocusZoneTabbableElement] = React.useState<React.RefObject<View> | string>();
233+
234+
return (
235+
<FocusZone defaultTabbableElement={outerFocusZoneTabbableElement} tabKeyNavigation="Normal">
236+
<View>
237+
<Button onClick={() => setOuterFocusZoneTabbableElement(undefined)}>clear OuterFocusZone</Button>
238+
<Button componentRef={outerNormalRef} onClick={() => setOuterFocusZoneTabbableElement(outerNormalRef)}>
239+
Test ref
240+
</Button>
241+
<Button nativeID={outerNativeID} onClick={() => setOuterFocusZoneTabbableElement(outerNativeID)}>
242+
Test nativeId
243+
</Button>
244+
</View>
245+
<FocusZone componentRef={innerFZRef} defaultTabbableElement={innerFocusZoneTabbableElement}>
246+
<Button onClick={() => setInnerFocusZoneTabbableElement(undefined)}>clear InnerFocusZone</Button>
247+
<Button componentRef={innerDirectRef} onClick={() => setOuterFocusZoneTabbableElement(innerDirectRef)}>
248+
Test nested (direct ref)
249+
</Button>
250+
<Button nativeID={innerDirectNativeID} onClick={() => setOuterFocusZoneTabbableElement(innerDirectNativeID)}>
251+
Test nested (direct nativeID)
252+
</Button>
253+
<Button componentRef={innerNormalRef} onClick={() => setInnerFocusZoneTabbableElement(innerNormalRef)}>
254+
Test inner zone (ref)
255+
</Button>
256+
<Button nativeID={innerNativeID} onClick={() => setInnerFocusZoneTabbableElement(innerNativeID)}>
257+
Test inner zone (nativeID)
258+
</Button>
259+
<Button onClick={() => setOuterFocusZoneTabbableElement(innerFZRef)}>Test nested (transitive)</Button>
260+
</FocusZone>
261+
</FocusZone>
262+
);
263+
};
264+
223265
const focusZoneSections: TestSection[] = [
266+
{
267+
name: 'First Focus FocusZone Usage',
268+
component: FirstFocusTest,
269+
testID: FOCUSZONE_TESTPAGE,
270+
},
224271
{
225272
name: 'Common FocusZone Usage',
226273
component: CommonUsageFocusZone,
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "minor",
3+
"comment": "Expand defaultTabbableElement to accept a string corresponding a child element's nativeID",
4+
"packageName": "@fluentui-react-native/focus-zone",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "patch",
3+
"comment": "Expand defaultTabbableElement to accept a string corresponding a child element's nativeID",
4+
"packageName": "@fluentui-react-native/tester",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}

packages/components/FocusZone/src/FocusZone.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@ export const FocusZone = composable<FocusZoneType>({
2323

2424
const ftzRef = useViewCommandFocus(componentRef);
2525

26-
const [targetNativeTag, setTargetNativeTag] = React.useState<number>(undefined);
26+
const [targetFirstFocus, setTargetFirstFocus] = React.useState<number | string>(undefined);
2727
React.useLayoutEffect(() => {
28-
if (defaultTabbableElement?.current) {
29-
setTargetNativeTag(findNodeHandle(defaultTabbableElement.current));
28+
if (typeof defaultTabbableElement === 'string') {
29+
setTargetFirstFocus(defaultTabbableElement);
30+
} else if (defaultTabbableElement?.current) {
31+
setTargetFirstFocus(findNodeHandle(defaultTabbableElement.current));
3032
} else {
31-
setTargetNativeTag(undefined);
33+
setTargetFirstFocus(undefined);
3234
}
3335
}, [defaultTabbableElement]);
3436

@@ -37,7 +39,7 @@ export const FocusZone = composable<FocusZoneType>({
3739
root: {
3840
navigateAtEnd: isCircularNavigation ? 'NavigateWrap' : 'NavigateStopAtEnds', // let rest override
3941
...rest,
40-
defaultTabbableElement: targetNativeTag,
42+
defaultTabbableElement: targetFirstFocus,
4143
ref: ftzRef,
4244
},
4345
}),

packages/components/FocusZone/src/FocusZone.types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export type FocusZoneProps = IViewProps & {
1717
/**
1818
* Optionally defines the initial tabbable element inside the FocusZone
1919
*/
20-
defaultTabbableElement?: React.RefObject<React.Component>;
20+
defaultTabbableElement?: React.RefObject<React.Component> | string;
2121

2222
/**
2323
** Defines which arrows to react to

0 commit comments

Comments
 (0)