diff --git a/Libraries/Components/DrawerAndroid/DrawerLayoutAndroid.android.js b/Libraries/Components/DrawerAndroid/DrawerLayoutAndroid.android.js
index 10e265c553cccb..4435044e57a102 100644
--- a/Libraries/Components/DrawerAndroid/DrawerLayoutAndroid.android.js
+++ b/Libraries/Components/DrawerAndroid/DrawerLayoutAndroid.android.js
@@ -21,6 +21,7 @@ import dismissKeyboard from '../../Utilities/dismissKeyboard';
import Platform from '../../Utilities/Platform';
import StatusBar from '../StatusBar/StatusBar';
import View from '../View/View';
+import type {AccessibilityRole} from '../../Components/View/ViewAccessibility';
import AndroidDrawerLayoutNativeComponent, {
Commands,
} from './AndroidDrawerLayoutNativeComponent';
@@ -36,6 +37,8 @@ type DrawerSlideEvent = $ReadOnly<{|
|}>;
type Props = $ReadOnly<{|
+ accessibilityRole?: ?AccessibilityRole,
+
/**
* Determines whether the keyboard gets dismissed in response to a drag.
* - 'none' (the default), drags do not dismiss the keyboard.
diff --git a/Libraries/Components/View/ViewAccessibility.js b/Libraries/Components/View/ViewAccessibility.js
index c62be022b046e7..18da75effcb1d7 100644
--- a/Libraries/Components/View/ViewAccessibility.js
+++ b/Libraries/Components/View/ViewAccessibility.js
@@ -16,6 +16,7 @@ import type {SyntheticEvent} from '../../Types/CoreEventTypes';
export type AccessibilityRole =
| 'none'
| 'button'
+ | 'dropdownlist'
| 'togglebutton'
| 'link'
| 'search'
@@ -44,7 +45,15 @@ export type AccessibilityRole =
| 'timer'
| 'list'
| 'toolbar'
- | 'grid';
+ | 'grid'
+ | 'pager'
+ | 'scrollview'
+ | 'horizontalscrollview'
+ | 'viewgroup'
+ | 'webview'
+ | 'drawerlayout'
+ | 'slidingdrawer'
+ | 'iconmenu';
// Role types for web
export type Role =
diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m
index b034ff08ac244a..309f9a2fb8fc5c 100644
--- a/React/Views/RCTViewManager.m
+++ b/React/Views/RCTViewManager.m
@@ -28,6 +28,7 @@ @implementation RCTConvert (UIAccessibilityTraits)
(@{
@"none" : @(UIAccessibilityTraitNone),
@"button" : @(UIAccessibilityTraitButton),
+ @"dropdownlist" : @(UIAccessibilityTraitNone),
@"togglebutton" : @(UIAccessibilityTraitButton),
@"link" : @(UIAccessibilityTraitLink),
@"header" : @(UIAccessibilityTraitHeader),
@@ -63,6 +64,15 @@ @implementation RCTConvert (UIAccessibilityTraits)
@"tablist" : @(UIAccessibilityTraitNone),
@"timer" : @(UIAccessibilityTraitNone),
@"toolbar" : @(UIAccessibilityTraitNone),
+ @"grid" : @(UIAccessibilityTraitNone),
+ @"pager" : @(UIAccessibilityTraitNone),
+ @"scrollview" : @(UIAccessibilityTraitNone),
+ @"horizontalscrollview" : @(UIAccessibilityTraitNone),
+ @"viewgroup" : @(UIAccessibilityTraitNone),
+ @"webview" : @(UIAccessibilityTraitNone),
+ @"drawerlayout" : @(UIAccessibilityTraitNone),
+ @"slidingdrawer" : @(UIAccessibilityTraitNone),
+ @"iconmenu" : @(UIAccessibilityTraitNone),
@"list" : @(UIAccessibilityTraitNone),
@"grid" : @(UIAccessibilityTraitNone),
}),
diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java
index 0f5f1671c8ffd6..1ec4a23f73fb0e 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java
@@ -96,6 +96,7 @@ private void scheduleAccessibilityEventSender(View host) {
public enum AccessibilityRole {
NONE,
BUTTON,
+ DROPDOWNLIST,
TOGGLEBUTTON,
LINK,
SEARCH,
@@ -123,12 +124,22 @@ public enum AccessibilityRole {
TIMER,
LIST,
GRID,
+ PAGER,
+ SCROLLVIEW,
+ HORIZONTALSCROLLVIEW,
+ VIEWGROUP,
+ WEBVIEW,
+ DRAWERLAYOUT,
+ SLIDINGDRAWER,
+ ICONMENU,
TOOLBAR;
public static String getValue(AccessibilityRole role) {
switch (role) {
case BUTTON:
return "android.widget.Button";
+ case DROPDOWNLIST:
+ return "android.widget.Spinner";
case TOGGLEBUTTON:
return "android.widget.ToggleButton";
case SEARCH:
@@ -136,7 +147,7 @@ public static String getValue(AccessibilityRole role) {
case IMAGE:
return "android.widget.ImageView";
case IMAGEBUTTON:
- return "android.widget.ImageButon";
+ return "android.widget.ImageButton";
case KEYBOARDKEY:
return "android.inputmethodservice.Keyboard$Key";
case TEXT:
@@ -155,6 +166,22 @@ public static String getValue(AccessibilityRole role) {
return "android.widget.AbsListView";
case GRID:
return "android.widget.GridView";
+ case SCROLLVIEW:
+ return "android.widget.ScrollView";
+ case HORIZONTALSCROLLVIEW:
+ return "android.widget.HorizontalScrollView";
+ case PAGER:
+ return "androidx.viewpager.widget.ViewPager";
+ case DRAWERLAYOUT:
+ return "androidx.drawerlayout.widget.DrawerLayout";
+ case SLIDINGDRAWER:
+ return "android.widget.SlidingDrawer";
+ case ICONMENU:
+ return "com.android.internal.view.menu.IconMenuView";
+ case VIEWGROUP:
+ return "android.view.ViewGroup";
+ case WEBVIEW:
+ return "android.webkit.WebView";
case NONE:
case LINK:
case SUMMARY:
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/drawer/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/drawer/BUCK
index 9677362369c1ca..0b70da4c4df2db 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/views/drawer/BUCK
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/drawer/BUCK
@@ -29,5 +29,6 @@ rn_android_library(
react_native_target("java/com/facebook/react/uimanager/annotations:annotations"),
react_native_target("java/com/facebook/react/views/scroll:scroll"),
react_native_root_target(":generated_components_java-FBReactNativeComponentSpec"),
+ react_native_target("res:uimanager"),
],
)
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayout.java b/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayout.java
index ab9bb7d30d3ea2..40392887a1c781 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayout.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayout.java
@@ -10,10 +10,16 @@
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import androidx.core.view.AccessibilityDelegateCompat;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import com.facebook.common.logging.FLog;
+import com.facebook.react.R;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.common.ReactConstants;
+import com.facebook.react.uimanager.ReactAccessibilityDelegate.AccessibilityRole;
import com.facebook.react.uimanager.events.NativeGestureUtil;
/**
@@ -29,6 +35,30 @@
public ReactDrawerLayout(ReactContext reactContext) {
super(reactContext);
+ ViewCompat.setAccessibilityDelegate(
+ this,
+ new AccessibilityDelegateCompat() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(
+ View host, AccessibilityNodeInfoCompat info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ final AccessibilityRole accessibilityRole =
+ (AccessibilityRole) host.getTag(R.id.accessibility_role);
+ if (accessibilityRole != null) {
+ info.setClassName(AccessibilityRole.getValue(accessibilityRole));
+ }
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(host, event);
+ final AccessibilityRole accessibilityRole =
+ (AccessibilityRole) host.getTag(R.id.accessibility_role);
+ if (accessibilityRole != null) {
+ event.setClassName(AccessibilityRole.getValue(accessibilityRole));
+ }
+ }
+ });
}
@Override
diff --git a/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js b/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js
index c552f75699656a..d5caca95b71530 100644
--- a/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js
+++ b/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js
@@ -28,6 +28,7 @@ const {
Slider,
Platform,
Switch,
+ ScrollView,
} = require('react-native');
import type {EventSubscription} from 'react-native/Libraries/vendor/emitter/EventEmitter';
@@ -84,6 +85,9 @@ const styles = StyleSheet.create({
textAlign: 'center',
backgroundColor: '#000000c0',
},
+ scrollView: {
+ height: 50,
+ },
});
class AccessibilityExample extends React.Component<{}> {
@@ -571,106 +575,168 @@ class NestedCheckBox extends React.Component<
class AccessibilityRoleAndStateExample extends React.Component<{}> {
render(): React.Node {
+ const content = [
+ This is some text,
+ This is some text,
+ This is some text,
+ This is some text,
+ This is some text,
+ This is some text,
+ This is some text,
+ ];
+
return (
-
-
- Alert example
-
-
-
- Combobox example
-
-
- Menu example
-
-
- Menu bar example
-
-
- Menu item example
-
-
- Progress bar example
-
-
- Radio button example
-
-
- Radio group example
-
-
- Scrollbar example
-
-
- Spin button example
-
-
-
- Tab example
-
-
- Tab list example
-
-
- Timer example
-
-
- Toolbar example
-
-
- State busy example
-
-
-
-
-
+ <>
+
+
+ {content}
+
-
+
+
+ {content}
+
+
+
+
+ {content}
+
+
+
+
+
+ Alert example
+
+
+
+ Combobox example
+
+
+ Menu example
+
+
+ Menu bar example
+
+
+ Menu item example
+
+
+ Progress bar example
+
+
+ Radio button example
+
+
+ Radio group example
+
+
+ Scrollbar example
+
+
+ Spin button example
+
+
+
+ Tab example
+
+
+ Tab list example
+
+
+ Timer example
+
+
+ Toolbar example
+
+
+ State busy example
+
+
+ Drop Down List example
+
+
+ Pager example
+
+
+ Toggle Button example
+
+
+ Viewgroup example
+
+
+ Webview example
+
+
+
+
+
+
+
+
+ >
);
}
}
@@ -1369,18 +1435,18 @@ exports.title = 'Accessibility';
exports.documentationURL = 'https://reactnative.dev/docs/accessibilityinfo';
exports.description = 'Examples of using Accessibility APIs.';
exports.examples = [
- {
- title: 'Accessibility elements',
- render(): React.Element {
- return ;
- },
- },
{
title: 'New accessibility roles and states',
render(): React.Element {
return ;
},
},
+ {
+ title: 'Accessibility elements',
+ render(): React.Element {
+ return ;
+ },
+ },
{
title: 'Accessibility action examples',
render(): React.Element {
diff --git a/packages/rn-tester/js/examples/DrawerLayoutAndroid/DrawerLayoutAndroidExample.js b/packages/rn-tester/js/examples/DrawerLayoutAndroid/DrawerLayoutAndroidExample.js
new file mode 100644
index 00000000000000..2906099c49d27d
--- /dev/null
+++ b/packages/rn-tester/js/examples/DrawerLayoutAndroid/DrawerLayoutAndroidExample.js
@@ -0,0 +1,100 @@
+/**
+ * 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
+ * @flow
+ */
+
+'use strict';
+
+import React, {useRef, useState} from 'react';
+import type {Node} from 'react';
+import {
+ Button,
+ DrawerLayoutAndroid,
+ Text,
+ StyleSheet,
+ View,
+} from 'react-native';
+
+const Drawer = () => {
+ const drawer = useRef(null);
+ const [drawerPosition, setDrawerPosition] = useState('left');
+ const changeDrawerPosition = () => {
+ if (drawerPosition === 'left') {
+ setDrawerPosition('right');
+ } else {
+ setDrawerPosition('left');
+ }
+ };
+
+ const navigationView = () => (
+
+ I'm in the Drawer!
+
+ );
+
+ return (
+
+
+ Drawer on the {drawerPosition}!
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ alignItems: 'center',
+ justifyContent: 'center',
+ padding: 16,
+ },
+ navigationContainer: {
+ backgroundColor: '#ecf0f1',
+ },
+ paragraph: {
+ padding: 16,
+ fontSize: 15,
+ textAlign: 'center',
+ },
+});
+
+exports.title = 'DrawerLayoutAndroid';
+exports.documentationURL =
+ 'https://reactnative.dev/docs/next/drawerlayoutandroid';
+exports.description = 'Drawer Example';
+exports.examples = [
+ {
+ title: 'Drawer Example',
+ render(): Node {
+ return ;
+ },
+ },
+];
diff --git a/packages/rn-tester/js/utils/RNTesterList.android.js b/packages/rn-tester/js/utils/RNTesterList.android.js
index f92ff40c207528..97e4b43cec6602 100644
--- a/packages/rn-tester/js/utils/RNTesterList.android.js
+++ b/packages/rn-tester/js/utils/RNTesterList.android.js
@@ -15,6 +15,11 @@ import type {RNTesterModuleInfo} from '../types/RNTesterTypes';
import ReactNativeFeatureFlags from 'react-native/Libraries/ReactNative/ReactNativeFeatureFlags';
const Components: Array = [
+ {
+ key: 'DrawerLayoutAndroid',
+ category: 'UI',
+ module: require('../examples/DrawerLayoutAndroid/DrawerLayoutAndroidExample'),
+ },
{
key: 'ActivityIndicatorExample',
category: 'UI',