Skip to content

Commit

Permalink
7df3eea|Facebook Github Bot|Add accessibilityValueDescription support. (
Browse files Browse the repository at this point in the history
  • Loading branch information
williamdeng committed Apr 21, 2020
1 parent 7593b0a commit fa50c08
Show file tree
Hide file tree
Showing 14 changed files with 313 additions and 26 deletions.
1 change: 1 addition & 0 deletions Libraries/Components/View/ReactNativeViewAttributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ ReactNativeViewAttributes.UIView = {
accessibilityLiveRegion: true,
accessibilityRole: true,
accessibilityState: true,
accessibilityValue: true,
importantForAccessibility: true,
nativeID: true,
testID: true,
Expand Down
22 changes: 22 additions & 0 deletions Libraries/Components/View/ViewAccessibility.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,25 @@ export type AccessibilityState = {
busy?: boolean,
expanded?: boolean,
};

export type AccessibilityValue = $ReadOnly<{|
/**
* The minimum value of this component's range. (should be an integer)
*/
min?: number,

/**
* The maximum value of this component's range. (should be an integer)
*/
max?: number,

/**
* The current value of this component's range. (should be an integer)
*/
now?: number,

/**
* A textual description of this component's value. (will override minimum, current, and maximum if set)
*/
text?: string,
|}>;
2 changes: 2 additions & 0 deletions Libraries/Components/View/ViewPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type {TVViewProps} from 'TVViewPropTypes';
import type {
AccessibilityRole,
AccessibilityState,
AccessibilityValue,
AccessibilityActionEvent,
AccessibilityActionInfo,
} from './ViewAccessibility';
Expand Down Expand Up @@ -418,6 +419,7 @@ export type ViewProps = $ReadOnly<{|
* Indicates to accessibility services that UI Component is in a specific State.
*/
accessibilityState?: ?AccessibilityState,
accessibilityValue?: ?AccessibilityValue,

/**
* Provides an array of custom actions available for accessibility.
Expand Down
1 change: 1 addition & 0 deletions Libraries/DeprecatedPropTypes/DeprecatedViewPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ module.exports = {
accessibilityRole: PropTypes.oneOf(DeprecatedAccessibilityRoles),

accessibilityState: PropTypes.object,
accessibilityValue: PropTypes.object,
/**
* Indicates to accessibility services whether the user should be notified
* when this view changes. Works for Android API >= 19 only.
Expand Down
88 changes: 88 additions & 0 deletions RNTester/js/AccessibilityExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,88 @@ class AccessibilityActionsExample extends React.Component {
}
}

class FakeSliderExample extends React.Component {
state = {
current: 50,
textualValue: 'center',
};

increment = () => {
let newValue = this.state.current + 2;
if (newValue > 100) {
newValue = 100;
}
this.setState({
current: newValue,
});
};

decrement = () => {
let newValue = this.state.current - 2;
if (newValue < 0) {
newValue = 0;
}
this.setState({
current: newValue,
});
};

render() {
return (
<View>
<View
accessible={true}
accessibilityLabel="Fake Slider"
accessibilityRole="adjustable"
accessibilityActions={[{name: 'increment'}, {name: 'decrement'}]}
onAccessibilityAction={event => {
switch (event.nativeEvent.actionName) {
case 'increment':
this.increment();
break;
case 'decrement':
this.decrement();
break;
}
}}
accessibilityValue={{
min: 0,
now: this.state.current,
max: 100,
}}>
<Text>Fake Slider</Text>
</View>
<View
accessible={true}
accessibilityLabel="Equalizer"
accessibilityRole="adjustable"
accessibilityActions={[{name: 'increment'}, {name: 'decrement'}]}
onAccessibilityAction={event => {
switch (event.nativeEvent.actionName) {
case 'increment':
if (this.state.textualValue === 'center') {
this.setState({textualValue: 'right'});
} else if (this.state.textualValue === 'left') {
this.setState({textualValue: 'center'});
}
break;
case 'decrement':
if (this.state.textualValue === 'center') {
this.setState({textualValue: 'left'});
} else if (this.state.textualValue === 'right') {
this.setState({textualValue: 'center'});
}
break;
}
}}
accessibilityValue={{text: this.state.textualValue}}>
<Text>Equalizer</Text>
</View>
</View>
);
}
}

class ScreenReaderStatusExample extends React.Component<{}> {
state = {
screenReaderEnabled: false,
Expand Down Expand Up @@ -592,6 +674,12 @@ exports.examples = [
return <AccessibilityActionsExample />;
},
},
{
title: 'Fake Slider Example',
render(): React.Element<typeof FakeSliderExample> {
return <FakeSliderExample />;
},
},
{
title: 'Check if the screen reader is enabled',
render(): React.Element<typeof ScreenReaderStatusExample> {
Expand Down
20 changes: 20 additions & 0 deletions React/Views/RCTView.m
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,26 @@ - (NSString *)accessibilityValue
[valueComponents addObject:stateDescriptions[@"busy"]];
}
}

// handle accessibilityValue

if (self.accessibilityValueInternal) {
id min = self.accessibilityValueInternal[@"min"];
id now = self.accessibilityValueInternal[@"now"];
id max = self.accessibilityValueInternal[@"max"];
id text = self.accessibilityValueInternal[@"text"];
if (text && [text isKindOfClass:[NSString class]]) {
[valueComponents addObject:text];
} else if ([min isKindOfClass:[NSNumber class]] &&
[now isKindOfClass:[NSNumber class]] &&
[max isKindOfClass:[NSNumber class]] &&
([min intValue] < [max intValue]) &&
([min intValue] <= [now intValue] && [now intValue] <= [max intValue])) {
int val = ([now intValue]*100)/([max intValue]-[min intValue]);
[valueComponents addObject:[NSString stringWithFormat:@"%d percent", val]];
}
}

if (valueComponents.count > 0) {
return [valueComponents componentsJoinedByString:@", "];
}
Expand Down
1 change: 1 addition & 0 deletions React/Views/RCTViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(__unused NSDictio
RCT_REMAP_VIEW_PROPERTY(accessible, reactAccessibilityElement.isAccessibilityElement, BOOL)
RCT_REMAP_VIEW_PROPERTY(accessibilityActions, reactAccessibilityElement.accessibilityActions, NSDictionaryArray)
RCT_REMAP_VIEW_PROPERTY(accessibilityLabel, reactAccessibilityElement.accessibilityLabel, NSString)
RCT_REMAP_VIEW_PROPERTY(accessibilityValue, reactAccessibilityElement.accessibilityValueInternal, NSDictionary)
RCT_REMAP_VIEW_PROPERTY(accessibilityViewIsModal, reactAccessibilityElement.accessibilityViewIsModal, BOOL)
RCT_REMAP_VIEW_PROPERTY(accessibilityIgnoresInvertColors, reactAccessibilityElement.shouldAccessibilityIgnoresInvertColors, BOOL)
RCT_REMAP_VIEW_PROPERTY(onAccessibilityAction, reactAccessibilityElement.onAccessibilityAction, RCTDirectEventBlock)
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) NSString *accessibilityRole;
@property (nonatomic, copy) NSDictionary<NSString *, id> *accessibilityState;
@property (nonatomic, copy) NSArray <NSDictionary *> *accessibilityActions;
@property (nonatomic, copy) NSDictionary *accessibilityValueInternal;

#if RCT_DEV

Expand Down
9 changes: 9 additions & 0 deletions React/Views/UIView+React.m
Original file line number Diff line number Diff line change
Expand Up @@ -344,4 +344,13 @@ - (void)setAccessibilityState:(NSDictionary<NSString *, id> *)accessibilityState
objc_setAssociatedObject(self, @selector(accessibilityState), accessibilityState, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSDictionary<NSString *, id> *)accessibilityValueInternal
{
return objc_getAssociatedObject(self, _cmd);
}
- (void)setAccessibilityValueInternal:(NSDictionary<NSString *, id> *)accessibilityValue
{
objc_setAssociatedObject(self, @selector(accessibilityValueInternal), accessibilityValue, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
import android.os.Build;
import android.view.View;
import android.view.ViewParent;

import java.util.ArrayList;
import java.util.HashMap;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.facebook.react.R;
import com.facebook.react.bridge.Dynamic;
Expand All @@ -24,9 +23,10 @@
import com.facebook.react.uimanager.util.ReactFindViewUtil;
import java.util.Locale;

import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;

/**
* Base class that should be suitable for the majority of subclasses of {@link ViewManager}.
Expand Down Expand Up @@ -66,7 +66,7 @@ public abstract class BaseViewManager<T extends View, C extends LayoutShadowNode
new MatrixMathHelper.MatrixDecompositionContext();
private static double[] sTransformDecompositionArray = new double[16];

public static final HashMap<String, Integer> sStateDescription = new HashMap<String, Integer>();
public static final Map<String, Integer> sStateDescription = new HashMap<>();

static {
sStateDescription.put("busy", R.string.state_busy_description);
Expand Down Expand Up @@ -115,7 +115,7 @@ public void setZIndex(T view, float zIndex) {
int integerZIndex = Math.round(zIndex);
ViewGroupManager.setViewZIndex(view, integerZIndex);
ViewParent parent = view.getParent();
if (parent != null && parent instanceof ReactZIndexedViewGroup) {
if (parent instanceof ReactZIndexedViewGroup) {
((ReactZIndexedViewGroup) parent).updateDrawingOrder();
}
}
Expand Down Expand Up @@ -180,7 +180,8 @@ public void setViewState(@Nonnull T view, @Nullable ReadableMap accessibilitySta
private void updateViewContentDescription(@Nonnull T view) {
final String accessibilityLabel = (String) view.getTag(R.id.accessibility_label);
final ReadableMap accessibilityState = (ReadableMap) view.getTag(R.id.accessibility_state);
final ArrayList<String> contentDescription = new ArrayList<String>();
final List<String> contentDescription = new ArrayList<>();
final ReadableMap accessibilityValue = (ReadableMap) view.getTag(R.id.accessibility_value);
if (accessibilityLabel != null) {
contentDescription.add(accessibilityLabel);
}
Expand All @@ -198,6 +199,12 @@ private void updateViewContentDescription(@Nonnull T view) {
}
}
}
if (accessibilityValue != null && accessibilityValue.hasKey("text")) {
final Dynamic text = accessibilityValue.getDynamic("text");
if (text != null && text.getType() == ReadableType.String) {
contentDescription.add(text.asString());
}
}
if (contentDescription.size() > 0) {
view.setContentDescription(TextUtils.join(", ", contentDescription));
}
Expand All @@ -212,6 +219,18 @@ public void setAccessibilityActions(T view, ReadableArray accessibilityActions)
view.setTag(R.id.accessibility_actions, accessibilityActions);
}

@ReactProp(name = ViewProps.ACCESSIBILITY_VALUE)
public void setAccessibilityValue(T view, ReadableMap accessibilityValue) {
if (accessibilityValue == null) {
return;
}

view.setTag(R.id.accessibility_value, accessibilityValue);
if (accessibilityValue.hasKey("text")) {
updateViewContentDescription(view);
}
}

@ReactProp(name = PROP_IMPORTANT_FOR_ACCESSIBILITY)
public void setImportantForAccessibility(
@Nonnull T view, @Nullable String importantForAccessibility) {
Expand Down
Loading

0 comments on commit fa50c08

Please sign in to comment.