Skip to content

Commit 20718e6

Browse files
gabrieldonadelfacebook-github-bot
authored andcommitted
feat: Add role prop to Text component (#34976)
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
1 parent 8ad86c7 commit 20718e6

File tree

6 files changed

+244
-69
lines changed

6 files changed

+244
-69
lines changed

Libraries/Components/View/View.js

+2-69
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type {ViewProps} from './ViewPropTypes';
1212

1313
import flattenStyle from '../../StyleSheet/flattenStyle';
1414
import TextAncestor from '../../Text/TextAncestor';
15+
import {getAccessibilityRoleFromRole} from '../../Utilities/AcessibilityMapping';
1516
import ViewNativeComponent from './ViewNativeComponent';
1617
import * as React from 'react';
1718

@@ -80,74 +81,6 @@ const View: React.AbstractComponent<
8081
text: ariaValueText ?? accessibilityValue?.text,
8182
};
8283

83-
// Map role values to AccessibilityRole values
84-
const roleToAccessibilityRoleMapping = {
85-
alert: 'alert',
86-
alertdialog: undefined,
87-
application: undefined,
88-
article: undefined,
89-
banner: undefined,
90-
button: 'button',
91-
cell: undefined,
92-
checkbox: 'checkbox',
93-
columnheader: undefined,
94-
combobox: 'combobox',
95-
complementary: undefined,
96-
contentinfo: undefined,
97-
definition: undefined,
98-
dialog: undefined,
99-
directory: undefined,
100-
document: undefined,
101-
feed: undefined,
102-
figure: undefined,
103-
form: undefined,
104-
grid: 'grid',
105-
group: undefined,
106-
heading: 'header',
107-
img: 'image',
108-
link: 'link',
109-
list: 'list',
110-
listitem: undefined,
111-
log: undefined,
112-
main: undefined,
113-
marquee: undefined,
114-
math: undefined,
115-
menu: 'menu',
116-
menubar: 'menubar',
117-
menuitem: 'menuitem',
118-
meter: undefined,
119-
navigation: undefined,
120-
none: 'none',
121-
note: undefined,
122-
presentation: 'none',
123-
progressbar: 'progressbar',
124-
radio: 'radio',
125-
radiogroup: 'radiogroup',
126-
region: undefined,
127-
row: undefined,
128-
rowgroup: undefined,
129-
rowheader: undefined,
130-
scrollbar: 'scrollbar',
131-
searchbox: 'search',
132-
separator: undefined,
133-
slider: 'adjustable',
134-
spinbutton: 'spinbutton',
135-
status: undefined,
136-
summary: 'summary',
137-
switch: 'switch',
138-
tab: 'tab',
139-
table: undefined,
140-
tablist: 'tablist',
141-
tabpanel: undefined,
142-
term: undefined,
143-
timer: 'timer',
144-
toolbar: 'toolbar',
145-
tooltip: undefined,
146-
tree: undefined,
147-
treegrid: undefined,
148-
treeitem: undefined,
149-
};
150-
15184
const flattenedStyle = flattenStyle(style);
15285
const newPointerEvents = flattenedStyle?.pointerEvents || pointerEvents;
15386

@@ -162,7 +95,7 @@ const View: React.AbstractComponent<
16295
focusable={tabIndex !== undefined ? !tabIndex : focusable}
16396
accessibilityState={_accessibilityState}
16497
accessibilityRole={
165-
role ? roleToAccessibilityRoleMapping[role] : accessibilityRole
98+
role ? getAccessibilityRoleFromRole(role) : accessibilityRole
16699
}
167100
accessibilityElementsHidden={
168101
ariaHidden ?? accessibilityElementsHidden

Libraries/Components/View/ViewAccessibility.d.ts

+71
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ export interface AccessibilityProps
101101

102102
'aria-live'?: ('polite' | 'assertive' | 'off') | undefined;
103103
'aria-modal'?: boolean | undefined;
104+
105+
/**
106+
* Indicates to accessibility services to treat UI component like a specific role.
107+
*/
108+
role?: Role;
104109
}
105110

106111
export type AccessibilityActionInfo = Readonly<{
@@ -286,3 +291,69 @@ export interface AccessibilityPropsIOS {
286291
*/
287292
accessibilityIgnoresInvertColors?: boolean | undefined;
288293
}
294+
295+
export type Role =
296+
| 'alert'
297+
| 'alertdialog'
298+
| 'application'
299+
| 'article'
300+
| 'banner'
301+
| 'button'
302+
| 'cell'
303+
| 'checkbox'
304+
| 'columnheader'
305+
| 'combobox'
306+
| 'complementary'
307+
| 'contentinfo'
308+
| 'definition'
309+
| 'dialog'
310+
| 'directory'
311+
| 'document'
312+
| 'feed'
313+
| 'figure'
314+
| 'form'
315+
| 'grid'
316+
| 'group'
317+
| 'heading'
318+
| 'img'
319+
| 'link'
320+
| 'list'
321+
| 'listitem'
322+
| 'log'
323+
| 'main'
324+
| 'marquee'
325+
| 'math'
326+
| 'menu'
327+
| 'menubar'
328+
| 'menuitem'
329+
| 'meter'
330+
| 'navigation'
331+
| 'none'
332+
| 'note'
333+
| 'presentation'
334+
| 'progressbar'
335+
| 'radio'
336+
| 'radiogroup'
337+
| 'region'
338+
| 'row'
339+
| 'rowgroup'
340+
| 'rowheader'
341+
| 'scrollbar'
342+
| 'searchbox'
343+
| 'separator'
344+
| 'slider'
345+
| 'spinbutton'
346+
| 'status'
347+
| 'summary'
348+
| 'switch'
349+
| 'tab'
350+
| 'table'
351+
| 'tablist'
352+
| 'tabpanel'
353+
| 'term'
354+
| 'timer'
355+
| 'toolbar'
356+
| 'tooltip'
357+
| 'tree'
358+
| 'treegrid'
359+
| 'treeitem';

Libraries/Text/Text.js

+9
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import usePressability from '../Pressability/usePressability';
1515
import flattenStyle from '../StyleSheet/flattenStyle';
1616
import processColor from '../StyleSheet/processColor';
1717
import StyleSheet from '../StyleSheet/StyleSheet';
18+
import {getAccessibilityRoleFromRole} from '../Utilities/AcessibilityMapping';
1819
import Platform from '../Utilities/Platform';
1920
import TextAncestor from './TextAncestor';
2021
import {NativeText, NativeVirtualText} from './TextNativeComponent';
@@ -34,6 +35,7 @@ const Text: React.AbstractComponent<
3435
const {
3536
accessible,
3637
accessibilityLabel,
38+
accessibilityRole,
3739
allowFontScaling,
3840
'aria-busy': ariaBusy,
3941
'aria-checked': ariaChecked,
@@ -55,6 +57,7 @@ const Text: React.AbstractComponent<
5557
onResponderTerminationRequest,
5658
onStartShouldSetResponder,
5759
pressRetentionOffset,
60+
role,
5861
suppressHighlighting,
5962
...restProps
6063
} = props;
@@ -223,6 +226,9 @@ const Text: React.AbstractComponent<
223226
accessibilityState={_accessibilityState}
224227
{...eventHandlersForText}
225228
accessibilityLabel={ariaLabel ?? accessibilityLabel}
229+
accessibilityRole={
230+
role ? getAccessibilityRoleFromRole(role) : accessibilityRole
231+
}
226232
isHighlighted={isHighlighted}
227233
isPressable={isPressable}
228234
selectable={_selectable}
@@ -246,6 +252,9 @@ const Text: React.AbstractComponent<
246252
}
247253
accessibilityLabel={ariaLabel ?? accessibilityLabel}
248254
accessibilityState={nativeTextAccessibilityState}
255+
accessibilityRole={
256+
role ? getAccessibilityRoleFromRole(role) : accessibilityRole
257+
}
249258
allowFontScaling={allowFontScaling !== false}
250259
ellipsizeMode={ellipsizeMode ?? 'tail'}
251260
isHighlighted={isHighlighted}

Libraries/Text/TextProps.js

+6
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import type {
1515
AccessibilityActionInfo,
1616
AccessibilityRole,
1717
AccessibilityState,
18+
Role,
1819
} from '../Components/View/ViewAccessibility';
1920
import type {TextStyleProp} from '../StyleSheet/StyleSheet';
2021
import type {
@@ -176,6 +177,11 @@ export type TextProps = $ReadOnly<{|
176177
*/
177178
pressRetentionOffset?: ?PressRetentionOffset,
178179

180+
/**
181+
* Indicates to accessibility services to treat UI component like a specific role.
182+
*/
183+
role?: ?Role,
184+
179185
/**
180186
* Lets the user select text.
181187
*
+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict-local
8+
* @format
9+
*/
10+
11+
'use strict';
12+
13+
import type {
14+
AccessibilityRole,
15+
Role,
16+
} from '../Components/View/ViewAccessibility';
17+
18+
// Map role values to AccessibilityRole values
19+
export function getAccessibilityRoleFromRole(role: Role): ?AccessibilityRole {
20+
switch (role) {
21+
case 'alert':
22+
return 'alert';
23+
case 'alertdialog':
24+
return undefined;
25+
case 'application':
26+
return undefined;
27+
case 'article':
28+
return undefined;
29+
case 'banner':
30+
return undefined;
31+
case 'button':
32+
return 'button';
33+
case 'cell':
34+
return undefined;
35+
case 'checkbox':
36+
return 'checkbox';
37+
case 'columnheader':
38+
return undefined;
39+
case 'combobox':
40+
return 'combobox';
41+
case 'complementary':
42+
return undefined;
43+
case 'contentinfo':
44+
return undefined;
45+
case 'definition':
46+
return undefined;
47+
case 'dialog':
48+
return undefined;
49+
case 'directory':
50+
return undefined;
51+
case 'document':
52+
return undefined;
53+
case 'feed':
54+
return undefined;
55+
case 'figure':
56+
return undefined;
57+
case 'form':
58+
return undefined;
59+
case 'grid':
60+
return 'grid';
61+
case 'group':
62+
return undefined;
63+
case 'heading':
64+
return 'header';
65+
case 'img':
66+
return 'image';
67+
case 'link':
68+
return 'link';
69+
case 'list':
70+
return 'list';
71+
case 'listitem':
72+
return undefined;
73+
case 'log':
74+
return undefined;
75+
case 'main':
76+
return undefined;
77+
case 'marquee':
78+
return undefined;
79+
case 'math':
80+
return undefined;
81+
case 'menu':
82+
return 'menu';
83+
case 'menubar':
84+
return 'menubar';
85+
case 'menuitem':
86+
return 'menuitem';
87+
case 'meter':
88+
return undefined;
89+
case 'navigation':
90+
return undefined;
91+
case 'none':
92+
return 'none';
93+
case 'note':
94+
return undefined;
95+
case 'presentation':
96+
return 'none';
97+
case 'progressbar':
98+
return 'progressbar';
99+
case 'radio':
100+
return 'radio';
101+
case 'radiogroup':
102+
return 'radiogroup';
103+
case 'region':
104+
return undefined;
105+
case 'row':
106+
return undefined;
107+
case 'rowgroup':
108+
return undefined;
109+
case 'rowheader':
110+
return undefined;
111+
case 'scrollbar':
112+
return 'scrollbar';
113+
case 'searchbox':
114+
return 'search';
115+
case 'separator':
116+
return undefined;
117+
case 'slider':
118+
return 'adjustable';
119+
case 'spinbutton':
120+
return 'spinbutton';
121+
case 'status':
122+
return undefined;
123+
case 'summary':
124+
return 'summary';
125+
case 'switch':
126+
return 'switch';
127+
case 'tab':
128+
return 'tab';
129+
case 'table':
130+
return undefined;
131+
case 'tablist':
132+
return 'tablist';
133+
case 'tabpanel':
134+
return undefined;
135+
case 'term':
136+
return undefined;
137+
case 'timer':
138+
return 'timer';
139+
case 'toolbar':
140+
return 'toolbar';
141+
case 'tooltip':
142+
return undefined;
143+
case 'tree':
144+
return undefined;
145+
case 'treegrid':
146+
return undefined;
147+
case 'treeitem':
148+
return undefined;
149+
}
150+
151+
return undefined;
152+
}

0 commit comments

Comments
 (0)