[0.71] Add support for announceForAccessibilityWithOptions (#12256)

* [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>

* Fix pipeline failing

* Fix overrides.json

* Change beachball change type

* Correctly suppress prop-missing error

---------

Co-authored-by: Krystal Kramer <krsiler@microsoft.com>
This commit is contained in:
Lawrence Win 2023-10-19 12:27:32 -07:00 коммит произвёл GitHub
Родитель 534a296ec0
Коммит 7f2e4a1c25
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 300 добавлений и 15 удалений

Просмотреть файл

@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "add support for announceForAccessibilityWithOptions",
"packageName": "@office-iss/react-native-win32",
"email": "krsiler@microsoft.com",
"dependentChangeType": "patch"
}

Просмотреть файл

@ -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>
);

Просмотреть файл

@ -15,6 +15,15 @@ import type {RNTesterModuleInfo} from '../types/RNTesterTypes';
import ReactNativeFeatureFlags from 'react-native/Libraries/ReactNative/ReactNativeFeatureFlags';
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',

Просмотреть файл

@ -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/Pressable/Pressable.js
<PROJECT_ROOT>/Libraries/Components/SafeAreaView/SafeAreaView.js
<PROJECT_ROOT>/Libraries/Components/TextInput/TextInput.js

Просмотреть файл

@ -32,11 +32,16 @@
"baseHash": "897569d77df852480332b7ce7ec1b594cf40aa28"
},
{
"type": "patch",
"type": "derived",
"file": "src/Libraries/Components/AccessibilityInfo/AccessibilityInfo.d.ts",
"baseFile": "Libraries/Components/AccessibilityInfo/AccessibilityInfo.d.ts",
"baseHash": "7b4cf9114df53038aeb1508f77d868b45e09049b"
},
{
"type": "derived",
"file": "src/Libraries/Components/AccessibilityInfo/AccessibilityInfo.win32.js",
"baseFile": "Libraries/Components/AccessibilityInfo/AccessibilityInfo.js",
"baseHash": "8ed7c87f3165558abe517218143dc6f73d91cc46",
"issue": 4578
"baseHash": "8ed7c87f3165558abe517218143dc6f73d91cc46"
},
{
"type": "copy",
@ -45,6 +50,12 @@
"baseHash": "d37b2f72125246ababf3260e99ef790ce76fe3bb",
"issue": 4578
},
{
"type": "derived",
"file": "src/Libraries/Components/AccessibilityInfo/NativeAccessibilityInfoWin32.js",
"baseFile": "Libraries/Components/AccessibilityInfo/NativeAccessibilityInfo.js",
"baseHash": "9427a7feebfbe3de606b2d100439cabf2faa8661"
},
{
"type": "platform",
"file": "src/Libraries/Components/Button/ButtonWin32.Props.ts"

Просмотреть файл

@ -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;

Просмотреть файл

@ -18,6 +18,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.
@ -167,12 +168,18 @@ const AccessibilityInfo: AccessibilityInfoType = {
*/
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(
@ -249,12 +256,18 @@ const AccessibilityInfo: AccessibilityInfoType = {
*/
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(
@ -371,8 +384,10 @@ const AccessibilityInfo: AccessibilityInfoType = {
* 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);
}
@ -383,19 +398,35 @@ const AccessibilityInfo: AccessibilityInfoType = {
* - `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.
*/
// $FlowIgnore[prop-missing]
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 (NativeAccessibilityManagerIOS?.announceForAccessibilityWithOptions) {
NativeAccessibilityManagerIOS?.announceForAccessibilityWithOptions(
} 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,
iosOptions,
);
} else {
NativeAccessibilityManagerIOS?.announceForAccessibility(announcement);
}
@ -408,7 +439,7 @@ const AccessibilityInfo: AccessibilityInfoType = {
* 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(
@ -419,6 +450,17 @@ const AccessibilityInfo: AccessibilityInfoType = {
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);
}

Просмотреть файл

@ -0,0 +1,37 @@
/**
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*
* @format
* @flow
*/
import type {TurboModule} from '../../TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+isReduceMotionEnabled: (
onSuccess: (isReduceMotionEnabled: boolean) => void,
) => void;
+isTouchExplorationEnabled: (
onSuccess: (isScreenReaderEnabled: boolean) => void,
) => void;
+isAccessibilityServiceEnabled?: ?(
onSuccess: (isAccessibilityServiceEnabled: boolean) => void,
) => void;
+setAccessibilityFocus: (reactTag: number) => void;
+announceForAccessibility: (announcement: string) => void;
// [Win32
+announceForAccessibilityWithOptions?: (
announcement: string,
options: {queue?: boolean, nativeID?: string},
) => void;
// Win32]
+getRecommendedTimeoutMillis?: (
mSec: number,
onSuccess: (recommendedTimeoutMillis: number) => void,
) => void;
}
export default (TurboModuleRegistry.get<Spec>('AccessibilityInfo'): ?Spec);