From 17d8eb5a9966af42637d8cd7f0fdf8f9ce938b48 Mon Sep 17 00:00:00 2001 From: chiuam <> Date: Thu, 23 Mar 2023 15:28:29 -0400 Subject: [PATCH 1/7] keyboard navigation works --- .../components/FocusZone/macos/RCTFocusZone.m | 36 +++++++++++++++++++ .../Menu/src/MenuItem/useMenuItem.ts | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/packages/components/FocusZone/macos/RCTFocusZone.m b/packages/components/FocusZone/macos/RCTFocusZone.m index 54d0c34ca4..4f2097b6b5 100644 --- a/packages/components/FocusZone/macos/RCTFocusZone.m +++ b/packages/components/FocusZone/macos/RCTFocusZone.m @@ -19,6 +19,9 @@ static const CGFloat FocusZoneBuffer = 3; @implementation RCTFocusZone +{ + NSTrackingArea *trackingArea; +} static inline CGFloat GetDistanceBetweenPoints(NSPoint point1, NSPoint point2) { @@ -186,6 +189,39 @@ static BOOL ShouldSkipFocusZone(NSView *view) return NO; } +-(id)init +{ + self = [super init]; + if (self != nil) + { + trackingArea = [[NSTrackingArea alloc] initWithRect:self.bounds options:NSTrackingActiveInActiveApp | NSTrackingMouseEnteredAndExited owner:self userInfo:nil]; + [self addTrackingArea:trackingArea]; + } + return self; +} + +-(void)updateTrackingAreas +{ + [super updateTrackingAreas]; + if (trackingArea != nil) + { + [self removeTrackingArea:trackingArea]; + } + + trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:NSTrackingActiveInActiveApp | NSTrackingMouseEnteredAndExited owner:self userInfo:nil]; + [self addTrackingArea:trackingArea]; +} + +- (void)mouseExited:(NSEvent *)event +{ + [super mouseExited:event]; + NSView *viewWithFocus = GetFirstResponder([self window]); + if (viewWithFocus != nil) + { + [viewWithFocus resignFirstResponder]; + } +} + /// Accept firstResponder on FocusZone itself in order to reassign it within the FocusZone. - (BOOL)acceptsFirstResponder { diff --git a/packages/components/Menu/src/MenuItem/useMenuItem.ts b/packages/components/Menu/src/MenuItem/useMenuItem.ts index d7258eb9a7..6e34343676 100644 --- a/packages/components/Menu/src/MenuItem/useMenuItem.ts +++ b/packages/components/Menu/src/MenuItem/useMenuItem.ts @@ -104,7 +104,7 @@ export const useHoverFocusEffect = (hovered: boolean, componentRef: React.Mutabl if (hovered) { componentRef?.current?.focus(); } else { - componentRef?.current?.blur(); + //componentRef?.current?.blur(); } }, [hovered, componentRef]); }; From a107f54d5f087e6bad1cf2a251281a6c469035b0 Mon Sep 17 00:00:00 2001 From: chiuam <> Date: Thu, 23 Mar 2023 16:14:41 -0400 Subject: [PATCH 2/7] Change files --- ...ve-focus-zone-bb03f1cd-8bb5-4100-a714-2ae966d3310c.json | 7 +++++++ ...t-native-menu-7499cad3-b53d-4e65-ac30-498b5e81638c.json | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 change/@fluentui-react-native-focus-zone-bb03f1cd-8bb5-4100-a714-2ae966d3310c.json create mode 100644 change/@fluentui-react-native-menu-7499cad3-b53d-4e65-ac30-498b5e81638c.json diff --git a/change/@fluentui-react-native-focus-zone-bb03f1cd-8bb5-4100-a714-2ae966d3310c.json b/change/@fluentui-react-native-focus-zone-bb03f1cd-8bb5-4100-a714-2ae966d3310c.json new file mode 100644 index 0000000000..2baba814d7 --- /dev/null +++ b/change/@fluentui-react-native-focus-zone-bb03f1cd-8bb5-4100-a714-2ae966d3310c.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Fix for keyboard focus is lost when hovering out of a menu", + "packageName": "@fluentui-react-native/focus-zone", + "email": "email not defined", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-native-menu-7499cad3-b53d-4e65-ac30-498b5e81638c.json b/change/@fluentui-react-native-menu-7499cad3-b53d-4e65-ac30-498b5e81638c.json new file mode 100644 index 0000000000..3235315618 --- /dev/null +++ b/change/@fluentui-react-native-menu-7499cad3-b53d-4e65-ac30-498b5e81638c.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Fix for keyboard focus is lost when hovering out of a menu", + "packageName": "@fluentui-react-native/menu", + "email": "email not defined", + "dependentChangeType": "patch" +} From 7945de32c187a6fc501821cc758a1d63784cef26 Mon Sep 17 00:00:00 2001 From: chiuam <> Date: Thu, 23 Mar 2023 16:36:46 -0400 Subject: [PATCH 3/7] Opt out .blur logic on macOS --- packages/components/Menu/src/MenuItem/useMenuItem.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/components/Menu/src/MenuItem/useMenuItem.ts b/packages/components/Menu/src/MenuItem/useMenuItem.ts index 6e34343676..e927f5d505 100644 --- a/packages/components/Menu/src/MenuItem/useMenuItem.ts +++ b/packages/components/Menu/src/MenuItem/useMenuItem.ts @@ -104,7 +104,11 @@ export const useHoverFocusEffect = (hovered: boolean, componentRef: React.Mutabl if (hovered) { componentRef?.current?.focus(); } else { - //componentRef?.current?.blur(); + // Since MenuList uses FocusZone on macOS, we need to handle the .blur() logic there in order to + // get keyboard navigation to work properly. + if (Platform.OS != 'macos') { + componentRef?.current?.blur(); + } } }, [hovered, componentRef]); }; From 8553c963fe899a9313dede5bdbe424b1327e8327 Mon Sep 17 00:00:00 2001 From: chiuam Date: Fri, 24 Mar 2023 08:44:26 -0400 Subject: [PATCH 4/7] Revert initial fix and set focus on MenuList instead --- .../components/FocusZone/macos/RCTFocusZone.m | 33 ------------------- .../Menu/src/MenuItem/useMenuItem.ts | 6 +--- .../components/Menu/src/MenuList/MenuList.tsx | 2 +- 3 files changed, 2 insertions(+), 39 deletions(-) diff --git a/packages/components/FocusZone/macos/RCTFocusZone.m b/packages/components/FocusZone/macos/RCTFocusZone.m index 4f2097b6b5..b9a836f83d 100644 --- a/packages/components/FocusZone/macos/RCTFocusZone.m +++ b/packages/components/FocusZone/macos/RCTFocusZone.m @@ -189,39 +189,6 @@ static BOOL ShouldSkipFocusZone(NSView *view) return NO; } --(id)init -{ - self = [super init]; - if (self != nil) - { - trackingArea = [[NSTrackingArea alloc] initWithRect:self.bounds options:NSTrackingActiveInActiveApp | NSTrackingMouseEnteredAndExited owner:self userInfo:nil]; - [self addTrackingArea:trackingArea]; - } - return self; -} - --(void)updateTrackingAreas -{ - [super updateTrackingAreas]; - if (trackingArea != nil) - { - [self removeTrackingArea:trackingArea]; - } - - trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:NSTrackingActiveInActiveApp | NSTrackingMouseEnteredAndExited owner:self userInfo:nil]; - [self addTrackingArea:trackingArea]; -} - -- (void)mouseExited:(NSEvent *)event -{ - [super mouseExited:event]; - NSView *viewWithFocus = GetFirstResponder([self window]); - if (viewWithFocus != nil) - { - [viewWithFocus resignFirstResponder]; - } -} - /// Accept firstResponder on FocusZone itself in order to reassign it within the FocusZone. - (BOOL)acceptsFirstResponder { diff --git a/packages/components/Menu/src/MenuItem/useMenuItem.ts b/packages/components/Menu/src/MenuItem/useMenuItem.ts index e927f5d505..d7258eb9a7 100644 --- a/packages/components/Menu/src/MenuItem/useMenuItem.ts +++ b/packages/components/Menu/src/MenuItem/useMenuItem.ts @@ -104,11 +104,7 @@ export const useHoverFocusEffect = (hovered: boolean, componentRef: React.Mutabl if (hovered) { componentRef?.current?.focus(); } else { - // Since MenuList uses FocusZone on macOS, we need to handle the .blur() logic there in order to - // get keyboard navigation to work properly. - if (Platform.OS != 'macos') { - componentRef?.current?.blur(); - } + componentRef?.current?.blur(); } }, [hovered, componentRef]); }; diff --git a/packages/components/Menu/src/MenuList/MenuList.tsx b/packages/components/Menu/src/MenuList/MenuList.tsx index d026b077f6..cb702e8931 100644 --- a/packages/components/Menu/src/MenuList/MenuList.tsx +++ b/packages/components/Menu/src/MenuList/MenuList.tsx @@ -83,7 +83,7 @@ export const MenuList = compose({ const content = Platform.OS === 'macos' ? ( - + focusZoneRef.current.focus()}> Date: Fri, 24 Mar 2023 09:00:20 -0400 Subject: [PATCH 5/7] Revert more code --- packages/components/FocusZone/macos/RCTFocusZone.m | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/components/FocusZone/macos/RCTFocusZone.m b/packages/components/FocusZone/macos/RCTFocusZone.m index b9a836f83d..54d0c34ca4 100644 --- a/packages/components/FocusZone/macos/RCTFocusZone.m +++ b/packages/components/FocusZone/macos/RCTFocusZone.m @@ -19,9 +19,6 @@ static const CGFloat FocusZoneBuffer = 3; @implementation RCTFocusZone -{ - NSTrackingArea *trackingArea; -} static inline CGFloat GetDistanceBetweenPoints(NSPoint point1, NSPoint point2) { From 3c788967f3cb9f699dfd6a7cc568c553ad27c092 Mon Sep 17 00:00:00 2001 From: chiuam Date: Fri, 24 Mar 2023 09:13:48 -0400 Subject: [PATCH 6/7] Address feedback --- packages/components/Menu/src/MenuList/MenuList.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/components/Menu/src/MenuList/MenuList.tsx b/packages/components/Menu/src/MenuList/MenuList.tsx index cb702e8931..06039dd4ab 100644 --- a/packages/components/Menu/src/MenuList/MenuList.tsx +++ b/packages/components/Menu/src/MenuList/MenuList.tsx @@ -52,9 +52,12 @@ export const MenuList = compose({ const Slots = useSlots(menuList.props, (layer) => menuListLookup(layer, menuList, userProps)); const focusZoneRef = React.useRef(); + const setFocusZoneFocus = () => { + focusZoneRef?.current?.focus(); + }; React.useEffect(() => { - focusZoneRef?.current?.focus(); + setFocusZoneFocus(); }, []); return (_final: MenuListProps, children: React.ReactNode) => { @@ -83,7 +86,7 @@ export const MenuList = compose({ const content = Platform.OS === 'macos' ? ( - focusZoneRef.current.focus()}> + Date: Fri, 24 Mar 2023 10:28:39 -0400 Subject: [PATCH 7/7] Remove change file --- ...ve-focus-zone-bb03f1cd-8bb5-4100-a714-2ae966d3310c.json | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 change/@fluentui-react-native-focus-zone-bb03f1cd-8bb5-4100-a714-2ae966d3310c.json diff --git a/change/@fluentui-react-native-focus-zone-bb03f1cd-8bb5-4100-a714-2ae966d3310c.json b/change/@fluentui-react-native-focus-zone-bb03f1cd-8bb5-4100-a714-2ae966d3310c.json deleted file mode 100644 index 2baba814d7..0000000000 --- a/change/@fluentui-react-native-focus-zone-bb03f1cd-8bb5-4100-a714-2ae966d3310c.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "patch", - "comment": "Fix for keyboard focus is lost when hovering out of a menu", - "packageName": "@fluentui-react-native/focus-zone", - "email": "email not defined", - "dependentChangeType": "patch" -}