Summary:
This adds aliasing for accessibility state, it's used as requested on https://github.com/facebook/react-native/issues/34424.

- [aria-disabled](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-disabled) to equivalent [accessibilityState.disabled](https://reactnative.dev/docs/accessibility#accessibilitystate)
- [aria-busy](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-busy) to equivalent [accessibilityState.busy](https://reactnative.dev/docs/accessibility#accessibilitystate)
- [aria-checked](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-checked) to equivalent [accessibilityState.checked](https://reactnative.dev/docs/accessibility#accessibilitystate)
- [aria-expanded](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-expanded) to equivalent [accessibilityState.expanded](https://reactnative.dev/docs/accessibility#accessibilitystate)
- [aria-selected](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-selected) to equivalent [accessibilityState.selected](https://reactnative.dev/docs/accessibility#accessibilitystate)

## Changelog

[General] [Added] - Add aria-disabled, aria-busy, aria-checked, aria-expanded and aria-selected prop to core components

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

Pull Request resolved: https://github.com/facebook/react-native/pull/34524

Test Plan:
```js
<View
  aria-disabled={true}
  aria-selected={false}
  aria-checked={true}
  aria-expanded={true}
  aria-busy={true}
  style={{backgroundColor: '#527FE4', padding: 5}}>
  <Text style={{fontSize: 11}}>Blue background</Text>
</View>
```

Reviewed By: cipolleschi

Differential Revision: D39137790

Pulled By: jacdebug

fbshipit-source-id: 27b5c56e91731ba36bb4754d9862286a7a8191bc
This commit is contained in:
ankit-tailor 2022-09-08 03:38:39 -07:00 коммит произвёл Facebook GitHub Bot
Родитель a5622165c1
Коммит 98d84e571d
21 изменённых файлов: 549 добавлений и 127 удалений

Просмотреть файл

@ -147,6 +147,17 @@ type ButtonProps = $ReadOnly<{|
onAccessibilityAction?: ?(event: AccessibilityActionEvent) => mixed,
accessibilityState?: ?AccessibilityState,
/**
* alias for accessibilityState
*
* see https://reactnative.dev/docs/accessibility#accessibilitystate
*/
'aria-busy'?: ?boolean,
'aria-checked'?: ?boolean,
'aria-disabled'?: ?boolean,
'aria-expanded'?: ?boolean,
'aria-selected'?: ?boolean,
/**
* [Android] Controlling if a view fires accessibility events and if it is reported to accessibility services.
*/
@ -270,6 +281,12 @@ class Button extends React.Component<ButtonProps> {
render(): React.Node {
const {
accessibilityLabel,
accessibilityState,
'aria-busy': ariaBusy,
'aria-checked': ariaChecked,
'aria-disabled': ariaDisabled,
'aria-expanded': ariaExpanded,
'aria-selected': ariaSelected,
importantForAccessibility,
color,
onPress,
@ -298,15 +315,23 @@ class Button extends React.Component<ButtonProps> {
}
}
let _accessibilityState = {
busy: ariaBusy ?? accessibilityState?.busy,
checked: ariaChecked ?? accessibilityState?.checked,
disabled: ariaDisabled ?? accessibilityState?.disabled,
expanded: ariaExpanded ?? accessibilityState?.expanded,
selected: ariaSelected ?? accessibilityState?.selected,
};
const disabled =
this.props.disabled != null
? this.props.disabled
: this.props.accessibilityState?.disabled;
: _accessibilityState?.disabled;
const accessibilityState =
disabled !== this.props.accessibilityState?.disabled
? {...this.props.accessibilityState, disabled}
: this.props.accessibilityState;
_accessibilityState =
disabled !== _accessibilityState?.disabled
? {..._accessibilityState, disabled}
: _accessibilityState;
if (disabled) {
buttonStyles.push(styles.buttonDisabled);
@ -337,7 +362,7 @@ class Button extends React.Component<ButtonProps> {
accessibilityHint={accessibilityHint}
accessibilityLanguage={accessibilityLanguage}
accessibilityRole="button"
accessibilityState={accessibilityState}
accessibilityState={_accessibilityState}
importantForAccessibility={_importantForAccessibility}
hasTVPreferredFocus={hasTVPreferredFocus}
nextFocusDown={nextFocusDown}

Просмотреть файл

@ -52,6 +52,17 @@ type Props = $ReadOnly<{|
accessibilityValue?: ?AccessibilityValue,
accessibilityViewIsModal?: ?boolean,
accessible?: ?boolean,
/**
* alias for accessibilityState
*
* see https://reactnative.dev/docs/accessibility#accessibilitystate
*/
'aria-busy'?: ?boolean,
'aria-checked'?: ?boolean,
'aria-disabled'?: ?boolean,
'aria-expanded'?: ?boolean,
'aria-selected'?: ?boolean,
/**
* A value indicating whether the accessibility elements contained within
* this accessibility element are hidden.
@ -179,9 +190,15 @@ type Props = $ReadOnly<{|
* LTI update could not be added via codemod */
function Pressable(props: Props, forwardedRef): React.Node {
const {
accessible,
accessibilityState,
android_disableSound,
android_ripple,
accessible,
'aria-busy': ariaBusy,
'aria-checked': ariaChecked,
'aria-disabled': ariaDisabled,
'aria-expanded': ariaExpanded,
'aria-selected': ariaSelected,
cancelable,
children,
delayHoverIn,
@ -210,16 +227,22 @@ function Pressable(props: Props, forwardedRef): React.Node {
const [pressed, setPressed] = usePressState(testOnly_pressed === true);
const accessibilityState =
disabled != null
? {...props.accessibilityState, disabled}
: props.accessibilityState;
let _accessibilityState = {
busy: ariaBusy ?? accessibilityState?.busy,
checked: ariaChecked ?? accessibilityState?.checked,
disabled: ariaDisabled ?? accessibilityState?.disabled,
expanded: ariaExpanded ?? accessibilityState?.expanded,
selected: ariaSelected ?? accessibilityState?.selected,
};
_accessibilityState =
disabled != null ? {..._accessibilityState, disabled} : _accessibilityState;
const restPropsWithDefaults: React.ElementConfig<typeof View> = {
...restProps,
...android_rippleConfig?.viewProps,
accessible: accessible !== false,
accessibilityState,
accessibilityState: _accessibilityState,
focusable: focusable !== false,
hitSlop,
};

Просмотреть файл

@ -2,6 +2,15 @@
exports[`<Pressable /> should render as expected: should deep render when mocked (please verify output manually) 1`] = `
<View
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
@ -21,6 +30,15 @@ exports[`<Pressable /> should render as expected: should deep render when mocked
exports[`<Pressable /> should render as expected: should deep render when not mocked (please verify output manually) 1`] = `
<View
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
@ -54,7 +72,11 @@ exports[`<Pressable disabled={true} /> should be disabled when disabled is true:
<View
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
@ -78,7 +100,11 @@ exports[`<Pressable disabled={true} /> should be disabled when disabled is true:
<View
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
@ -118,7 +144,11 @@ exports[`<Pressable disabled={true} accessibilityState={{}} /> should be disable
<View
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
@ -142,7 +172,11 @@ exports[`<Pressable disabled={true} accessibilityState={{}} /> should be disable
<View
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
@ -184,8 +218,11 @@ exports[`<Pressable disabled={true} accessibilityState={{checked: true}} /> shou
<View
accessibilityState={
Object {
"busy": undefined,
"checked": true,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
@ -209,8 +246,11 @@ exports[`<Pressable disabled={true} accessibilityState={{checked: true}} /> shou
<View
accessibilityState={
Object {
"busy": undefined,
"checked": true,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
@ -260,7 +300,11 @@ exports[`<Pressable disabled={true} accessibilityState={{disabled: false}} /> sh
<View
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
@ -284,7 +328,11 @@ exports[`<Pressable disabled={true} accessibilityState={{disabled: false}} /> sh
<View
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}

Просмотреть файл

@ -1339,6 +1339,14 @@ function InternalTextInput(props: Props): React.Node {
// so omitting onBlur and onFocus pressability handlers here.
const {onBlur, onFocus, ...eventHandlers} = usePressability(config) || {};
const _accessibilityState = {
busy: props['aria-busy'] ?? props.accessibilityState?.busy,
checked: props['aria-checked'] ?? props.accessibilityState?.checked,
disabled: props['aria-disabled'] ?? props.accessibilityState?.disabled,
expanded: props['aria-expanded'] ?? props.accessibilityState?.expanded,
selected: props['aria-selected'] ?? props.accessibilityState?.selected,
};
if (Platform.OS === 'ios') {
const RCTTextInputView =
props.multiline === true
@ -1360,6 +1368,7 @@ function InternalTextInput(props: Props): React.Node {
{...props}
{...eventHandlers}
accessible={accessible}
accessibilityState={_accessibilityState}
submitBehavior={submitBehavior}
caretHidden={caretHidden}
dataDetectorTypes={props.dataDetectorTypes}
@ -1407,6 +1416,7 @@ function InternalTextInput(props: Props): React.Node {
{...props}
{...eventHandlers}
accessible={accessible}
accessibilityState={_accessibilityState}
autoCapitalize={autoCapitalize}
submitBehavior={submitBehavior}
caretHidden={caretHidden}

Просмотреть файл

@ -2,6 +2,15 @@
exports[`TextInput tests should render as expected: should deep render when mocked (please verify output manually) 1`] = `
<RCTSinglelineTextInputView
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
allowFontScaling={true}
focusable={true}
@ -31,6 +40,15 @@ exports[`TextInput tests should render as expected: should deep render when mock
exports[`TextInput tests should render as expected: should deep render when not mocked (please verify output manually) 1`] = `
<RCTSinglelineTextInputView
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
allowFontScaling={true}
focusable={true}

Просмотреть файл

@ -131,6 +131,18 @@ class TouchableBounce extends React.Component<Props, State> {
const {onBlur, onFocus, ...eventHandlersWithoutBlurAndFocus} =
this.state.pressability.getEventHandlers();
const _accessibilityState = {
busy: this.props['aria-busy'] ?? this.props.accessibilityState?.busy,
checked:
this.props['aria-checked'] ?? this.props.accessibilityState?.checked,
disabled:
this.props['aria-disabled'] ?? this.props.accessibilityState?.disabled,
expanded:
this.props['aria-expanded'] ?? this.props.accessibilityState?.expanded,
selected:
this.props['aria-selected'] ?? this.props.accessibilityState?.selected,
};
return (
<Animated.View
style={[{transform: [{scale: this.state.scale}]}, this.props.style]}
@ -139,7 +151,7 @@ class TouchableBounce extends React.Component<Props, State> {
accessibilityHint={this.props.accessibilityHint}
accessibilityLanguage={this.props.accessibilityLanguage}
accessibilityRole={this.props.accessibilityRole}
accessibilityState={this.props.accessibilityState}
accessibilityState={_accessibilityState}
accessibilityActions={this.props.accessibilityActions}
onAccessibilityAction={this.props.onAccessibilityAction}
accessibilityValue={this.props.accessibilityValue}

Просмотреть файл

@ -162,12 +162,14 @@ class TouchableNativeFeedback extends React.Component<Props, State> {
};
_createPressabilityConfig(): PressabilityConfig {
const accessibilityStateDisabled =
this.props['aria-disabled'] ?? this.props.accessibilityState?.disabled;
return {
cancelable: !this.props.rejectResponderTermination,
disabled:
this.props.disabled != null
? this.props.disabled
: this.props.accessibilityState?.disabled,
: accessibilityStateDisabled,
hitSlop: this.props.hitSlop,
delayLongPress: this.props.delayLongPress,
delayPressIn: this.props.delayPressIn,
@ -251,13 +253,25 @@ class TouchableNativeFeedback extends React.Component<Props, State> {
const {onBlur, onFocus, ...eventHandlersWithoutBlurAndFocus} =
this.state.pressability.getEventHandlers();
const accessibilityState =
let _accessibilityState = {
busy: this.props['aria-busy'] ?? this.props.accessibilityState?.busy,
checked:
this.props['aria-checked'] ?? this.props.accessibilityState?.checked,
disabled:
this.props['aria-disabled'] ?? this.props.accessibilityState?.disabled,
expanded:
this.props['aria-expanded'] ?? this.props.accessibilityState?.expanded,
selected:
this.props['aria-selected'] ?? this.props.accessibilityState?.selected,
};
_accessibilityState =
this.props.disabled != null
? {
...this.props.accessibilityState,
..._accessibilityState,
disabled: this.props.disabled,
}
: this.props.accessibilityState;
: _accessibilityState;
return React.cloneElement(
element,
@ -274,7 +288,7 @@ class TouchableNativeFeedback extends React.Component<Props, State> {
accessibilityLanguage: this.props.accessibilityLanguage,
accessibilityLabel: this.props.accessibilityLabel,
accessibilityRole: this.props.accessibilityRole,
accessibilityState: accessibilityState,
accessibilityState: _accessibilityState,
accessibilityActions: this.props.accessibilityActions,
onAccessibilityAction: this.props.onAccessibilityAction,
accessibilityValue: this.props.accessibilityValue,

Просмотреть файл

@ -137,7 +137,10 @@ class TouchableOpacity extends React.Component<Props, State> {
_createPressabilityConfig(): PressabilityConfig {
return {
cancelable: !this.props.rejectResponderTermination,
disabled: this.props.disabled ?? this.props.accessibilityState?.disabled,
disabled:
this.props.disabled ??
this.props['aria-disabled'] ??
this.props.accessibilityState?.disabled,
hitSlop: this.props.hitSlop,
delayLongPress: this.props.delayLongPress,
delayPressIn: this.props.delayPressIn,
@ -212,13 +215,25 @@ class TouchableOpacity extends React.Component<Props, State> {
const {onBlur, onFocus, ...eventHandlersWithoutBlurAndFocus} =
this.state.pressability.getEventHandlers();
const accessibilityState =
let _accessibilityState = {
busy: this.props['aria-busy'] ?? this.props.accessibilityState?.busy,
checked:
this.props['aria-checked'] ?? this.props.accessibilityState?.checked,
disabled:
this.props['aria-disabled'] ?? this.props.accessibilityState?.disabled,
expanded:
this.props['aria-expanded'] ?? this.props.accessibilityState?.expanded,
selected:
this.props['aria-selected'] ?? this.props.accessibilityState?.selected,
};
_accessibilityState =
this.props.disabled != null
? {
...this.props.accessibilityState,
..._accessibilityState,
disabled: this.props.disabled,
}
: this.props.accessibilityState;
: _accessibilityState;
return (
<Animated.View
@ -227,7 +242,7 @@ class TouchableOpacity extends React.Component<Props, State> {
accessibilityHint={this.props.accessibilityHint}
accessibilityLanguage={this.props.accessibilityLanguage}
accessibilityRole={this.props.accessibilityRole}
accessibilityState={accessibilityState}
accessibilityState={_accessibilityState}
accessibilityActions={this.props.accessibilityActions}
onAccessibilityAction={this.props.onAccessibilityAction}
accessibilityValue={this.props.accessibilityValue}

Просмотреть файл

@ -42,6 +42,16 @@ type Props = $ReadOnly<{|
accessibilityValue?: ?AccessibilityValue,
accessibilityViewIsModal?: ?boolean,
accessible?: ?boolean,
/**
* alias for accessibilityState
*
* see https://reactnative.dev/docs/accessibility#accessibilitystate
*/
'aria-busy'?: ?boolean,
'aria-checked'?: ?boolean,
'aria-disabled'?: ?boolean,
'aria-expanded'?: ?boolean,
'aria-selected'?: ?boolean,
'aria-hidden'?: ?boolean,
children?: ?React.Node,
delayLongPress?: ?number,
@ -105,6 +115,18 @@ class TouchableWithoutFeedback extends React.Component<Props, State> {
}
}
let _accessibilityState = {
busy: this.props['aria-busy'] ?? this.props.accessibilityState?.busy,
checked:
this.props['aria-checked'] ?? this.props.accessibilityState?.checked,
disabled:
this.props['aria-disabled'] ?? this.props.accessibilityState?.disabled,
expanded:
this.props['aria-expanded'] ?? this.props.accessibilityState?.expanded,
selected:
this.props['aria-selected'] ?? this.props.accessibilityState?.selected,
};
// BACKWARD-COMPATIBILITY: Focus and blur events were never supported before
// adopting `Pressability`, so preserve that behavior.
const {onBlur, onFocus, ...eventHandlersWithoutBlurAndFocus} =
@ -116,10 +138,10 @@ class TouchableWithoutFeedback extends React.Component<Props, State> {
accessibilityState:
this.props.disabled != null
? {
...this.props.accessibilityState,
..._accessibilityState,
disabled: this.props.disabled,
}
: this.props.accessibilityState,
: _accessibilityState,
focusable:
this.props.focusable !== false && this.props.onPress !== undefined,
@ -148,13 +170,16 @@ class TouchableWithoutFeedback extends React.Component<Props, State> {
}
}
function createPressabilityConfig(props: Props): PressabilityConfig {
function createPressabilityConfig({
'aria-disabled': ariaDisabled,
...props
}: Props): PressabilityConfig {
const accessibilityStateDisabled =
ariaDisabled ?? props.accessibilityState?.disabled;
return {
cancelable: !props.rejectResponderTermination,
disabled:
props.disabled !== null
? props.disabled
: props.accessibilityState?.disabled,
props.disabled !== null ? props.disabled : accessibilityStateDisabled,
hitSlop: props.hitSlop,
delayLongPress: props.delayLongPress,
delayPressIn: props.delayPressIn,

Просмотреть файл

@ -1,98 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`TouchableWithoutFeedback renders correctly 1`] = `
<Text
accessible={true}
focusable={false}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
>
Touchable
</Text>
`;
exports[`<TouchableNativeFeedback /> should render as expected 1`] = `
<View
accessible={true}
focusable={false}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
/>
`;
exports[`<TouchableNativeFeedback disabled={true}> should be disabled when disabled is true 1`] = `
<View
accessibilityState={
Object {
"disabled": true,
}
}
accessible={true}
focusable={false}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
/>
`;
exports[`<TouchableNativeFeedback disabled={true} accessibilityState={{}}> should be disabled when disabled is true and accessibilityState is empty 1`] = `
<View
accessibilityState={
Object {
"disabled": true,
}
}
accessible={true}
focusable={false}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
/>
`;
exports[`<TouchableNativeFeedback disabled={true} accessibilityState={{checked: true}}> should keep accessibilityState when disabled is true 1`] = `
<View
accessibilityState={
Object {
"checked": true,
"disabled": true,
}
}
accessible={true}
focusable={false}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
/>
`;
exports[`<TouchableNativeFeedback disabled={true} accessibilityState={{disabled:false}}> should overwrite accessibilityState with value of disabled prop 1`] = `
<View
accessibilityState={
Object {
"disabled": true,
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
@ -111,7 +27,11 @@ exports[`<TouchableNativeFeedback disabled={false} accessibilityState={{disabled
<View
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": false,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
@ -125,3 +45,120 @@ exports[`<TouchableNativeFeedback disabled={false} accessibilityState={{disabled
onStartShouldSetResponder={[Function]}
/>
`;
exports[`<TouchableNativeFeedback disabled={true} accessibilityState={{}}> should be disabled when disabled is true and accessibilityState is empty 1`] = `
<View
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
focusable={false}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
/>
`;
exports[`<TouchableNativeFeedback disabled={true} accessibilityState={{checked: true}}> should keep accessibilityState when disabled is true 1`] = `
<View
accessibilityState={
Object {
"busy": undefined,
"checked": true,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
focusable={false}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
/>
`;
exports[`<TouchableNativeFeedback disabled={true} accessibilityState={{disabled:false}}> should overwrite accessibilityState with value of disabled prop 1`] = `
<View
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
focusable={false}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
/>
`;
exports[`<TouchableNativeFeedback disabled={true}> should be disabled when disabled is true 1`] = `
<View
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
focusable={false}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
/>
`;
exports[`TouchableWithoutFeedback renders correctly 1`] = `
<Text
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
focusable={false}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
>
Touchable
</Text>
`;

Просмотреть файл

@ -2,6 +2,15 @@
exports[`TouchableOpacity renders correctly 1`] = `
<View
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
collapsable={false}
focusable={false}
@ -28,7 +37,11 @@ exports[`TouchableOpacity renders in disabled state when a disabled prop is pass
<View
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
@ -57,7 +70,11 @@ exports[`TouchableOpacity renders in disabled state when a key disabled in acces
<View
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}

Просмотреть файл

@ -2,6 +2,15 @@
exports[`TouchableWithoutFeedback renders correctly 1`] = `
<Text
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
focusable={false}
onClick={[Function]}
@ -20,7 +29,11 @@ exports[`TouchableWithoutFeedback with disabled state should be disabled when di
<View
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
@ -39,7 +52,11 @@ exports[`TouchableWithoutFeedback with disabled state should be disabled when di
<View
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
@ -58,7 +75,11 @@ exports[`TouchableWithoutFeedback with disabled state should disable button when
<View
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
@ -77,8 +98,11 @@ exports[`TouchableWithoutFeedback with disabled state should keep accessibilityS
<View
accessibilityState={
Object {
"busy": undefined,
"checked": true,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
@ -97,7 +121,11 @@ exports[`TouchableWithoutFeedback with disabled state should overwrite accessibi
<View
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}

Просмотреть файл

@ -43,6 +43,24 @@ const View: React.AbstractComponent<
}: ViewProps,
forwardedRef,
) => {
const {
accessibilityState,
'aria-busy': ariaBusy,
'aria-checked': ariaChecked,
'aria-disabled': ariaDisabled,
'aria-expanded': ariaExpanded,
'aria-selected': ariaSelected,
...restProps
} = otherProps;
const _accessibilityState = {
busy: ariaBusy ?? accessibilityState?.busy,
checked: ariaChecked ?? accessibilityState?.checked,
disabled: ariaDisabled ?? accessibilityState?.disabled,
expanded: ariaExpanded ?? accessibilityState?.expanded,
selected: ariaSelected ?? accessibilityState?.selected,
};
// Map role values to AccessibilityRole values
const roleToAccessibilityRoleMapping = {
alert: 'alert',
@ -118,6 +136,7 @@ const View: React.AbstractComponent<
<TextAncestor.Provider value={false}>
<ViewNativeComponent
focusable={tabIndex !== undefined ? !tabIndex : focusable}
accessibilityState={_accessibilityState}
accessibilityRole={
role ? roleToAccessibilityRoleMapping[role] : accessibilityRole
}
@ -129,7 +148,7 @@ const View: React.AbstractComponent<
? 'no-hide-descendants'
: importantForAccessibility
}
{...otherProps}
{...restProps}
style={style}
pointerEvents={newPointerEvents}
ref={forwardedRef}

Просмотреть файл

@ -492,7 +492,16 @@ export type ViewProps = $ReadOnly<{|
accessibilityLabelledBy?: ?string | ?Array<string>,
/**
* A value indicating whether the accessibility elements contained within
* alias for accessibilityState
*
* see https://reactnative.dev/docs/accessibility#accessibilitystate
*/
'aria-busy'?: ?boolean,
'aria-checked'?: ?boolean,
'aria-disabled'?: ?boolean,
'aria-expanded'?: ?boolean,
'aria-selected'?: ?boolean,
/** A value indicating whether the accessibility elements contained within
* this accessibility element are hidden.
*
* See https://reactnative.dev/docs/view#aria-hidden

Просмотреть файл

@ -5,7 +5,11 @@ exports[`<Button /> should be disabled and it should set accessibilityState to d
accessibilityRole="button"
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
@ -59,7 +63,11 @@ exports[`<Button /> should be disabled when disabled is empty and accessibilityS
accessibilityRole="button"
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
@ -113,7 +121,11 @@ exports[`<Button /> should be disabled when disabled={true} and accessibilitySta
accessibilityRole="button"
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
@ -165,6 +177,15 @@ exports[`<Button /> should be disabled when disabled={true} and accessibilitySta
exports[`<Button /> should be set importantForAccessibility={no-hide-descendants} when importantForAccessibility={no} 1`] = `
<View
accessibilityRole="button"
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
collapsable={false}
focusable={false}
@ -210,6 +231,15 @@ exports[`<Button /> should be set importantForAccessibility={no-hide-descendants
exports[`<Button /> should be set importantForAccessibility={no-hide-descendants} when importantForAccessibility={no-hide-descendants} 1`] = `
<View
accessibilityRole="button"
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
collapsable={false}
focusable={false}
@ -257,7 +287,11 @@ exports[`<Button /> should not be disabled when disabled={false} and accessibili
accessibilityRole="button"
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": false,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
@ -307,7 +341,11 @@ exports[`<Button /> should not be disabled when disabled={false} and accessibili
accessibilityRole="button"
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": false,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
@ -357,7 +395,11 @@ exports[`<Button /> should overwrite accessibilityState with value of disabled p
accessibilityRole="button"
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": true,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
@ -409,6 +451,15 @@ exports[`<Button /> should overwrite accessibilityState with value of disabled p
exports[`<Button /> should render as expected 1`] = `
<View
accessibilityRole="button"
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessible={true}
collapsable={false}
focusable={false}

Просмотреть файл

@ -178,6 +178,13 @@ const BaseImage = (props: ImagePropsType, forwardedRef) => {
? loadingIndicatorSource.uri
: null,
ref: forwardedRef,
accessibilityState: {
busy: props['aria-busy'] ?? props.accessibilityState?.busy,
checked: props['aria-checked'] ?? props.accessibilityState?.checked,
disabled: props['aria-disabled'] ?? props.accessibilityState?.disabled,
expanded: props['aria-expanded'] ?? props.accessibilityState?.expanded,
selected: props['aria-selected'] ?? props.accessibilityState?.selected,
},
};
return (

Просмотреть файл

@ -137,14 +137,32 @@ const BaseImage = (props: ImagePropsType, forwardedRef) => {
'The <Image> component cannot contain children. If you want to render content on top of the image, consider using the <ImageBackground> component or absolute positioning.',
);
}
const {
'aria-busy': ariaBusy,
'aria-checked': ariaChecked,
'aria-disabled': ariaDisabled,
'aria-expanded': ariaExpanded,
'aria-selected': ariaSelected,
height,
src,
width,
...restProps
} = props;
const {src, width, height, ...restProps} = props;
const _accessibilityState = {
busy: ariaBusy ?? props.accessibilityState?.busy,
checked: ariaChecked ?? props.accessibilityState?.checked,
disabled: ariaDisabled ?? props.accessibilityState?.disabled,
expanded: ariaExpanded ?? props.accessibilityState?.expanded,
selected: ariaSelected ?? props.accessibilityState?.selected,
};
return (
<ImageAnalyticsTagContext.Consumer>
{analyticTag => {
return (
<ImageViewNativeComponent
accessibilityState={_accessibilityState}
{...restProps}
ref={forwardedRef}
style={style}

Просмотреть файл

@ -12,6 +12,15 @@ exports[`<Image /> should render as <Image> when mocked 1`] = `
exports[`<Image /> should render as <RCTImageView> when not mocked 1`] = `
<RCTImageView
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
internal_analyticTag={null}
resizeMode="cover"
source={

Просмотреть файл

@ -34,6 +34,11 @@ const Text: React.AbstractComponent<
const {
accessible,
allowFontScaling,
'aria-busy': ariaBusy,
'aria-checked': ariaChecked,
'aria-disabled': ariaDisabled,
'aria-expanded': ariaExpanded,
'aria-selected': ariaSelected,
ellipsizeMode,
onLongPress,
onPress,
@ -52,14 +57,23 @@ const Text: React.AbstractComponent<
const [isHighlighted, setHighlighted] = useState(false);
const _accessibilityState = {
busy: ariaBusy ?? props.accessibilityState?.busy,
checked: ariaChecked ?? props.accessibilityState?.checked,
disabled: ariaDisabled ?? props.accessibilityState?.disabled,
expanded: ariaExpanded ?? props.accessibilityState?.expanded,
selected: ariaSelected ?? props.accessibilityState?.selected,
};
const _disabled =
restProps.disabled != null
? restProps.disabled
: props.accessibilityState?.disabled;
const _accessibilityState =
_disabled !== props.accessibilityState?.disabled
? {...props.accessibilityState, disabled: _disabled}
: props.accessibilityState;
: _accessibilityState?.disabled;
const nativeTextAccessibilityState =
_disabled !== _accessibilityState?.disabled
? {..._accessibilityState, disabled: _disabled}
: _accessibilityState;
const isPressable =
(onPress != null ||
@ -186,6 +200,7 @@ const Text: React.AbstractComponent<
return hasTextAncestor ? (
<NativeVirtualText
{...restProps}
accessibilityState={_accessibilityState}
{...eventHandlersForText}
isHighlighted={isHighlighted}
isPressable={isPressable}
@ -203,7 +218,7 @@ const Text: React.AbstractComponent<
disabled={_disabled}
selectable={_selectable}
accessible={_accessible}
accessibilityState={_accessibilityState}
accessibilityState={nativeTextAccessibilityState}
allowFontScaling={allowFontScaling !== false}
ellipsizeMode={ellipsizeMode ?? 'tail'}
isHighlighted={isHighlighted}

Просмотреть файл

@ -77,6 +77,17 @@ export type TextProps = $ReadOnly<{|
*
*/
android_hyphenationFrequency?: ?('normal' | 'none' | 'full'),
/**
* alias for accessibilityState
*
* see https://reactnative.dev/docs/accessibility#accessibilitystate
*/
'aria-busy'?: ?boolean,
'aria-checked'?: ?boolean,
'aria-disabled'?: ?boolean,
'aria-expanded'?: ?boolean,
'aria-selected'?: ?boolean,
children?: ?Node,
/**

Просмотреть файл

@ -220,6 +220,17 @@ class AccessibilityExample extends React.Component<{}> {
</View>
</RNTesterBlock>
<RNTesterBlock title="View with label, hint, role, and state">
<View
accessible={true}
accessibilityLabel="Accessibility label."
accessibilityRole="button"
aria-selected={true}
accessibilityHint="Accessibility hint.">
<Text>Accessible view with label, hint, role, and state</Text>
</View>
</RNTesterBlock>
<RNTesterBlock title="TextInput with accessibilityLabelledBy attribute">
<View>
<Text nativeID="formLabel1">Mail Address</Text>