Skip to content

Commit

Permalink
feat: add accessibilityLabelledBy props (#32470)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
grgr-dkrk authored and facebook-github-bot committed Jan 11, 2022
1 parent b4b9c54 commit 36037fa
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 0 deletions.
7 changes: 7 additions & 0 deletions Libraries/Components/View/ViewPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,13 @@ export type ViewProps = $ReadOnly<{|
*/
accessibilityActions?: ?$ReadOnlyArray<AccessibilityActionInfo>,

/**
* Specifies the nativeID of the associated label text. When the assistive technology focuses on the component with this props, the text is read aloud.
*
* @platform android
*/
accessibilityLabelledBy?: ?string | ?Array<string>,

/**
* Views that are only used to layout their children or otherwise don't draw
* anything may be automatically removed from the native hierarchy as an
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,21 @@ public void setNativeId(@NonNull T view, @Nullable String nativeId) {
ReactFindViewUtil.notifyViewRendered(view);
}

@Override
@ReactProp(name = ViewProps.ACCESSIBILITY_LABELLED_BY)
public void setAccessibilityLabelledBy(@NonNull T view, @Nullable Dynamic nativeId) {
if (nativeId.isNull()) {
return;
}
if (nativeId.getType() == ReadableType.String) {
view.setTag(R.id.labelled_by, nativeId.asString());
} else if (nativeId.getType() == ReadableType.Array) {
// On Android, this takes a single View as labeledBy. If an array is specified, set the first
// element in the tag.
view.setTag(R.id.labelled_by, nativeId.asArray().getString(0));
}
}

@Override
@ReactProp(name = ViewProps.ACCESSIBILITY_LABEL)
public void setAccessibilityLabel(@NonNull T view, @Nullable String accessibilityLabel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;

Expand Down Expand Up @@ -64,6 +65,9 @@ public void setImportantForAccessibility(
@Override
public void setNativeId(@NonNull T view, String nativeId) {}

@Override
public void setAccessibilityLabelledBy(@NonNull T view, Dynamic nativeId) {}

@Override
public void setOpacity(@NonNull T view, float opacity) {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.events.Event;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.react.uimanager.util.ReactFindViewUtil;
import java.util.HashMap;

/**
Expand Down Expand Up @@ -191,6 +192,8 @@ public void handleMessage(Message msg) {
};
}

@Nullable View mAccessibilityLabelledBy;

@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
Expand All @@ -200,6 +203,15 @@ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCo
setRole(info, accessibilityRole, host.getContext());
}

final Object accessibilityLabelledBy = host.getTag(R.id.labelled_by);
if (accessibilityLabelledBy != null) {
mAccessibilityLabelledBy =
ReactFindViewUtil.findView(host.getRootView(), (String) accessibilityLabelledBy);
if (mAccessibilityLabelledBy != null) {
info.setLabeledBy(mAccessibilityLabelledBy);
}
}

// state is changeable.
final ReadableMap accessibilityState = (ReadableMap) host.getTag(R.id.accessibility_state);
if (accessibilityState != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import android.view.View;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.ColorPropConverter;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.yoga.YogaConstants;
Expand Down Expand Up @@ -84,6 +85,9 @@ public void setProperty(T view, String propName, @Nullable Object value) {
case ViewProps.NATIVE_ID:
mViewManager.setNativeId(view, (String) value);
break;
case ViewProps.ACCESSIBILITY_LABELLED_BY:
mViewManager.setAccessibilityLabelledBy(view, (Dynamic) value);
break;
case ViewProps.OPACITY:
mViewManager.setOpacity(view, value == null ? 1.0f : ((Double) value).floatValue());
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import android.view.View;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;

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

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

void setAccessibilityLabelledBy(T view, @Nullable Dynamic nativeId);

void setOpacity(T view, float opacity);

void setRenderToHardwareTexture(T view, boolean useHWTexture);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ public class ViewProps {
public static final String ACCESSIBILITY_STATE = "accessibilityState";
public static final String ACCESSIBILITY_ACTIONS = "accessibilityActions";
public static final String ACCESSIBILITY_VALUE = "accessibilityValue";
public static final String ACCESSIBILITY_LABELLED_BY = "accessibilityLabelledBy";
public static final String IMPORTANT_FOR_ACCESSIBILITY = "importantForAccessibility";

// DEPRECATED
Expand Down
3 changes: 3 additions & 0 deletions ReactAndroid/src/main/res/views/uimanager/values/ids.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
<!--tag is used to store accessibilityValue tag -->
<item type="id" name="accessibility_value"/>

<!--tag is used to store accessibilityLabelledBy tag -->
<item type="id" name="labelled_by"/>

<!-- tag is used to store if a view is subscribed to the pointerenter event -->
<item type="id" name="pointer_enter"/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,24 @@ class AccessibilityExample extends React.Component<{}> {
<Text>Accessible view with label, hint, role, and state</Text>
</View>
</RNTesterBlock>

<RNTesterBlock title="TextInput with accessibilityLabelledBy attribute">
<View>
<Text nativeID="formLabel1">Mail Address</Text>
<TextInput
accessibilityLabel="input test1"
accessibilityLabelledBy="formLabel1"
style={styles.default}
/>
<Text nativeID="formLabel2">First Name</Text>
<TextInput
accessibilityLabel="input test2"
accessibilityLabelledBy={['formLabel2', 'formLabel3']}
style={styles.default}
value="Foo"
/>
</View>
</RNTesterBlock>
</View>
);
}
Expand Down

0 comments on commit 36037fa

Please sign in to comment.