Skip to content

Commit

Permalink
Integrated iOS-only accessibilityLanguage prop (#33090)
Browse files Browse the repository at this point in the history
Summary:
This PR fixes #30891

This PR is going to add an `accessibilityLanguage` prop to all the available components. This props is currently working only on iOS and should follow the [guidelines of the relative configuration](https://developer.apple.com/documentation/objectivec/nsobject/1615192-accessibilitylanguage).

I'm in no way an expert on native programming (especially Objective-C) so I'm open to changes / improvements 🙂

## 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
-->

[iOS] [Added] - Integrated the `accessibilityLanguage` prop to all the available components. The prop is available for any platform but it will work only on iOS.

Pull Request resolved: #33090

Test Plan:
This has been tested using both the Simulator, checking for the `Language` attribute, and using a physical device with the Voice Over enabled.

<img width="1083" alt="Screenshot 2022-02-11 at 13 17 32" src="https://user-images.githubusercontent.com/5963683/153590415-65fcb4ff-8f31-4a0f-90e5-8eb1aae6aa3d.png">

Reviewed By: philIip

Differential Revision: D34523608

Pulled By: rh389

fbshipit-source-id: b5d77fc0b3d76ea8ed8f30c8385459ba98122ff6
  • Loading branch information
dgopsq authored and facebook-github-bot committed Mar 7, 2022
1 parent ff76952 commit 7b05b09
Show file tree
Hide file tree
Showing 19 changed files with 53 additions and 0 deletions.
3 changes: 3 additions & 0 deletions Libraries/Components/Button.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ type ButtonProps = $ReadOnly<{|
onAccessibilityAction?: ?(event: AccessibilityActionEvent) => mixed,
accessibilityState?: ?AccessibilityState,
accessibilityHint?: ?string,
accessibilityLanguage?: ?Stringish,
|}>;

/**
Expand Down Expand Up @@ -277,6 +278,7 @@ class Button extends React.Component<ButtonProps> {
accessible,
accessibilityActions,
accessibilityHint,
accessibilityLanguage,
onAccessibilityAction,
} = this.props;
const buttonStyles = [styles.button];
Expand Down Expand Up @@ -320,6 +322,7 @@ class Button extends React.Component<ButtonProps> {
onAccessibilityAction={onAccessibilityAction}
accessibilityLabel={accessibilityLabel}
accessibilityHint={accessibilityHint}
accessibilityLanguage={accessibilityLanguage}
accessibilityRole="button"
accessibilityState={accessibilityState}
hasTVPreferredFocus={hasTVPreferredFocus}
Expand Down
1 change: 1 addition & 0 deletions Libraries/Components/Pressable/Pressable.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type Props = $ReadOnly<{|
accessibilityActions?: ?$ReadOnlyArray<AccessibilityActionInfo>,
accessibilityElementsHidden?: ?boolean,
accessibilityHint?: ?Stringish,
accessibilityLanguage?: ?Stringish,
accessibilityIgnoresInvertColors?: ?boolean,
accessibilityLabel?: ?Stringish,
accessibilityLiveRegion?: ?('none' | 'polite' | 'assertive'),
Expand Down
1 change: 1 addition & 0 deletions Libraries/Components/Touchable/TouchableBounce.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ class TouchableBounce extends React.Component<Props, State> {
accessible={this.props.accessible !== false}
accessibilityLabel={this.props.accessibilityLabel}
accessibilityHint={this.props.accessibilityHint}
accessibilityLanguage={this.props.accessibilityLanguage}
accessibilityRole={this.props.accessibilityRole}
accessibilityState={this.props.accessibilityState}
accessibilityActions={this.props.accessibilityActions}
Expand Down
1 change: 1 addition & 0 deletions Libraries/Components/Touchable/TouchableHighlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ class TouchableHighlight extends React.Component<Props, State> {
accessible={this.props.accessible !== false}
accessibilityLabel={this.props.accessibilityLabel}
accessibilityHint={this.props.accessibilityHint}
accessibilityLanguage={this.props.accessibilityLanguage}
accessibilityRole={this.props.accessibilityRole}
accessibilityState={accessibilityState}
accessibilityValue={this.props.accessibilityValue}
Expand Down
1 change: 1 addition & 0 deletions Libraries/Components/Touchable/TouchableNativeFeedback.js
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ class TouchableNativeFeedback extends React.Component<Props, State> {
),
accessible: this.props.accessible !== false,
accessibilityHint: this.props.accessibilityHint,
accessibilityLanguage: this.props.accessibilityLanguage,
accessibilityLabel: this.props.accessibilityLabel,
accessibilityRole: this.props.accessibilityRole,
accessibilityState: accessibilityState,
Expand Down
1 change: 1 addition & 0 deletions Libraries/Components/Touchable/TouchableOpacity.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ class TouchableOpacity extends React.Component<Props, State> {
accessible={this.props.accessible !== false}
accessibilityLabel={this.props.accessibilityLabel}
accessibilityHint={this.props.accessibilityHint}
accessibilityLanguage={this.props.accessibilityLanguage}
accessibilityRole={this.props.accessibilityRole}
accessibilityState={accessibilityState}
accessibilityActions={this.props.accessibilityActions}
Expand Down
2 changes: 2 additions & 0 deletions Libraries/Components/Touchable/TouchableWithoutFeedback.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type Props = $ReadOnly<{|
accessibilityActions?: ?$ReadOnlyArray<AccessibilityActionInfo>,
accessibilityElementsHidden?: ?boolean,
accessibilityHint?: ?Stringish,
accessibilityLanguage?: ?Stringish,
accessibilityIgnoresInvertColors?: ?boolean,
accessibilityLabel?: ?Stringish,
accessibilityLiveRegion?: ?('none' | 'polite' | 'assertive'),
Expand Down Expand Up @@ -72,6 +73,7 @@ const PASSTHROUGH_PROPS = [
'accessibilityActions',
'accessibilityElementsHidden',
'accessibilityHint',
'accessibilityLanguage',
'accessibilityIgnoresInvertColors',
'accessibilityLabel',
'accessibilityLiveRegion',
Expand Down
1 change: 1 addition & 0 deletions Libraries/Components/View/ReactNativeViewAttributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const UIView = {
accessibilityState: true,
accessibilityValue: true,
accessibilityHint: true,
accessibilityLanguage: true,
importantForAccessibility: true,
nativeID: true,
testID: true,
Expand Down
9 changes: 9 additions & 0 deletions Libraries/Components/View/ViewPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,15 @@ export type ViewProps = $ReadOnly<{|
*/
accessibilityHint?: ?Stringish,

/**
* Indicates to the accessibility services that the UI component is in
* a specific language. The provided string should be formatted following
* the BCP 47 specification (https://www.rfc-editor.org/info/bcp47).
*
* @platform ios
*/
accessibilityLanguage?: ?Stringish,

/**
* Indicates to accessibility services to treat UI component like a specific role.
*/
Expand Down
2 changes: 2 additions & 0 deletions Libraries/NativeComponent/PlatformBaseViewConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ const PlatformBaseViewConfig: PartialViewConfigWithoutName =
accessibilityLabelledBy: true,
accessibilityLabel: true,
accessibilityHint: true,
accessibilityLanguage: true,
accessibilityRole: true,
accessibilityState: true,
accessibilityActions: true,
Expand Down Expand Up @@ -354,6 +355,7 @@ const PlatformBaseViewConfig: PartialViewConfigWithoutName =
accessibilityActions: true,
accessibilityLabel: true,
accessibilityHint: true,
accessibilityLanguage: true,
accessibilityValue: true,
accessibilityViewIsModal: true,
accessibilityElementsHidden: true,
Expand Down
1 change: 1 addition & 0 deletions Libraries/Text/TextProps.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export type TextProps = $ReadOnly<{|
accessibilityActions?: ?$ReadOnlyArray<AccessibilityActionInfo>,
onAccessibilityAction?: ?(event: AccessibilityActionEvent) => mixed,
accessibilityHint?: ?Stringish,
accessibilityLanguage?: ?Stringish,
accessibilityLabel?: ?Stringish,
accessibilityRole?: ?AccessibilityRole,
accessibilityState?: ?AccessibilityState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ - (instancetype)initWithString:(facebook::react::AttributedString)attributedStri
firstElement.isAccessibilityElement = YES;
firstElement.accessibilityTraits = _view.accessibilityTraits;
firstElement.accessibilityLabel = accessibilityLabel;
firstElement.accessibilityLanguage = _view.accessibilityLanguage;
firstElement.accessibilityFrame = UIAccessibilityConvertFrameToScreenCoordinates(_view.bounds, _view);
[firstElement setAccessibilityActivationPoint:CGPointMake(
firstElement.accessibilityFrame.origin.x + 1.0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,12 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &
self.accessibilityElement.accessibilityLabel = RCTNSStringFromStringNilIfEmpty(newViewProps.accessibilityLabel);
}

// `accessibilityLanguage`
if (oldViewProps.accessibilityLanguage != newViewProps.accessibilityLanguage) {
self.accessibilityElement.accessibilityLanguage =
RCTNSStringFromStringNilIfEmpty(newViewProps.accessibilityLanguage);
}

// `accessibilityHint`
if (oldViewProps.accessibilityHint != newViewProps.accessibilityHint) {
self.accessibilityElement.accessibilityHint = RCTNSStringFromStringNilIfEmpty(newViewProps.accessibilityHint);
Expand Down
1 change: 1 addition & 0 deletions React/Views/RCTViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ - (RCTShadowView *)shadowView
RCT_REMAP_VIEW_PROPERTY(accessibilityActions, reactAccessibilityElement.accessibilityActions, NSDictionaryArray)
RCT_REMAP_VIEW_PROPERTY(accessibilityLabel, reactAccessibilityElement.accessibilityLabel, NSString)
RCT_REMAP_VIEW_PROPERTY(accessibilityHint, reactAccessibilityElement.accessibilityHint, NSString)
RCT_REMAP_VIEW_PROPERTY(accessibilityLanguage, reactAccessibilityElement.accessibilityLanguage, NSString)
RCT_REMAP_VIEW_PROPERTY(accessibilityValue, reactAccessibilityElement.accessibilityValueInternal, NSDictionary)
RCT_REMAP_VIEW_PROPERTY(accessibilityViewIsModal, reactAccessibilityElement.accessibilityViewIsModal, BOOL)
RCT_REMAP_VIEW_PROPERTY(accessibilityElementsHidden, reactAccessibilityElement.accessibilityElementsHidden, BOOL)
Expand Down
1 change: 1 addition & 0 deletions React/Views/UIView+React.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@
@property (nonatomic, copy) NSDictionary<NSString *, id> *accessibilityState;
@property (nonatomic, copy) NSArray<NSDictionary *> *accessibilityActions;
@property (nonatomic, copy) NSDictionary *accessibilityValueInternal;
@property (nonatomic, copy) NSString *accessibilityLanguage;

/**
* Used in debugging to get a description of the view hierarchy rooted at
Expand Down
11 changes: 11 additions & 0 deletions React/Views/UIView+React.m
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,17 @@ - (void)setAccessibilityActions:(NSArray<NSDictionary *> *)accessibilityActions
self, @selector(accessibilityActions), accessibilityActions, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSString *)accessibilityLanguage
{
return objc_getAssociatedObject(self, _cmd);
}

- (void)setAccessibilityLanguage:(NSString *)accessibilityLanguage
{
objc_setAssociatedObject(
self, @selector(accessibilityLanguage), accessibilityLanguage, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSString *)accessibilityRole
{
return objc_getAssociatedObject(self, _cmd);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ AccessibilityProps::AccessibilityProps(
"accessibilityHint",
sourceProps.accessibilityHint,
"")),
accessibilityLanguage(convertRawProp(
context,
rawProps,
"accessibilityLanguage",
sourceProps.accessibilityLanguage,
"")),
accessibilityValue(convertRawProp(
context,
rawProps,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class AccessibilityProps {
AccessibilityLiveRegion::None};
std::string accessibilityRole{""};
std::string accessibilityHint{""};
std::string accessibilityLanguage{""};
AccessibilityValue accessibilityValue;
std::vector<AccessibilityAction> accessibilityActions{};
bool accessibilityViewIsModal{false};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ class AccessibilityIOSExample extends React.Component<Props> {
This view's children are hidden from the accessibility tree
</Text>
</View>
<View accessible={true} accessibilityLanguage="it-IT">
<Text>This view's language should be `it-IT`</Text>
</View>
</RNTesterBlock>
);
}
Expand Down

0 comments on commit 7b05b09

Please sign in to comment.