diff --git a/docs/guides/accessibility.md b/docs/guides/accessibility.md index 518b36b02..b1f971ec1 100644 --- a/docs/guides/accessibility.md +++ b/docs/guides/accessibility.md @@ -35,6 +35,34 @@ using `aria-label`. ``` +### accessibilityLiveRegion + +When components dynamically change we may need to inform the user. The +`accessibilityLiveRegion` property serves this purpose and can be set to +`none`, `polite` and `assertive`. On web, `accessibilityLiveRegion` is +implemented using `aria-live`. + +* `none`: Accessibility services should not announce changes to this view. +* `polite`: Accessibility services should announce changes to this view. +* `assertive`: Accessibility services should interrupt ongoing speech to immediately announce changes to this view. + +``` + + + Click me + + + + + Clicked {this.state.count} times + +``` + +In the above example, method `_addOne` changes the `state.count` variable. As +soon as an end user clicks the `TouchableWithoutFeedback`, screen readers +announce text in the `Text` view because of its +`accessibilityLiveRegion="polite"` property. + ### accessibilityRole In some cases, we also want to alert the end user of the type of selected @@ -49,7 +77,8 @@ element][html-aria-url] and ARIA `role`, where possible. In most cases, both the element and ARIA `role` are rendered. While this may contradict some ARIA recommendations, it also helps avoid certain browser bugs, HTML5 conformance errors, and accessibility anti-patterns (e.g., giving a `heading` role to a -`button` element). +`button` element). On the Web, `accessibilityRole` supports more values than +React Native does for [Andriod and iOS](https://facebook.github.io/react-native/docs/accessibility#accessibilityrole-ios-android). Straight-forward examples: @@ -85,33 +114,12 @@ Note: Avoid changing `accessibilityRole` values over time or after user actions. Generally, accessibility APIs do not provide a means of notifying assistive technologies of a `role` value change. -### accessibilityLiveRegion - -When components dynamically change we may need to inform the user. The -`accessibilityLiveRegion` property serves this purpose and can be set to -`none`, `polite` and `assertive`. On web, `accessibilityLiveRegion` is -implemented using `aria-live`. - -* `none`: Accessibility services should not announce changes to this view. -* `polite`: Accessibility services should announce changes to this view. -* `assertive`: Accessibility services should interrupt ongoing speech to immediately announce changes to this view. +### accessibilityStates -``` - - - Click me - - - - - Clicked {this.state.count} times - -``` - -In the above example, method `_addOne` changes the `state.count` variable. As -soon as an end user clicks the `TouchableWithoutFeedback`, screen readers -announce text in the `Text` view because of its -`accessibilityLiveRegion="polite"` property. +The `accessibilityStates` prop is an array of values used to infer the +analogous ARIA states, e.g., `aria-disabled`, `aria-pressed`, `aria-selected`. +On the Web, `accessibilityStates` supports more values than React Native does +for [Andriod and iOS](https://facebook.github.io/react-native/docs/accessibility#accessibilitystate-ios-android). ### importantForAccessibility diff --git a/packages/react-native-web/src/exports/View/ViewPropTypes.js b/packages/react-native-web/src/exports/View/ViewPropTypes.js index 2d36c910c..5683360b0 100644 --- a/packages/react-native-web/src/exports/View/ViewPropTypes.js +++ b/packages/react-native-web/src/exports/View/ViewPropTypes.js @@ -11,7 +11,7 @@ import EdgeInsetsPropType, { type EdgeInsetsProp } from '../EdgeInsetsPropType'; import StyleSheetPropType from '../../modules/StyleSheetPropType'; import ViewStylePropTypes from './ViewStylePropTypes'; -import { any, array, bool, func, object, oneOf, oneOfType, string } from 'prop-types'; +import { any, array, arrayOf, bool, func, object, oneOf, oneOfType, string } from 'prop-types'; const stylePropType = StyleSheetPropType(ViewStylePropTypes); @@ -33,6 +33,7 @@ export type ViewProps = { accessibilityLabel?: string, accessibilityLiveRegion?: 'none' | 'polite' | 'assertive', accessibilityRole?: string, + accessibilityStates?: Array, accessibilityTraits?: string | Array, accessible?: boolean, children?: any, @@ -83,6 +84,17 @@ const ViewPropTypes = { accessibilityLabel: string, accessibilityLiveRegion: oneOf(['assertive', 'none', 'polite']), accessibilityRole: string, + accessibilityStates: arrayOf(oneOf([ + 'disabled', + 'selected', + /* web-only */ + 'busy', + 'checked', + 'expanded', + 'grabbed', + 'invalid', + 'pressed' + ])), accessibilityTraits: oneOfType([array, string]), accessible: bool, children: any, diff --git a/packages/react-native-web/src/exports/View/filterSupportedProps.js b/packages/react-native-web/src/exports/View/filterSupportedProps.js index 41c718446..5f5a763c9 100644 --- a/packages/react-native-web/src/exports/View/filterSupportedProps.js +++ b/packages/react-native-web/src/exports/View/filterSupportedProps.js @@ -1,8 +1,9 @@ -const whitelist = { +const supportedProps = { accessibilityComponentType: true, accessibilityLabel: true, accessibilityLiveRegion: true, accessibilityRole: true, + accessibilityStates: true, accessibilityTraits: true, accessible: true, children: true, @@ -67,7 +68,7 @@ const filterSupportedProps = props => { const safeProps = {}; for (const prop in props) { if (props.hasOwnProperty(prop)) { - if (whitelist[prop] || prop.indexOf('aria-') === 0 || prop.indexOf('data-') === 0) { + if (supportedProps[prop] || prop.indexOf('aria-') === 0 || prop.indexOf('data-') === 0) { safeProps[prop] = props[prop]; } } diff --git a/packages/react-native-web/src/modules/AccessibilityUtil/__tests__/propsToAriaRole-test.js b/packages/react-native-web/src/modules/AccessibilityUtil/__tests__/propsToAriaRole-test.js index cab195655..0685c1457 100644 --- a/packages/react-native-web/src/modules/AccessibilityUtil/__tests__/propsToAriaRole-test.js +++ b/packages/react-native-web/src/modules/AccessibilityUtil/__tests__/propsToAriaRole-test.js @@ -25,4 +25,11 @@ describe('modules/AccessibilityUtil/propsToAriaRole', () => { }) ).toEqual('link'); }); + + test('when "accessibilityRole" is a native-only value', () => { + expect(propsToAriaRole({ accessibilityRole: 'none' })).toEqual('presentation'); + expect(propsToAriaRole({ accessibilityRole: 'imagebutton' })).toEqual(undefined); + // not really native-only, but used to allow Web to render