Skip to content

Commit

Permalink
Feat/accessibility state alias (facebook#34524)
Browse files Browse the repository at this point in the history
Summary:
This adds aliasing for accessibility state, it's used as requested on facebook#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: facebook#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
  • Loading branch information
ankit-tailor authored and OlimpiaZurek committed May 22, 2023
1 parent 0f02127 commit f809850
Show file tree
Hide file tree
Showing 21 changed files with 475 additions and 53 deletions.
37 changes: 31 additions & 6 deletions Libraries/Components/Button.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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}
Expand Down
35 changes: 29 additions & 6 deletions Libraries/Components/Pressable/Pressable.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand All @@ -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}
Expand Down Expand Up @@ -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}
Expand All @@ -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}
Expand Down Expand Up @@ -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}
Expand All @@ -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}
Expand Down Expand Up @@ -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}
Expand All @@ -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}
Expand Down Expand Up @@ -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}
Expand All @@ -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}
Expand Down
10 changes: 10 additions & 0 deletions Libraries/Components/TextInput/TextInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -1360,6 +1368,7 @@ function InternalTextInput(props: Props): React.Node {
{...props}
{...eventHandlers}
accessible={accessible}
accessibilityState={_accessibilityState}
submitBehavior={submitBehavior}
caretHidden={caretHidden}
dataDetectorTypes={props.dataDetectorTypes}
Expand Down Expand Up @@ -1407,6 +1416,7 @@ function InternalTextInput(props: Props): React.Node {
{...props}
{...eventHandlers}
accessible={accessible}
accessibilityState={_accessibilityState}
autoCapitalize={autoCapitalize}
submitBehavior={submitBehavior}
caretHidden={caretHidden}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -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}
Expand Down
14 changes: 13 additions & 1 deletion Libraries/Components/Touchable/TouchableBounce.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]}
Expand All @@ -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}
Expand Down
Loading

0 comments on commit f809850

Please sign in to comment.