Skip to content

Commit

Permalink
[0.72] Add support for announceForAccessibilityWithOptions (#12255)
Browse files Browse the repository at this point in the history
* [Win32] Add support for announceForAccessibilityWithOptions  (#12237)

* initial commit

* add NativeAccessibilityInfoWin32.js

* update example

* Change files

* add override and .d.ts file

* add another override

* add comma

* extend options type instead of redefine

* try [prop-missing] to fix lint error

* fix comment in RNTesterList.win32

* try ignoring AccessibilityInfo.js in .flowconfig

* Revert flowconfig version

* Lint script runs succesfully locally

---------

Co-authored-by: Krystal Kramer <krsiler@microsoft.com>

* Change beachball change type

---------

Co-authored-by: Krystal Kramer <krsiler@microsoft.com>
  • Loading branch information
lawrencewin and ksiler committed Oct 19, 2023
1 parent 7cb5464 commit 8b51781
Show file tree
Hide file tree
Showing 8 changed files with 291 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "add support for announceForAccessibilityWithOptions",
"packageName": "@office-iss/react-native-win32",
"email": "krsiler@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -474,10 +474,27 @@ const AccessibilityInfoExample: React.FunctionComponent<{}> =() => {
const onClick = React.useCallback(() => {
AccessibilityInfo.announceForAccessibility('AccessibilityInfo announcement succeeded!');
}, []);

const onClickDelayed = React.useCallback(() => {
setTimeout(() => {
AccessibilityInfo.announceForAccessibilityWithOptions(
'AccessibilityInfo announcement succeeded!',
{ nativeID: 'AnnouncementTarget' });
}, 3000);
}, []);

return (
<View style={styles.box}>
<TouchableHighlight onPress={onClick}>
<Text>AccessibilityInfo.announceForAccessibility</Text>
<View>
<TouchableHighlight onPress={onClick} underlayColor={'transparent'}>
<ViewWin32 style={styles.box} accessible focusable>
<Text>AccessibilityInfo.announceForAccessibility</Text>
</ViewWin32>
</TouchableHighlight>

<TouchableHighlight onPress={onClickDelayed} underlayColor={'transparent'}>
<ViewWin32 style={styles.box} accessible focusable nativeID={'AnnouncementTarget'}>
<Text>AccessibilityInfo.announceForAccessibilityWithOptions</Text>
</ViewWin32>
</TouchableHighlight>
</View>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ const Components: Array<RNTesterModuleInfo> = [
key: 'DrawerLayoutAndroid',
category: 'UI',
module: require('../examples/DrawerLayoutAndroid/DrawerLayoutAndroidExample'),
},*/
{
key: 'AccessibilityExampleWin32',
module: require('../examples-win32/Accessibility/AccessibilityExampleWin32'),
},*/
},
{
key: 'ActivityIndicatorExample',
category: 'UI',
Expand Down
1 change: 1 addition & 0 deletions packages/@office-iss/react-native-win32/.flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
; initRNLibraries build step
<PROJECT_ROOT>/index.js
<PROJECT_ROOT>/Libraries/Alert/Alert.js
<PROJECT_ROOT>/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js
<PROJECT_ROOT>/Libraries/Components/Button.js
<PROJECT_ROOT>/Libraries/Components/Pressable/Pressable.js
<PROJECT_ROOT>/Libraries/Components/SafeAreaView/SafeAreaView.js
Expand Down
17 changes: 14 additions & 3 deletions packages/@office-iss/react-native-win32/overrides.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,16 @@
"issue": 11041
},
{
"type": "patch",
"type": "derived",
"file": "src/Libraries/Components/AccessibilityInfo/AccessibilityInfo.d.ts",
"baseFile": "packages/react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo.d.ts",
"baseHash": "f55fda723b8dc0f9836f2fa4a4a766ed45c71c00"
},
{
"type": "derived",
"file": "src/Libraries/Components/AccessibilityInfo/AccessibilityInfo.win32.js",
"baseFile": "packages/react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js",
"baseHash": "163927cc271b53109f06eaf5fc6d30a9af8cb27e",
"issue": 4578
"baseHash": "163927cc271b53109f06eaf5fc6d30a9af8cb27e"
},
{
"type": "copy",
Expand All @@ -52,6 +57,12 @@
"baseHash": "d37b2f72125246ababf3260e99ef790ce76fe3bb",
"issue": 4578
},
{
"type": "derived",
"file": "src/Libraries/Components/AccessibilityInfo/NativeAccessibilityInfoWin32.js",
"baseFile": "packages/react-native/Libraries/Components/AccessibilityInfo/NativeAccessibilityInfo.js",
"baseHash": "9427a7feebfbe3de606b2d100439cabf2faa8661"
},
{
"type": "derived",
"file": "src/Libraries/Components/Button.win32.js",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/

import type * as React from 'react';
import {HostComponent} from '../../../types/public/ReactNativeTypes';
import {EmitterSubscription} from '../../vendor/emitter/EventEmitter';

type AccessibilityChangeEventName =
| 'change' // deprecated, maps to screenReaderChanged
| 'boldTextChanged' // iOS-only Event
| 'grayscaleChanged' // iOS-only Event
| 'invertColorsChanged' // iOS-only Event
| 'reduceMotionChanged'
| 'screenReaderChanged'
| 'reduceTransparencyChanged'; // iOS-only Event

type AccessibilityChangeEvent = boolean;

type AccessibilityChangeEventHandler = (
event: AccessibilityChangeEvent,
) => void;

type AccessibilityAnnouncementEventName = 'announcementFinished'; // iOS-only Event

type AccessibilityAnnouncementFinishedEvent = {
announcement: string;
success: boolean;
};

type AccessibilityAnnouncementFinishedEventHandler = (
event: AccessibilityAnnouncementFinishedEvent,
) => void;

type AccessibilityEventTypes = 'click' | 'focus' | 'viewHoverEnter';

/**
* @see https://reactnative.dev/docs/accessibilityinfo
*/
export interface AccessibilityInfoStatic {
/**
* Query whether bold text is currently enabled.
*
* @platform ios
*/
isBoldTextEnabled: () => Promise<boolean>;

/**
* Query whether grayscale is currently enabled.
*
* @platform ios
*/
isGrayscaleEnabled: () => Promise<boolean>;

/**
* Query whether invert colors is currently enabled.
*
* @platform ios
*/
isInvertColorsEnabled: () => Promise<boolean>;

/**
* Query whether reduce motion is currently enabled.
*/
isReduceMotionEnabled: () => Promise<boolean>;

/**
* Query whether reduce motion and prefer cross-fade transitions settings are currently enabled.
*
* Returns a promise which resolves to a boolean.
* The result is `true` when prefer cross-fade transitions is enabled and `false` otherwise.
*/
prefersCrossFadeTransitions(): Promise<boolean>;

/**
* Query whether reduce transparency is currently enabled.
*
* @platform ios
*/
isReduceTransparencyEnabled: () => Promise<boolean>;

/**
* Query whether a screen reader is currently enabled.
*/
isScreenReaderEnabled: () => Promise<boolean>;

/**
* Query whether Accessibility Service is currently enabled.
*
* Returns a promise which resolves to a boolean.
* The result is `true` when any service is enabled and `false` otherwise.
*
* @platform android
*/
isAccessibilityServiceEnabled(): Promise<boolean>;

/**
* Add an event handler. Supported events:
* - announcementFinished: iOS-only event. Fires when the screen reader has finished making an announcement.
* The argument to the event handler is a dictionary with these keys:
* - announcement: The string announced by the screen reader.
* - success: A boolean indicating whether the announcement was successfully made.
* - AccessibilityEventName constants other than announcementFinished: Fires on accessibility feature change.
* The argument to the event handler is a boolean.
* The boolean is true when the related event's feature is enabled and false otherwise.
*
*/
addEventListener(
eventName: AccessibilityChangeEventName,
handler: AccessibilityChangeEventHandler,
): EmitterSubscription;
addEventListener(
eventName: AccessibilityAnnouncementEventName,
handler: AccessibilityAnnouncementFinishedEventHandler,
): EmitterSubscription;

/**
* Set accessibility focus to a react component.
*/
setAccessibilityFocus: (reactTag: number) => void;

/**
* Post a string to be announced by the screen reader.
*/
announceForAccessibility: (announcement: string) => void;

/**
* Post a string to be announced by the screen reader.
* - `announcement`: The string announced by the screen reader.
* - `options`: An object that configures the reading options.
* - `queue`: The announcement will be queued behind existing announcements. iOS only.
* - `nativeID`: The nativeID of the element to send the announcement from. win32 only.
*/
announceForAccessibilityWithOptions(
announcement: string,
options: {
queue?: boolean | undefined;
nativeID?: string | undefined; // win32
},
): void;

/**
* Gets the timeout in millisecond that the user needs.
* This value is set in "Time to take action (Accessibility timeout)" of "Accessibility" settings.
*
* @platform android
*/
getRecommendedTimeoutMillis: (originalTimeout: number) => Promise<number>;
sendAccessibilityEvent: (
handle: React.ElementRef<HostComponent<unknown>>,
eventType: AccessibilityEventTypes,
) => void;
}

export const AccessibilityInfo: AccessibilityInfoStatic;
export type AccessibilityInfo = AccessibilityInfoStatic;
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {sendAccessibilityEvent} from '../../ReactNative/RendererProxy';
import Platform from '../../Utilities/Platform';
import legacySendAccessibilityEvent from './legacySendAccessibilityEvent';
import NativeAccessibilityInfo from './NativeAccessibilityInfo';
import NativeAccessibilityInfoWin32 from './NativeAccessibilityInfoWin32';
import NativeAccessibilityManagerIOS from './NativeAccessibilityManager';

// Events that are only supported on Android.
Expand Down Expand Up @@ -166,12 +167,18 @@ const AccessibilityInfo = {
*/
isReduceMotionEnabled(): Promise<boolean> {
return new Promise((resolve, reject) => {
if (Platform.OS === 'android' || Platform.OS === 'win32') {
if (Platform.OS === 'android') {
if (NativeAccessibilityInfo != null) {
NativeAccessibilityInfo.isReduceMotionEnabled(resolve);
} else {
reject(null);
}
} else if (Platform.OS === 'win32') {
if (NativeAccessibilityInfoWin32 != null) {
NativeAccessibilityInfoWin32.isReduceMotionEnabled(resolve);
} else {
reject(null);
}
} else {
if (NativeAccessibilityManagerIOS != null) {
NativeAccessibilityManagerIOS.getCurrentReduceMotionState(
Expand Down Expand Up @@ -248,12 +255,18 @@ const AccessibilityInfo = {
*/
isScreenReaderEnabled(): Promise<boolean> {
return new Promise((resolve, reject) => {
if (Platform.OS === 'android' || Platform.OS === 'win32') {
if (Platform.OS === 'android') {
if (NativeAccessibilityInfo != null) {
NativeAccessibilityInfo.isTouchExplorationEnabled(resolve);
} else {
reject(null);
}
} else if (Platform.OS === 'win32') {
if (NativeAccessibilityInfoWin32 != null) {
NativeAccessibilityInfoWin32.isTouchExplorationEnabled(resolve);
} else {
reject(null);
}
} else {
if (NativeAccessibilityManagerIOS != null) {
NativeAccessibilityManagerIOS.getCurrentVoiceOverState(
Expand Down Expand Up @@ -370,8 +383,10 @@ const AccessibilityInfo = {
* See https://reactnative.dev/docs/accessibilityinfo#announceforaccessibility
*/
announceForAccessibility(announcement: string): void {
if (Platform.OS === 'android' || Platform.OS === 'win32') {
if (Platform.OS === 'android') {
NativeAccessibilityInfo?.announceForAccessibility(announcement);
} else if (Platform.OS === 'win32') {
NativeAccessibilityInfoWin32?.announceForAccessibility(announcement);
} else {
NativeAccessibilityManagerIOS?.announceForAccessibility(announcement);
}
Expand All @@ -382,18 +397,33 @@ const AccessibilityInfo = {
* - `announcement`: The string announced by the screen reader.
* - `options`: An object that configures the reading options.
* - `queue`: The announcement will be queued behind existing announcements. iOS only.
* - `nativeID`: The nativeID of the element to send the announcement from. win32 only.
*/
announceForAccessibilityWithOptions(
announcement: string,
options: {queue?: boolean},
options: {
queue?: boolean,
nativeID?: string, // win32
},
): void {
if (Platform.OS === 'android' || Platform.OS === 'win32') {
if (Platform.OS === 'android') {
NativeAccessibilityInfo?.announceForAccessibility(announcement);
} else if (Platform.OS === 'win32') {
if (NativeAccessibilityInfoWin32?.announceForAccessibilityWithOptions) {
NativeAccessibilityInfoWin32?.announceForAccessibilityWithOptions(
announcement,
options,
);
} else {
NativeAccessibilityInfoWin32?.announceForAccessibility(announcement);
}
} else {
if (NativeAccessibilityManagerIOS?.announceForAccessibilityWithOptions) {
const {nativeID: _, ...iosOptions} = options;
// $FlowFixMe[prop-missing]
NativeAccessibilityManagerIOS?.announceForAccessibilityWithOptions(
announcement,
options,
iosOptions,
);
} else {
NativeAccessibilityManagerIOS?.announceForAccessibility(announcement);
Expand All @@ -407,7 +437,7 @@ const AccessibilityInfo = {
* See https://reactnative.dev/docs/accessibilityinfo#getrecommendedtimeoutmillis
*/
getRecommendedTimeoutMillis(originalTimeout: number): Promise<number> {
if (Platform.OS === 'android' || Platform.OS === 'win32') {
if (Platform.OS === 'android') {
return new Promise((resolve, reject) => {
if (NativeAccessibilityInfo?.getRecommendedTimeoutMillis) {
NativeAccessibilityInfo.getRecommendedTimeoutMillis(
Expand All @@ -418,6 +448,17 @@ const AccessibilityInfo = {
resolve(originalTimeout);
}
});
} else if (Platform.OS === 'win32') {
return new Promise((resolve, reject) => {
if (NativeAccessibilityInfoWin32?.getRecommendedTimeoutMillis) {
NativeAccessibilityInfoWin32.getRecommendedTimeoutMillis(
originalTimeout,
resolve,
);
} else {
resolve(originalTimeout);
}
});
} else {
return Promise.resolve(originalTimeout);
}
Expand Down

0 comments on commit 8b51781

Please sign in to comment.