diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java
index f32bc44be2d58f..ee6ab32496e6fc 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java
@@ -79,6 +79,7 @@ protected T prepareToRecycleView(@NonNull ThemedReactContext reactContext, T vie
view.setTag(R.id.accessibility_state, null);
view.setTag(R.id.accessibility_actions, null);
view.setTag(R.id.accessibility_value, null);
+ view.setTag(R.id.accessibility_state_expanded, null);
// This indirectly calls (and resets):
// setTranslationX
@@ -270,6 +271,9 @@ public void setViewState(@NonNull T view, @Nullable ReadableMap accessibilitySta
if (accessibilityState == null) {
return;
}
+ if (accessibilityState.hasKey("expanded")) {
+ view.setTag(R.id.accessibility_state_expanded, accessibilityState.getBoolean("expanded"));
+ }
if (accessibilityState.hasKey("selected")) {
boolean prevSelected = view.isSelected();
boolean nextSelected = accessibilityState.getBoolean("selected");
@@ -335,13 +339,6 @@ private void updateViewContentDescription(@NonNull T view) {
&& value.getType() == ReadableType.Boolean
&& value.asBoolean()) {
contentDescription.add(view.getContext().getString(R.string.state_busy_description));
- } else if (state.equals(STATE_EXPANDED) && value.getType() == ReadableType.Boolean) {
- contentDescription.add(
- view.getContext()
- .getString(
- value.asBoolean()
- ? R.string.state_expanded_description
- : R.string.state_collapsed_description));
}
}
}
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 1ec4a23f73fb0e..e6ef3c837975c9 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java
@@ -67,6 +67,8 @@ public class ReactAccessibilityDelegate extends ExploreByTouchHelper {
sActionIdMap.put("longpress", AccessibilityActionCompat.ACTION_LONG_CLICK.getId());
sActionIdMap.put("increment", AccessibilityActionCompat.ACTION_SCROLL_FORWARD.getId());
sActionIdMap.put("decrement", AccessibilityActionCompat.ACTION_SCROLL_BACKWARD.getId());
+ sActionIdMap.put("expand", AccessibilityActionCompat.ACTION_EXPAND.getId());
+ sActionIdMap.put("collapse", AccessibilityActionCompat.ACTION_COLLAPSE.getId());
}
private final View mView;
@@ -250,6 +252,14 @@ public void handleMessage(Message msg) {
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
+ if (host.getTag(R.id.accessibility_state_expanded) != null) {
+ final boolean accessibilityStateExpanded =
+ (boolean) host.getTag(R.id.accessibility_state_expanded);
+ info.addAction(
+ accessibilityStateExpanded
+ ? AccessibilityNodeInfoCompat.ACTION_COLLAPSE
+ : AccessibilityNodeInfoCompat.ACTION_EXPAND);
+ }
final AccessibilityRole accessibilityRole =
(AccessibilityRole) host.getTag(R.id.accessibility_role);
final String accessibilityHint = (String) host.getTag(R.id.accessibility_hint);
@@ -380,6 +390,12 @@ public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event)
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
+ if (action == AccessibilityNodeInfoCompat.ACTION_COLLAPSE) {
+ host.setTag(R.id.accessibility_state_expanded, false);
+ }
+ if (action == AccessibilityNodeInfoCompat.ACTION_EXPAND) {
+ host.setTag(R.id.accessibility_state_expanded, true);
+ }
if (mAccessibilityActionsMap.containsKey(action)) {
final WritableMap event = Arguments.createMap();
event.putString("actionName", mAccessibilityActionsMap.get(action));
diff --git a/ReactAndroid/src/main/res/views/uimanager/values/ids.xml b/ReactAndroid/src/main/res/views/uimanager/values/ids.xml
index 998cb3e222caca..6324b85af44673 100644
--- a/ReactAndroid/src/main/res/views/uimanager/values/ids.xml
+++ b/ReactAndroid/src/main/res/views/uimanager/values/ids.xml
@@ -24,7 +24,10 @@
-
+
+
+
+
diff --git a/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js b/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js
index d5caca95b71530..f80f587e547e05 100644
--- a/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js
+++ b/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js
@@ -70,6 +70,11 @@ const styles = StyleSheet.create({
flexDirection: 'column',
justifyContent: 'space-between',
},
+ button: {
+ padding: 8,
+ borderWidth: 1,
+ borderColor: 'blue',
+ },
container: {
flex: 1,
},
@@ -1431,10 +1436,74 @@ function DisplayOptionStatusExample({
);
}
+function AccessibilityExpandedExample(): React.Node {
+ const [expand, setExpanded] = React.useState(false);
+ const [pressed, setPressed] = React.useState(false);
+ const expandAction = {name: 'expand'};
+ const collapseAction = {name: 'collapse'};
+ return (
+ <>
+
+
+ The following component announces expanded/collapsed state correctly
+
+
+
+
+ Announcing expanded/collapse and the visible text.
+ setExpanded(!expand)}
+ accessibilityState={{expanded: expand}}>
+ Click me to change state
+
+
+
+
+
+
+ Clicking me does not change state
+
+
+
+ >
+ );
+}
+
exports.title = 'Accessibility';
exports.documentationURL = 'https://reactnative.dev/docs/accessibilityinfo';
exports.description = 'Examples of using Accessibility APIs.';
exports.examples = [
+ {
+ title: 'Accessibility expanded',
+ render(): React.Element {
+ return ;
+ },
+ },
+ {
+ title: 'Accessibility elements',
+ render(): React.Element {
+ return ;
+ },
+ },
{
title: 'New accessibility roles and states',
render(): React.Element {