Skip to content
49 changes: 40 additions & 9 deletions apps/fluent-tester/src/TestComponents/Callout/CalloutTest.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -423,34 +423,63 @@ 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 toggleShowCustomizedCallout = React.useCallback(() => {
setShowCustomizedCallout(!showCustomizedCallout);
const a11yOnShowAnnouncement = 'Be informed that a customized callout has been opened.';
const calloutMessage = 'This is an example message.';
const closeButtonRef = React.useRef(null);

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);
}, [showCustomizedCallout, setIsCustomizedCalloutVisible, setShowCustomizedCallout]);
setShouldFocusCloseButton(focusCloseButton);
}, []);

const onClick = React.useCallback(() => {
openCallout(false /*focusCloseButton*/);
}, [openCallout]);

const onKeyDown = React.useCallback(
(e) => {
if (e.nativeEvent.key === 'Enter' || e.nativeEvent.key === ' ') {
openCallout(true /*focusCloseButton*/);
}
},
[openCallout],
);

React.useEffect(() => {
if (isCustomizedCalloutVisible && shouldFocusCloseButton) {
closeButtonRef.current?.focus?.();
}
}, [isCustomizedCalloutVisible, shouldFocusCloseButton]);

const onShowCustomizedCallout = React.useCallback(() => {
setIsCustomizedCalloutVisible(true);
}, [setIsCustomizedCalloutVisible]);
AccessibilityInfo.announceForAccessibility(a11yOnShowAnnouncement + ' ' + calloutMessage);
}, []);

const onDismissCustomizedCallout = React.useCallback(() => {
setIsCustomizedCalloutVisible(false);

// setting the internal state to false will instigate unmounting the
// zombie Callout control.
setShowCustomizedCallout(false);
}, [setIsCustomizedCalloutVisible, setShowCustomizedCallout]);
setShouldFocusCloseButton(false);
}, []);

const myRect: KeyboardMetrics = { screenX: 10, screenY: 10, width: 100, height: 100 };

return (
<View>
<View style={{ flexDirection: 'column', paddingVertical: 5 }}>
<Button onClick={toggleShowCustomizedCallout}>{'Press for Callout'}</Button>
<Button onClick={onClick} onKeyDown={onKeyDown} onAccessibilityTap={() => openCallout(true)}>
{'Press for Callout'}
</Button>
<Text selectable={true}>
<Text>Visibility: </Text>
{isCustomizedCalloutVisible ? <Text style={{ color: 'green' }}>Visible</Text> : <Text style={{ color: 'red' }}>Not Visible</Text>}
Expand All @@ -464,10 +493,12 @@ const CustomCallout: React.FunctionComponent = () => {
onShow={onShowCustomizedCallout}
accessibilityLabel="Customized Callout"
accessibilityRole="alert"
accessibilityOnShowAnnouncement="Be informed that a customized callout has been opened."
>
<View style={{ padding: 20, borderWidth: 2, borderColor: 'black' }}>
<Text>just some text so it does not take focus and is not empty.</Text>
<Text>{calloutMessage}</Text>
<Button componentRef={closeButtonRef} onClick={onDismissCustomizedCallout}>
{'Close'}
</Button>
</View>
</Callout>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Fix custom callout example",
"packageName": "@fluentui-react-native/tester",
"email": "krsiler@microsoft.com",
"dependentChangeType": "patch"
}