Skip to content

Commit

Permalink
feat: Add role prop to Text component (#34976)
Browse files Browse the repository at this point in the history
Summary:
As pointed out by necolas on #34424 (comment) we forgot we add the `role` prop mapping to the `Text` component. This PR adds a new `role` prop to `Text`, mapping the web `role` values to the already existing `accessibilityRole` prop and moves the `roleToAccessibilityRoleMapping` to a common file that can be imported by both the `Text` and `View` components as requested on #34424. This PR also updates the RNTester AcessebilityExample to include a test using this new prop.

## Changelog

[General] [Added] - Add role prop to Text component

Pull Request resolved: #34976

Test Plan:
1. Open the RNTester app and navigate to the Accessibility Example page
2. Test the `role` prop through the `Text with role = heading` section

Reviewed By: yungsters

Differential Revision: D40596039

Pulled By: jacdebug

fbshipit-source-id: f72f02e8bd32169423ea517ad18b598b52257b17
  • Loading branch information
gabrieldonadel authored and facebook-github-bot committed Oct 24, 2022
1 parent 8ad86c7 commit 20718e6
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 69 deletions.
71 changes: 2 additions & 69 deletions Libraries/Components/View/View.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {ViewProps} from './ViewPropTypes';

import flattenStyle from '../../StyleSheet/flattenStyle';
import TextAncestor from '../../Text/TextAncestor';
import {getAccessibilityRoleFromRole} from '../../Utilities/AcessibilityMapping';
import ViewNativeComponent from './ViewNativeComponent';
import * as React from 'react';

Expand Down Expand Up @@ -80,74 +81,6 @@ const View: React.AbstractComponent<
text: ariaValueText ?? accessibilityValue?.text,
};

// Map role values to AccessibilityRole values
const roleToAccessibilityRoleMapping = {
alert: 'alert',
alertdialog: undefined,
application: undefined,
article: undefined,
banner: undefined,
button: 'button',
cell: undefined,
checkbox: 'checkbox',
columnheader: undefined,
combobox: 'combobox',
complementary: undefined,
contentinfo: undefined,
definition: undefined,
dialog: undefined,
directory: undefined,
document: undefined,
feed: undefined,
figure: undefined,
form: undefined,
grid: 'grid',
group: undefined,
heading: 'header',
img: 'image',
link: 'link',
list: 'list',
listitem: undefined,
log: undefined,
main: undefined,
marquee: undefined,
math: undefined,
menu: 'menu',
menubar: 'menubar',
menuitem: 'menuitem',
meter: undefined,
navigation: undefined,
none: 'none',
note: undefined,
presentation: 'none',
progressbar: 'progressbar',
radio: 'radio',
radiogroup: 'radiogroup',
region: undefined,
row: undefined,
rowgroup: undefined,
rowheader: undefined,
scrollbar: 'scrollbar',
searchbox: 'search',
separator: undefined,
slider: 'adjustable',
spinbutton: 'spinbutton',
status: undefined,
summary: 'summary',
switch: 'switch',
tab: 'tab',
table: undefined,
tablist: 'tablist',
tabpanel: undefined,
term: undefined,
timer: 'timer',
toolbar: 'toolbar',
tooltip: undefined,
tree: undefined,
treegrid: undefined,
treeitem: undefined,
};

const flattenedStyle = flattenStyle(style);
const newPointerEvents = flattenedStyle?.pointerEvents || pointerEvents;

Expand All @@ -162,7 +95,7 @@ const View: React.AbstractComponent<
focusable={tabIndex !== undefined ? !tabIndex : focusable}
accessibilityState={_accessibilityState}
accessibilityRole={
role ? roleToAccessibilityRoleMapping[role] : accessibilityRole
role ? getAccessibilityRoleFromRole(role) : accessibilityRole
}
accessibilityElementsHidden={
ariaHidden ?? accessibilityElementsHidden
Expand Down
71 changes: 71 additions & 0 deletions Libraries/Components/View/ViewAccessibility.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ export interface AccessibilityProps

'aria-live'?: ('polite' | 'assertive' | 'off') | undefined;
'aria-modal'?: boolean | undefined;

/**
* Indicates to accessibility services to treat UI component like a specific role.
*/
role?: Role;
}

export type AccessibilityActionInfo = Readonly<{
Expand Down Expand Up @@ -286,3 +291,69 @@ export interface AccessibilityPropsIOS {
*/
accessibilityIgnoresInvertColors?: boolean | undefined;
}

export type Role =
| 'alert'
| 'alertdialog'
| 'application'
| 'article'
| 'banner'
| 'button'
| 'cell'
| 'checkbox'
| 'columnheader'
| 'combobox'
| 'complementary'
| 'contentinfo'
| 'definition'
| 'dialog'
| 'directory'
| 'document'
| 'feed'
| 'figure'
| 'form'
| 'grid'
| 'group'
| 'heading'
| 'img'
| 'link'
| 'list'
| 'listitem'
| 'log'
| 'main'
| 'marquee'
| 'math'
| 'menu'
| 'menubar'
| 'menuitem'
| 'meter'
| 'navigation'
| 'none'
| 'note'
| 'presentation'
| 'progressbar'
| 'radio'
| 'radiogroup'
| 'region'
| 'row'
| 'rowgroup'
| 'rowheader'
| 'scrollbar'
| 'searchbox'
| 'separator'
| 'slider'
| 'spinbutton'
| 'status'
| 'summary'
| 'switch'
| 'tab'
| 'table'
| 'tablist'
| 'tabpanel'
| 'term'
| 'timer'
| 'toolbar'
| 'tooltip'
| 'tree'
| 'treegrid'
| 'treeitem';
9 changes: 9 additions & 0 deletions Libraries/Text/Text.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import usePressability from '../Pressability/usePressability';
import flattenStyle from '../StyleSheet/flattenStyle';
import processColor from '../StyleSheet/processColor';
import StyleSheet from '../StyleSheet/StyleSheet';
import {getAccessibilityRoleFromRole} from '../Utilities/AcessibilityMapping';
import Platform from '../Utilities/Platform';
import TextAncestor from './TextAncestor';
import {NativeText, NativeVirtualText} from './TextNativeComponent';
Expand All @@ -34,6 +35,7 @@ const Text: React.AbstractComponent<
const {
accessible,
accessibilityLabel,
accessibilityRole,
allowFontScaling,
'aria-busy': ariaBusy,
'aria-checked': ariaChecked,
Expand All @@ -55,6 +57,7 @@ const Text: React.AbstractComponent<
onResponderTerminationRequest,
onStartShouldSetResponder,
pressRetentionOffset,
role,
suppressHighlighting,
...restProps
} = props;
Expand Down Expand Up @@ -223,6 +226,9 @@ const Text: React.AbstractComponent<
accessibilityState={_accessibilityState}
{...eventHandlersForText}
accessibilityLabel={ariaLabel ?? accessibilityLabel}
accessibilityRole={
role ? getAccessibilityRoleFromRole(role) : accessibilityRole
}
isHighlighted={isHighlighted}
isPressable={isPressable}
selectable={_selectable}
Expand All @@ -246,6 +252,9 @@ const Text: React.AbstractComponent<
}
accessibilityLabel={ariaLabel ?? accessibilityLabel}
accessibilityState={nativeTextAccessibilityState}
accessibilityRole={
role ? getAccessibilityRoleFromRole(role) : accessibilityRole
}
allowFontScaling={allowFontScaling !== false}
ellipsizeMode={ellipsizeMode ?? 'tail'}
isHighlighted={isHighlighted}
Expand Down
6 changes: 6 additions & 0 deletions Libraries/Text/TextProps.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
AccessibilityActionInfo,
AccessibilityRole,
AccessibilityState,
Role,
} from '../Components/View/ViewAccessibility';
import type {TextStyleProp} from '../StyleSheet/StyleSheet';
import type {
Expand Down Expand Up @@ -176,6 +177,11 @@ export type TextProps = $ReadOnly<{|
*/
pressRetentionOffset?: ?PressRetentionOffset,

/**
* Indicates to accessibility services to treat UI component like a specific role.
*/
role?: ?Role,

/**
* Lets the user select text.
*
Expand Down
152 changes: 152 additions & 0 deletions Libraries/Utilities/AcessibilityMapping.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/

'use strict';

import type {
AccessibilityRole,
Role,
} from '../Components/View/ViewAccessibility';

// Map role values to AccessibilityRole values
export function getAccessibilityRoleFromRole(role: Role): ?AccessibilityRole {
switch (role) {
case 'alert':
return 'alert';
case 'alertdialog':
return undefined;
case 'application':
return undefined;
case 'article':
return undefined;
case 'banner':
return undefined;
case 'button':
return 'button';
case 'cell':
return undefined;
case 'checkbox':
return 'checkbox';
case 'columnheader':
return undefined;
case 'combobox':
return 'combobox';
case 'complementary':
return undefined;
case 'contentinfo':
return undefined;
case 'definition':
return undefined;
case 'dialog':
return undefined;
case 'directory':
return undefined;
case 'document':
return undefined;
case 'feed':
return undefined;
case 'figure':
return undefined;
case 'form':
return undefined;
case 'grid':
return 'grid';
case 'group':
return undefined;
case 'heading':
return 'header';
case 'img':
return 'image';
case 'link':
return 'link';
case 'list':
return 'list';
case 'listitem':
return undefined;
case 'log':
return undefined;
case 'main':
return undefined;
case 'marquee':
return undefined;
case 'math':
return undefined;
case 'menu':
return 'menu';
case 'menubar':
return 'menubar';
case 'menuitem':
return 'menuitem';
case 'meter':
return undefined;
case 'navigation':
return undefined;
case 'none':
return 'none';
case 'note':
return undefined;
case 'presentation':
return 'none';
case 'progressbar':
return 'progressbar';
case 'radio':
return 'radio';
case 'radiogroup':
return 'radiogroup';
case 'region':
return undefined;
case 'row':
return undefined;
case 'rowgroup':
return undefined;
case 'rowheader':
return undefined;
case 'scrollbar':
return 'scrollbar';
case 'searchbox':
return 'search';
case 'separator':
return undefined;
case 'slider':
return 'adjustable';
case 'spinbutton':
return 'spinbutton';
case 'status':
return undefined;
case 'summary':
return 'summary';
case 'switch':
return 'switch';
case 'tab':
return 'tab';
case 'table':
return undefined;
case 'tablist':
return 'tablist';
case 'tabpanel':
return undefined;
case 'term':
return undefined;
case 'timer':
return 'timer';
case 'toolbar':
return 'toolbar';
case 'tooltip':
return undefined;
case 'tree':
return undefined;
case 'treegrid':
return undefined;
case 'treeitem':
return undefined;
}

return undefined;
}

0 comments on commit 20718e6

Please sign in to comment.