From 6ef7eaf6630ad6fdd97c5d377771e2df72fcff7e Mon Sep 17 00:00:00 2001 From: Georgiy Kassabli Date: Mon, 18 May 2015 07:32:21 -0700 Subject: [PATCH] Added accessibility traits support to View class --- Libraries/Components/View/View.js | 28 +++++++++++++++++++ .../ReactNative/ReactNativeViewAttributes.js | 1 + React/Base/RCTConvert.h | 16 +++++++++++ React/Base/RCTConvert.m | 16 +++++++++++ React/Views/RCTViewManager.m | 25 +++++++++++++++++ 5 files changed, 86 insertions(+) diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js index c3659e961..7fb9d34a6 100644 --- a/Libraries/Components/View/View.js +++ b/Libraries/Components/View/View.js @@ -24,6 +24,26 @@ var createReactNativeComponentClass = require('createReactNativeComponentClass') var stylePropType = StyleSheetPropType(ViewStylePropTypes); +var AccessibilityTraits = [ + 'none', + 'button', + 'link', + 'header', + 'search', + 'image', + 'selected', + 'plays', + 'key', + 'text', + 'summary', + 'disabled', + 'frequentUpdates', + 'startsMedia', + 'adjustable', + 'allowsDirectInteraction', + 'pageTurn', +]; + /** * The most fundamental component for building UI, `View` is a * container that supports layout with flexbox, style, some touch handling, and @@ -70,6 +90,14 @@ var View = React.createClass({ */ accessibilityLabel: PropTypes.string, + /** + * Provides additional traits to screen reader. By default no traits are + * provided unless specified otherwise in element + */ + accessibilityTraits: PropTypes.oneOfType([ + PropTypes.oneOf(AccessibilityTraits), + PropTypes.arrayOf(PropTypes.oneOf(AccessibilityTraits)), + ]), /** * Used to locate this view in end-to-end tests. */ diff --git a/Libraries/ReactNative/ReactNativeViewAttributes.js b/Libraries/ReactNative/ReactNativeViewAttributes.js index d154d045f..e9e9bbd56 100644 --- a/Libraries/ReactNative/ReactNativeViewAttributes.js +++ b/Libraries/ReactNative/ReactNativeViewAttributes.js @@ -19,6 +19,7 @@ ReactNativeViewAttributes.UIView = { pointerEvents: true, accessible: true, accessibilityLabel: true, + accessibilityTraits: true, testID: true, onLayout: true, }; diff --git a/React/Base/RCTConvert.h b/React/Base/RCTConvert.h index dd99ac9fb..0f73c2829 100644 --- a/React/Base/RCTConvert.h +++ b/React/Base/RCTConvert.h @@ -141,6 +141,7 @@ RCT_EXTERN BOOL RCTCopyProperty(id target, id source, NSString *keyPath); * Underlying implementations of RCT_XXX_CONVERTER macros. Ignore these. */ RCT_EXTERN NSNumber *RCTConvertEnumValue(const char *, NSDictionary *, NSNumber *, id); +RCT_EXTERN NSNumber *RCTConvertMultiEnumValue(const char *, NSDictionary *, NSNumber *, id); RCT_EXTERN NSArray *RCTConvertArrayValue(SEL, id); RCT_EXTERN void RCTLogConvertError(id, const char *); @@ -194,6 +195,21 @@ RCT_CUSTOM_CONVERTER(type, type, [[self NSNumber:json] getter]) return [RCTConvertEnumValue(#type, mapping, @(default), json) getter]; \ } +/** + * This macro is used for creating converters for enum types for + * multiple enum values combined with | operator + */ +#define RCT_MULTI_ENUM_CONVERTER(type, values, default, getter) \ ++ (type)type:(id)json \ +{ \ + static NSDictionary *mapping; \ + static dispatch_once_t onceToken; \ + dispatch_once(&onceToken, ^{ \ + mapping = values; \ + }); \ + return [RCTConvertMultiEnumValue(#type, mapping, @(default), json) getter]; \ +} + /** * This macro is used for creating converter functions for typed arrays. */ diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m index 06b5aa023..0047f8461 100644 --- a/React/Base/RCTConvert.m +++ b/React/Base/RCTConvert.m @@ -175,6 +175,22 @@ NSNumber *RCTConvertEnumValue(const char *typeName, NSDictionary *mapping, NSNum return value ?: defaultValue; } +NSNumber *RCTConvertMultiEnumValue(const char *typeName, NSDictionary *mapping, NSNumber *defaultValue, id json) +{ + if ([json isKindOfClass:[NSArray class]]) { + if ([json count] == 0) { + return defaultValue; + } + long long result = 0; + for (id arrayElement in json) { + NSNumber *value = RCTConvertEnumValue(typeName, mapping, defaultValue, arrayElement); + result |= [value longLongValue]; + } + return @(result); + } + return RCTConvertEnumValue(typeName, mapping, defaultValue, json); +} + RCT_ENUM_CONVERTER(NSTextAlignment, (@{ @"auto": @(NSTextAlignmentNatural), @"left": @(NSTextAlignmentLeft), diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index cb42c7ec5..3bedccdc8 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -18,6 +18,30 @@ #import "RCTUtils.h" #import "RCTView.h" +@implementation RCTConvert(UIAccessibilityTraits) + +RCT_MULTI_ENUM_CONVERTER(UIAccessibilityTraits, (@{ + @"none": @(UIAccessibilityTraitNone), + @"button": @(UIAccessibilityTraitButton), + @"link": @(UIAccessibilityTraitLink), + @"header": @(UIAccessibilityTraitHeader), + @"search": @(UIAccessibilityTraitSearchField), + @"image": @(UIAccessibilityTraitImage), + @"selected": @(UIAccessibilityTraitSelected), + @"plays": @(UIAccessibilityTraitPlaysSound), + @"key": @(UIAccessibilityTraitKeyboardKey), + @"text": @(UIAccessibilityTraitStaticText), + @"summary": @(UIAccessibilityTraitSummaryElement), + @"disabled": @(UIAccessibilityTraitNotEnabled), + @"frequentUpdates": @(UIAccessibilityTraitUpdatesFrequently), + @"startsMedia": @(UIAccessibilityTraitStartsMediaSession), + @"adjustable": @(UIAccessibilityTraitAdjustable), + @"allowsDirectInteraction": @(UIAccessibilityTraitAllowsDirectInteraction), + @"pageTurn": @(UIAccessibilityTraitCausesPageTurn), + }), UIAccessibilityTraitNone, unsignedLongLongValue) + +@end + @implementation RCTViewManager @synthesize bridge = _bridge; @@ -67,6 +91,7 @@ RCT_EXPORT_MODULE() #pragma mark - View properties RCT_EXPORT_VIEW_PROPERTY(accessibilityLabel, NSString) +RCT_EXPORT_VIEW_PROPERTY(accessibilityTraits, UIAccessibilityTraits) RCT_EXPORT_VIEW_PROPERTY(backgroundColor, UIColor) RCT_REMAP_VIEW_PROPERTY(accessible, isAccessibilityElement, BOOL) RCT_REMAP_VIEW_PROPERTY(testID, accessibilityIdentifier, NSString)