From e74a342cfa6b9abd17f67e33b827edc88fa6210e Mon Sep 17 00:00:00 2001 From: ksiler Date: Tue, 9 Sep 2025 11:58:34 -0700 Subject: [PATCH 01/12] Edit announcement and add focusable element to callout --- .../TestComponents/Callout/CalloutTest.tsx | 50 +++++++++++++++---- yarn.lock | 4 +- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/apps/fluent-tester/src/TestComponents/Callout/CalloutTest.tsx b/apps/fluent-tester/src/TestComponents/Callout/CalloutTest.tsx index 2bbdc65960..0453df7dbc 100644 --- a/apps/fluent-tester/src/TestComponents/Callout/CalloutTest.tsx +++ b/apps/fluent-tester/src/TestComponents/Callout/CalloutTest.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import type { KeyboardMetrics } from 'react-native'; -import { Text, View, Switch, ScrollView, Platform } from 'react-native'; +import { AccessibilityInfo, Platform, ScrollView, Switch, Text, View } from 'react-native'; import { ButtonV1 as Button, Separator, Pressable } from '@fluentui/react-native'; import type { IFocusable, RestoreFocusEvent, DismissBehaviors } from '@fluentui/react-native'; @@ -423,18 +423,38 @@ const StandardCallout: React.FunctionComponent = () => { const CustomCallout: React.FunctionComponent = () => { const [showCustomizedCallout, setShowCustomizedCallout] = React.useState(false); const [isCustomizedCalloutVisible, setIsCustomizedCalloutVisible] = React.useState(false); + const [shouldFocusCloseButton, setShouldFocusCloseButton] = React.useState(false); + + const a11yOnShowAnnouncement = 'Be informed that a customized callout has been opened.'; + const calloutMessage = 'This is an example message.'; + + const closeButtonRef = React.useRef(null); const toggleShowCustomizedCallout = React.useCallback(() => { setShowCustomizedCallout(!showCustomizedCallout); - - // Unmounting a callout does not invoke onDismiss; onDismiss is only invoked - // for dismissals generated by the native app. When toggling to 'show', - // the isVisible state will be corrected to 'true' by the onShow callback. setIsCustomizedCalloutVisible(false); - }, [showCustomizedCallout, setIsCustomizedCalloutVisible, setShowCustomizedCallout]); + setShouldFocusCloseButton(false); + }, [showCustomizedCallout, setIsCustomizedCalloutVisible, setShouldFocusCloseButton, setShowCustomizedCallout]); + + const toggleShowCustomizedCalloutOnKeyDown = React.useCallback( + (e) => { + if (e.nativeEvent.key === 'Enter' || e.nativeEvent.key === ' ') { + setShowCustomizedCallout(true); + setShouldFocusCloseButton(true); + } + }, + [setShouldFocusCloseButton, setShowCustomizedCallout], + ); + + React.useEffect(() => { + if (isCustomizedCalloutVisible && shouldFocusCloseButton) { + closeButtonRef.current?.focus?.(); // Focus the close button when the callout is shown via keyboard or accessibility tap + } + }, [isCustomizedCalloutVisible, shouldFocusCloseButton]); const onShowCustomizedCallout = React.useCallback(() => { setIsCustomizedCalloutVisible(true); + AccessibilityInfo.announceForAccessibility(a11yOnShowAnnouncement + ' ' + calloutMessage); }, [setIsCustomizedCalloutVisible]); const onDismissCustomizedCallout = React.useCallback(() => { @@ -443,6 +463,7 @@ const CustomCallout: React.FunctionComponent = () => { // setting the internal state to false will instigate unmounting the // zombie Callout control. setShowCustomizedCallout(false); + setShouldFocusCloseButton(false); }, [setIsCustomizedCalloutVisible, setShowCustomizedCallout]); const myRect: KeyboardMetrics = { screenX: 10, screenY: 10, width: 100, height: 100 }; @@ -450,7 +471,16 @@ const CustomCallout: React.FunctionComponent = () => { return ( - + Visibility: {isCustomizedCalloutVisible ? Visible : Not Visible} @@ -464,10 +494,12 @@ const CustomCallout: React.FunctionComponent = () => { onShow={onShowCustomizedCallout} accessibilityLabel="Customized Callout" accessibilityRole="alert" - accessibilityOnShowAnnouncement="Be informed that a customized callout has been opened." > - just some text so it does not take focus and is not empty. + {calloutMessage} + )} diff --git a/yarn.lock b/yarn.lock index 2ffa7e32f5..fa1aceebdc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4784,9 +4784,9 @@ __metadata: languageName: unknown linkType: soft -"@fluentui-react-native/text@npm:*, @fluentui-react-native/text@workspace:*, @fluentui-react-native/text@workspace:packages/components/Text": +"@fluentui-react-native/text@npm:*, @fluentui-react-native/text@workspace:*, @fluentui-react-native/text@workspace:packages/components/text": version: 0.0.0-use.local - resolution: "@fluentui-react-native/text@workspace:packages/components/Text" + resolution: "@fluentui-react-native/text@workspace:packages/components/text" dependencies: "@babel/core": "npm:^7.20.0" "@fluentui-react-native/adapters": "workspace:*" From ca46f175c17f94e9a3257b685af3fefc62547c8a Mon Sep 17 00:00:00 2001 From: ksiler Date: Tue, 9 Sep 2025 13:09:54 -0700 Subject: [PATCH 02/12] optimize custom callout example --- .../TestComponents/Callout/CalloutTest.tsx | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/apps/fluent-tester/src/TestComponents/Callout/CalloutTest.tsx b/apps/fluent-tester/src/TestComponents/Callout/CalloutTest.tsx index 0453df7dbc..c759ee282e 100644 --- a/apps/fluent-tester/src/TestComponents/Callout/CalloutTest.tsx +++ b/apps/fluent-tester/src/TestComponents/Callout/CalloutTest.tsx @@ -427,35 +427,37 @@ const CustomCallout: React.FunctionComponent = () => { const a11yOnShowAnnouncement = 'Be informed that a customized callout has been opened.'; const calloutMessage = 'This is an example message.'; - const closeButtonRef = React.useRef(null); - const toggleShowCustomizedCallout = React.useCallback(() => { - setShowCustomizedCallout(!showCustomizedCallout); + const openCallout = React.useCallback((focusCloseButton: boolean) => { + setShowCustomizedCallout(true); setIsCustomizedCalloutVisible(false); - setShouldFocusCloseButton(false); - }, [showCustomizedCallout, setIsCustomizedCalloutVisible, setShouldFocusCloseButton, setShowCustomizedCallout]); + setShouldFocusCloseButton(focusCloseButton); + }, []); + + const toggleShowCustomizedCallout = React.useCallback(() => { + openCallout(false); + }, [openCallout]); const toggleShowCustomizedCalloutOnKeyDown = React.useCallback( (e) => { if (e.nativeEvent.key === 'Enter' || e.nativeEvent.key === ' ') { - setShowCustomizedCallout(true); - setShouldFocusCloseButton(true); + openCallout(true); } }, - [setShouldFocusCloseButton, setShowCustomizedCallout], + [openCallout], ); React.useEffect(() => { if (isCustomizedCalloutVisible && shouldFocusCloseButton) { - closeButtonRef.current?.focus?.(); // Focus the close button when the callout is shown via keyboard or accessibility tap + closeButtonRef.current?.focus?.(); } }, [isCustomizedCalloutVisible, shouldFocusCloseButton]); const onShowCustomizedCallout = React.useCallback(() => { setIsCustomizedCalloutVisible(true); AccessibilityInfo.announceForAccessibility(a11yOnShowAnnouncement + ' ' + calloutMessage); - }, [setIsCustomizedCalloutVisible]); + }, []); const onDismissCustomizedCallout = React.useCallback(() => { setIsCustomizedCalloutVisible(false); @@ -464,7 +466,7 @@ const CustomCallout: React.FunctionComponent = () => { // zombie Callout control. setShowCustomizedCallout(false); setShouldFocusCloseButton(false); - }, [setIsCustomizedCalloutVisible, setShowCustomizedCallout]); + }, []); const myRect: KeyboardMetrics = { screenX: 10, screenY: 10, width: 100, height: 100 }; @@ -474,10 +476,7 @@ const CustomCallout: React.FunctionComponent = () => { From 0b6f192d16b4c5697e58e5eb741535cfbc04b104 Mon Sep 17 00:00:00 2001 From: ksiler Date: Tue, 9 Sep 2025 13:23:46 -0700 Subject: [PATCH 03/12] Add back removed comment --- apps/fluent-tester/src/TestComponents/Callout/CalloutTest.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/fluent-tester/src/TestComponents/Callout/CalloutTest.tsx b/apps/fluent-tester/src/TestComponents/Callout/CalloutTest.tsx index c759ee282e..f6037d8c37 100644 --- a/apps/fluent-tester/src/TestComponents/Callout/CalloutTest.tsx +++ b/apps/fluent-tester/src/TestComponents/Callout/CalloutTest.tsx @@ -431,6 +431,10 @@ const CustomCallout: React.FunctionComponent = () => { const openCallout = React.useCallback((focusCloseButton: boolean) => { setShowCustomizedCallout(true); + + // Unmounting a callout does not invoke onDismiss; onDismiss is only invoked + // for dismissals generated by the native app. When toggling to 'show', + // the isVisible state will be corrected to 'true' by the onShow callback. setIsCustomizedCalloutVisible(false); setShouldFocusCloseButton(focusCloseButton); }, []); From c189c195cfcebf852a3b743c82c172a4b237e3d7 Mon Sep 17 00:00:00 2001 From: ksiler Date: Tue, 9 Sep 2025 13:32:58 -0700 Subject: [PATCH 04/12] restore yarn.lock file --- yarn.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn.lock b/yarn.lock index fa1aceebdc..2ffa7e32f5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4784,9 +4784,9 @@ __metadata: languageName: unknown linkType: soft -"@fluentui-react-native/text@npm:*, @fluentui-react-native/text@workspace:*, @fluentui-react-native/text@workspace:packages/components/text": +"@fluentui-react-native/text@npm:*, @fluentui-react-native/text@workspace:*, @fluentui-react-native/text@workspace:packages/components/Text": version: 0.0.0-use.local - resolution: "@fluentui-react-native/text@workspace:packages/components/text" + resolution: "@fluentui-react-native/text@workspace:packages/components/Text" dependencies: "@babel/core": "npm:^7.20.0" "@fluentui-react-native/adapters": "workspace:*" From e4bad1cbf66fb8d1361209820f9a707f8e008449 Mon Sep 17 00:00:00 2001 From: ksiler Date: Tue, 9 Sep 2025 13:37:09 -0700 Subject: [PATCH 05/12] Change files --- ...native-tester-78450674-4274-4fca-a61c-3ae7e8f4d826.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-react-native-tester-78450674-4274-4fca-a61c-3ae7e8f4d826.json diff --git a/change/@fluentui-react-native-tester-78450674-4274-4fca-a61c-3ae7e8f4d826.json b/change/@fluentui-react-native-tester-78450674-4274-4fca-a61c-3ae7e8f4d826.json new file mode 100644 index 0000000000..f67bf53dc7 --- /dev/null +++ b/change/@fluentui-react-native-tester-78450674-4274-4fca-a61c-3ae7e8f4d826.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Fix custom callout example", + "packageName": "@fluentui-react-native/tester", + "email": "krsiler@microsoft.com", + "dependentChangeType": "patch" +} From 838d0b8834d2c60aa72f96d57b19e657a9333c11 Mon Sep 17 00:00:00 2001 From: ksiler Date: Tue, 9 Sep 2025 15:36:42 -0700 Subject: [PATCH 06/12] debug PR pipeline --- .ado/azure-pipelines.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.ado/azure-pipelines.yml b/.ado/azure-pipelines.yml index e1200b300e..1d2387e0df 100644 --- a/.ado/azure-pipelines.yml +++ b/.ado/azure-pipelines.yml @@ -54,6 +54,12 @@ jobs: cancelTimeoutInMinutes: 5 # how much time to give 'run always even if cancelled tasks' before killing them steps: + - script: | + echo "Checking for leftover Git lock files..." + find .git/refs/remotes/origin -name "*.lock" -print + displayName: 'Debug: List Git lock files' + continueOnError: true + - checkout: self persistCredentials: true From 308ae2375dd83c873acf21a66d08b38e026ef278 Mon Sep 17 00:00:00 2001 From: ksiler Date: Tue, 9 Sep 2025 15:43:21 -0700 Subject: [PATCH 07/12] update debug code --- .ado/azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ado/azure-pipelines.yml b/.ado/azure-pipelines.yml index 1d2387e0df..390c96ea11 100644 --- a/.ado/azure-pipelines.yml +++ b/.ado/azure-pipelines.yml @@ -56,7 +56,7 @@ jobs: steps: - script: | echo "Checking for leftover Git lock files..." - find .git/refs/remotes/origin -name "*.lock" -print + find /Users/runner/work/1/s/.git/refs/remotes/origin -name "*.lock" -print displayName: 'Debug: List Git lock files' continueOnError: true From e2cde78e95eef4a0a15f2eccc47c4cd97973df62 Mon Sep 17 00:00:00 2001 From: ksiler Date: Tue, 9 Sep 2025 16:29:57 -0700 Subject: [PATCH 08/12] restore pipeline file --- .ado/azure-pipelines.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.ado/azure-pipelines.yml b/.ado/azure-pipelines.yml index 390c96ea11..e1200b300e 100644 --- a/.ado/azure-pipelines.yml +++ b/.ado/azure-pipelines.yml @@ -54,12 +54,6 @@ jobs: cancelTimeoutInMinutes: 5 # how much time to give 'run always even if cancelled tasks' before killing them steps: - - script: | - echo "Checking for leftover Git lock files..." - find /Users/runner/work/1/s/.git/refs/remotes/origin -name "*.lock" -print - displayName: 'Debug: List Git lock files' - continueOnError: true - - checkout: self persistCredentials: true From 95f05619705ccfec2e9390a28d9e9652c2a0f671 Mon Sep 17 00:00:00 2001 From: ksiler Date: Wed, 10 Sep 2025 07:19:13 -0700 Subject: [PATCH 09/12] debug --- .ado/templates/apple-tools-setup.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.ado/templates/apple-tools-setup.yml b/.ado/templates/apple-tools-setup.yml index a7faef316d..abcc750069 100644 --- a/.ado/templates/apple-tools-setup.yml +++ b/.ado/templates/apple-tools-setup.yml @@ -19,6 +19,11 @@ steps: gem install cocoapods -v 1.15.2 fi + - bash: | + ls /Applications/ + displayName: Check Applications + failOnStderr: true + - bash: | sudo xcode-select --switch $(xcode_version) displayName: Use $(xcode_friendly_name) From a3ac01171ccc819f294448ac11468be6d4cc9910 Mon Sep 17 00:00:00 2001 From: ksiler Date: Wed, 10 Sep 2025 08:48:02 -0700 Subject: [PATCH 10/12] update xcode to 16.2.0 --- .ado/templates/apple-tools-setup.yml | 5 ----- .ado/variables/vars.yml | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.ado/templates/apple-tools-setup.yml b/.ado/templates/apple-tools-setup.yml index abcc750069..a7faef316d 100644 --- a/.ado/templates/apple-tools-setup.yml +++ b/.ado/templates/apple-tools-setup.yml @@ -19,11 +19,6 @@ steps: gem install cocoapods -v 1.15.2 fi - - bash: | - ls /Applications/ - displayName: Check Applications - failOnStderr: true - - bash: | sudo xcode-select --switch $(xcode_version) displayName: Use $(xcode_friendly_name) diff --git a/.ado/variables/vars.yml b/.ado/variables/vars.yml index 77ab09cfd7..f2aab57246 100644 --- a/.ado/variables/vars.yml +++ b/.ado/variables/vars.yml @@ -2,6 +2,6 @@ variables: - name: VmImageApple value: macos-latest-internal - name: xcode_friendly_name - value: 'Xcode 15.2' + value: 'Xcode 16.2.0' - name: xcode_version - value: '/Applications/Xcode_15.2.app' + value: '/Applications/Xcode_16.2.0.app' From 4f668effc5a11ce4bb02a0a9d71d582ef1e9c91b Mon Sep 17 00:00:00 2001 From: ksiler Date: Wed, 10 Sep 2025 15:07:58 -0700 Subject: [PATCH 11/12] revert xcode changes --- .ado/variables/vars.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ado/variables/vars.yml b/.ado/variables/vars.yml index f2aab57246..77ab09cfd7 100644 --- a/.ado/variables/vars.yml +++ b/.ado/variables/vars.yml @@ -2,6 +2,6 @@ variables: - name: VmImageApple value: macos-latest-internal - name: xcode_friendly_name - value: 'Xcode 16.2.0' + value: 'Xcode 15.2' - name: xcode_version - value: '/Applications/Xcode_16.2.0.app' + value: '/Applications/Xcode_15.2.app' From 99f4e818ed77a2ff95b836edc166a678d31a97b7 Mon Sep 17 00:00:00 2001 From: ksiler Date: Thu, 11 Sep 2025 12:19:22 -0700 Subject: [PATCH 12/12] update names for onclick and onkeydown handlers --- .../src/TestComponents/Callout/CalloutTest.tsx | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/apps/fluent-tester/src/TestComponents/Callout/CalloutTest.tsx b/apps/fluent-tester/src/TestComponents/Callout/CalloutTest.tsx index f6037d8c37..fb7ec08c30 100644 --- a/apps/fluent-tester/src/TestComponents/Callout/CalloutTest.tsx +++ b/apps/fluent-tester/src/TestComponents/Callout/CalloutTest.tsx @@ -439,14 +439,14 @@ const CustomCallout: React.FunctionComponent = () => { setShouldFocusCloseButton(focusCloseButton); }, []); - const toggleShowCustomizedCallout = React.useCallback(() => { - openCallout(false); + const onClick = React.useCallback(() => { + openCallout(false /*focusCloseButton*/); }, [openCallout]); - const toggleShowCustomizedCalloutOnKeyDown = React.useCallback( + const onKeyDown = React.useCallback( (e) => { if (e.nativeEvent.key === 'Enter' || e.nativeEvent.key === ' ') { - openCallout(true); + openCallout(true /*focusCloseButton*/); } }, [openCallout], @@ -477,11 +477,7 @@ const CustomCallout: React.FunctionComponent = () => { return ( -