Skip to content

Commit 36037fa

Browse files
grgr-dkrkfacebook-github-bot
authored andcommitted
feat: add accessibilityLabelledBy props (#32470)
Summary: related: #30846, #26739 Added `accessibilityLabelledBy` props to find the nativeID of the associated label, it mainly for` <TextInput> `. The reason for implementing it as `labelledBy` instead of `labelFor` is as follows. - It was difficult to find a component with `labelFor` because the `<Text>` component does not add the `labelFor` received from her Props to the View's tag. - The use case looks like the HTML `aria-labelledby`, which is intuitive for web developers. It also seems easy to convert to a web platform. ## Changelog <!-- Help reviewers and the release process by writing your own changelog entry. For an example, see: https://github.com/facebook/react-native/wiki/Changelog --> [Android] [Added] - add `accessibilityLabelledBy` props Pull Request resolved: #32470 Test Plan: I checked it with RNTester using an Android11. https://user-images.githubusercontent.com/40130327/138666856-891d9f4d-52cf-4181-a81f-13b033037db4.mp4 Reviewed By: lunaleaps, kacieb Differential Revision: D31897112 Pulled By: ShikaSD fbshipit-source-id: 66361735679560c01834b3a4483adf264098b3e3
1 parent b4b9c54 commit 36037fa

File tree

9 files changed

+67
-0
lines changed

9 files changed

+67
-0
lines changed

Libraries/Components/View/ViewPropTypes.js

+7
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,13 @@ export type ViewProps = $ReadOnly<{|
432432
*/
433433
accessibilityActions?: ?$ReadOnlyArray<AccessibilityActionInfo>,
434434

435+
/**
436+
* Specifies the nativeID of the associated label text. When the assistive technology focuses on the component with this props, the text is read aloud.
437+
*
438+
* @platform android
439+
*/
440+
accessibilityLabelledBy?: ?string | ?Array<string>,
441+
435442
/**
436443
* Views that are only used to layout their children or otherwise don't draw
437444
* anything may be automatically removed from the native hierarchy as an

ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java

+15
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,21 @@ public void setNativeId(@NonNull T view, @Nullable String nativeId) {
137137
ReactFindViewUtil.notifyViewRendered(view);
138138
}
139139

140+
@Override
141+
@ReactProp(name = ViewProps.ACCESSIBILITY_LABELLED_BY)
142+
public void setAccessibilityLabelledBy(@NonNull T view, @Nullable Dynamic nativeId) {
143+
if (nativeId.isNull()) {
144+
return;
145+
}
146+
if (nativeId.getType() == ReadableType.String) {
147+
view.setTag(R.id.labelled_by, nativeId.asString());
148+
} else if (nativeId.getType() == ReadableType.Array) {
149+
// On Android, this takes a single View as labeledBy. If an array is specified, set the first
150+
// element in the tag.
151+
view.setTag(R.id.labelled_by, nativeId.asArray().getString(0));
152+
}
153+
}
154+
140155
@Override
141156
@ReactProp(name = ViewProps.ACCESSIBILITY_LABEL)
142157
public void setAccessibilityLabel(@NonNull T view, @Nullable String accessibilityLabel) {

ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerAdapter.java

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import android.view.View;
1111
import androidx.annotation.NonNull;
1212
import androidx.annotation.Nullable;
13+
import com.facebook.react.bridge.Dynamic;
1314
import com.facebook.react.bridge.ReadableArray;
1415
import com.facebook.react.bridge.ReadableMap;
1516

@@ -64,6 +65,9 @@ public void setImportantForAccessibility(
6465
@Override
6566
public void setNativeId(@NonNull T view, String nativeId) {}
6667

68+
@Override
69+
public void setAccessibilityLabelledBy(@NonNull T view, Dynamic nativeId) {}
70+
6771
@Override
6872
public void setOpacity(@NonNull T view, float opacity) {}
6973

ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java

+12
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.facebook.react.bridge.WritableMap;
3636
import com.facebook.react.uimanager.events.Event;
3737
import com.facebook.react.uimanager.events.EventDispatcher;
38+
import com.facebook.react.uimanager.util.ReactFindViewUtil;
3839
import java.util.HashMap;
3940

4041
/**
@@ -191,6 +192,8 @@ public void handleMessage(Message msg) {
191192
};
192193
}
193194

195+
@Nullable View mAccessibilityLabelledBy;
196+
194197
@Override
195198
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
196199
super.onInitializeAccessibilityNodeInfo(host, info);
@@ -200,6 +203,15 @@ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCo
200203
setRole(info, accessibilityRole, host.getContext());
201204
}
202205

206+
final Object accessibilityLabelledBy = host.getTag(R.id.labelled_by);
207+
if (accessibilityLabelledBy != null) {
208+
mAccessibilityLabelledBy =
209+
ReactFindViewUtil.findView(host.getRootView(), (String) accessibilityLabelledBy);
210+
if (mAccessibilityLabelledBy != null) {
211+
info.setLabeledBy(mAccessibilityLabelledBy);
212+
}
213+
}
214+
203215
// state is changeable.
204216
final ReadableMap accessibilityState = (ReadableMap) host.getTag(R.id.accessibility_state);
205217
if (accessibilityState != null) {

ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/BaseViewManagerDelegate.java

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import android.view.View;
1111
import androidx.annotation.Nullable;
1212
import com.facebook.react.bridge.ColorPropConverter;
13+
import com.facebook.react.bridge.Dynamic;
1314
import com.facebook.react.bridge.ReadableArray;
1415
import com.facebook.react.bridge.ReadableMap;
1516
import com.facebook.yoga.YogaConstants;
@@ -84,6 +85,9 @@ public void setProperty(T view, String propName, @Nullable Object value) {
8485
case ViewProps.NATIVE_ID:
8586
mViewManager.setNativeId(view, (String) value);
8687
break;
88+
case ViewProps.ACCESSIBILITY_LABELLED_BY:
89+
mViewManager.setAccessibilityLabelledBy(view, (Dynamic) value);
90+
break;
8791
case ViewProps.OPACITY:
8892
mViewManager.setOpacity(view, value == null ? 1.0f : ((Double) value).floatValue());
8993
break;

ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/BaseViewManagerInterface.java

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import android.view.View;
1111
import androidx.annotation.Nullable;
12+
import com.facebook.react.bridge.Dynamic;
1213
import com.facebook.react.bridge.ReadableArray;
1314
import com.facebook.react.bridge.ReadableMap;
1415

@@ -49,6 +50,8 @@ public interface BaseViewManagerInterface<T extends View> {
4950

5051
void setNativeId(T view, @Nullable String nativeId);
5152

53+
void setAccessibilityLabelledBy(T view, @Nullable Dynamic nativeId);
54+
5255
void setOpacity(T view, float opacity);
5356

5457
void setRenderToHardwareTexture(T view, boolean useHWTexture);

ReactAndroid/src/main/java/com/facebook/react/uimanager/interfaces/ViewProps.java

+1
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ public class ViewProps {
152152
public static final String ACCESSIBILITY_STATE = "accessibilityState";
153153
public static final String ACCESSIBILITY_ACTIONS = "accessibilityActions";
154154
public static final String ACCESSIBILITY_VALUE = "accessibilityValue";
155+
public static final String ACCESSIBILITY_LABELLED_BY = "accessibilityLabelledBy";
155156
public static final String IMPORTANT_FOR_ACCESSIBILITY = "importantForAccessibility";
156157

157158
// DEPRECATED

ReactAndroid/src/main/res/views/uimanager/values/ids.xml

+3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
<!--tag is used to store accessibilityValue tag -->
2828
<item type="id" name="accessibility_value"/>
2929

30+
<!--tag is used to store accessibilityLabelledBy tag -->
31+
<item type="id" name="labelled_by"/>
32+
3033
<!-- tag is used to store if a view is subscribed to the pointerenter event -->
3134
<item type="id" name="pointer_enter"/>
3235

packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js

+18
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,24 @@ class AccessibilityExample extends React.Component<{}> {
194194
<Text>Accessible view with label, hint, role, and state</Text>
195195
</View>
196196
</RNTesterBlock>
197+
198+
<RNTesterBlock title="TextInput with accessibilityLabelledBy attribute">
199+
<View>
200+
<Text nativeID="formLabel1">Mail Address</Text>
201+
<TextInput
202+
accessibilityLabel="input test1"
203+
accessibilityLabelledBy="formLabel1"
204+
style={styles.default}
205+
/>
206+
<Text nativeID="formLabel2">First Name</Text>
207+
<TextInput
208+
accessibilityLabel="input test2"
209+
accessibilityLabelledBy={['formLabel2', 'formLabel3']}
210+
style={styles.default}
211+
value="Foo"
212+
/>
213+
</View>
214+
</RNTesterBlock>
197215
</View>
198216
);
199217
}

0 commit comments

Comments
 (0)