From 28266c492530502726d4afa4b788dbb8183448e7 Mon Sep 17 00:00:00 2001 From: Daksh Bhardwaj Date: Tue, 30 Aug 2022 20:23:48 +0530 Subject: [PATCH 1/7] feat: added accessibility value alias --- Libraries/Components/Pressable/Pressable.js | 24 +++++ .../Components/Touchable/TouchableBounce.js | 21 ++++- .../Touchable/TouchableHighlight.js | 21 ++++- .../Touchable/TouchableNativeFeedback.js | 21 ++++- .../Components/Touchable/TouchableOpacity.js | 21 ++++- .../Touchable/TouchableWithoutFeedback.js | 8 ++ Libraries/Components/View/View.js | 23 ++++- Libraries/Components/View/ViewPropTypes.js | 10 ++ .../Accessibility/AccessibilityExample.js | 91 +++++++++++++++++++ 9 files changed, 235 insertions(+), 5 deletions(-) diff --git a/Libraries/Components/Pressable/Pressable.js b/Libraries/Components/Pressable/Pressable.js index aab4f21d33ed89..1a0bf34c88d660 100644 --- a/Libraries/Components/Pressable/Pressable.js +++ b/Libraries/Components/Pressable/Pressable.js @@ -50,6 +50,10 @@ type Props = $ReadOnly<{| accessibilityRole?: ?AccessibilityRole, accessibilityState?: ?AccessibilityState, accessibilityValue?: ?AccessibilityValue, + 'aria-valuemax'?: AccessibilityValue['max'], + 'aria-valuemin'?: AccessibilityValue['min'], + 'aria-valuenow'?: AccessibilityValue['now'], + 'aria-valuetext'?: AccessibilityValue['text'], accessibilityViewIsModal?: ?boolean, accessible?: ?boolean, focusable?: ?boolean, @@ -210,12 +214,32 @@ function Pressable(props: Props, forwardedRef): React.Node { ? {...props.accessibilityState, disabled} : props.accessibilityState; + const accessibilityValue = { + max: + props['aria-valuemax'] !== null + ? props['aria-valuemax'] + : props.accessibilityValue?.max, + min: + props['aria-valuemin'] !== null + ? props['aria-valuemin'] + : props.accessibilityValue?.min, + now: + props['aria-valuenow'] !== null + ? props['aria-valuenow'] + : props.accessibilityValue?.now, + text: + props['aria-valuetext'] !== null + ? props['aria-valuetext'] + : props.accessibilityValue?.max, + }; + const restPropsWithDefaults: React.ElementConfig = { ...restProps, ...android_rippleConfig?.viewProps, accessible: accessible !== false, accessibilityState, focusable: focusable !== false, + accessibilityValue, hitSlop, }; diff --git a/Libraries/Components/Touchable/TouchableBounce.js b/Libraries/Components/Touchable/TouchableBounce.js index f77ab0f5738fcf..712538702f51dc 100644 --- a/Libraries/Components/Touchable/TouchableBounce.js +++ b/Libraries/Components/Touchable/TouchableBounce.js @@ -131,6 +131,25 @@ class TouchableBounce extends React.Component { const {onBlur, onFocus, ...eventHandlersWithoutBlurAndFocus} = this.state.pressability.getEventHandlers(); + const accessibilityValue = { + max: + this.props['aria-valuemax'] !== null + ? this.props['aria-valuemax'] + : this.props.accessibilityValue?.max, + min: + this.props['aria-valuemin'] !== null + ? this.props['aria-valuemin'] + : this.props.accessibilityValue?.min, + now: + this.props['aria-valuenow'] !== null + ? this.props['aria-valuenow'] + : this.props.accessibilityValue?.now, + text: + this.props['aria-valuetext'] !== null + ? this.props['aria-valuetext'] + : this.props.accessibilityValue?.max, + }; + return ( { accessibilityState={this.props.accessibilityState} accessibilityActions={this.props.accessibilityActions} onAccessibilityAction={this.props.onAccessibilityAction} - accessibilityValue={this.props.accessibilityValue} + accessibilityValue={accessibilityValue} importantForAccessibility={this.props.importantForAccessibility} accessibilityLiveRegion={this.props.accessibilityLiveRegion} accessibilityViewIsModal={this.props.accessibilityViewIsModal} diff --git a/Libraries/Components/Touchable/TouchableHighlight.js b/Libraries/Components/Touchable/TouchableHighlight.js index d41513b189b55d..356590a74c28cb 100644 --- a/Libraries/Components/Touchable/TouchableHighlight.js +++ b/Libraries/Components/Touchable/TouchableHighlight.js @@ -291,6 +291,25 @@ class TouchableHighlight extends React.Component { } : this.props.accessibilityState; + const accessibilityValue = { + max: + this.props['aria-valuemax'] !== null + ? this.props['aria-valuemax'] + : this.props.accessibilityValue?.max, + min: + this.props['aria-valuemin'] !== null + ? this.props['aria-valuemin'] + : this.props.accessibilityValue?.min, + now: + this.props['aria-valuenow'] !== null + ? this.props['aria-valuenow'] + : this.props.accessibilityValue?.now, + text: + this.props['aria-valuetext'] !== null + ? this.props['aria-valuetext'] + : this.props.accessibilityValue?.max, + }; + return ( { accessibilityLanguage={this.props.accessibilityLanguage} accessibilityRole={this.props.accessibilityRole} accessibilityState={accessibilityState} - accessibilityValue={this.props.accessibilityValue} + accessibilityValue={accessibilityValue} accessibilityActions={this.props.accessibilityActions} onAccessibilityAction={this.props.onAccessibilityAction} importantForAccessibility={this.props.importantForAccessibility} diff --git a/Libraries/Components/Touchable/TouchableNativeFeedback.js b/Libraries/Components/Touchable/TouchableNativeFeedback.js index 8fff2f302397f2..d30d7bfe64639f 100644 --- a/Libraries/Components/Touchable/TouchableNativeFeedback.js +++ b/Libraries/Components/Touchable/TouchableNativeFeedback.js @@ -259,6 +259,25 @@ class TouchableNativeFeedback extends React.Component { } : this.props.accessibilityState; + const accessibilityValue = { + max: + this.props['aria-valuemax'] !== null + ? this.props['aria-valuemax'] + : this.props.accessibilityValue?.max, + min: + this.props['aria-valuemin'] !== null + ? this.props['aria-valuemin'] + : this.props.accessibilityValue?.min, + now: + this.props['aria-valuenow'] !== null + ? this.props['aria-valuenow'] + : this.props.accessibilityValue?.now, + text: + this.props['aria-valuetext'] !== null + ? this.props['aria-valuetext'] + : this.props.accessibilityValue?.max, + }; + return React.cloneElement( element, { @@ -277,7 +296,7 @@ class TouchableNativeFeedback extends React.Component { accessibilityState: accessibilityState, accessibilityActions: this.props.accessibilityActions, onAccessibilityAction: this.props.onAccessibilityAction, - accessibilityValue: this.props.accessibilityValue, + accessibilityValue: accessibilityValue, importantForAccessibility: this.props.importantForAccessibility, accessibilityLiveRegion: this.props.accessibilityLiveRegion, accessibilityViewIsModal: this.props.accessibilityViewIsModal, diff --git a/Libraries/Components/Touchable/TouchableOpacity.js b/Libraries/Components/Touchable/TouchableOpacity.js index 67e18c06d54c83..64970d64491f64 100644 --- a/Libraries/Components/Touchable/TouchableOpacity.js +++ b/Libraries/Components/Touchable/TouchableOpacity.js @@ -220,6 +220,25 @@ class TouchableOpacity extends React.Component { } : this.props.accessibilityState; + const accessibilityValue = { + max: + this.props['aria-valuemax'] !== null + ? this.props['aria-valuemax'] + : this.props.accessibilityValue?.max, + min: + this.props['aria-valuemin'] !== null + ? this.props['aria-valuemin'] + : this.props.accessibilityValue?.min, + now: + this.props['aria-valuenow'] !== null + ? this.props['aria-valuenow'] + : this.props.accessibilityValue?.now, + text: + this.props['aria-valuetext'] !== null + ? this.props['aria-valuetext'] + : this.props.accessibilityValue?.max, + }; + return ( { accessibilityState={accessibilityState} accessibilityActions={this.props.accessibilityActions} onAccessibilityAction={this.props.onAccessibilityAction} - accessibilityValue={this.props.accessibilityValue} + accessibilityValue={accessibilityValue} importantForAccessibility={this.props.importantForAccessibility} accessibilityLiveRegion={this.props.accessibilityLiveRegion} accessibilityViewIsModal={this.props.accessibilityViewIsModal} diff --git a/Libraries/Components/Touchable/TouchableWithoutFeedback.js b/Libraries/Components/Touchable/TouchableWithoutFeedback.js index 2b3bd4b930e14b..1794279bb657a9 100755 --- a/Libraries/Components/Touchable/TouchableWithoutFeedback.js +++ b/Libraries/Components/Touchable/TouchableWithoutFeedback.js @@ -40,6 +40,10 @@ type Props = $ReadOnly<{| accessibilityRole?: ?AccessibilityRole, accessibilityState?: ?AccessibilityState, accessibilityValue?: ?AccessibilityValue, + 'aria-valuemax'?: AccessibilityValue['max'], + 'aria-valuemin'?: AccessibilityValue['min'], + 'aria-valuenow'?: AccessibilityValue['now'], + 'aria-valuetext'?: AccessibilityValue['text'], accessibilityViewIsModal?: ?boolean, accessible?: ?boolean, children?: ?React.Node, @@ -79,6 +83,10 @@ const PASSTHROUGH_PROPS = [ 'accessibilityLiveRegion', 'accessibilityRole', 'accessibilityValue', + 'aria-valuemax', + 'aria-valuemin', + 'aria-valuenow', + 'aria-valuetext', 'accessibilityViewIsModal', 'hitSlop', 'importantForAccessibility', diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js index 7822e8184f214b..ceb249fe34eab7 100644 --- a/Libraries/Components/View/View.js +++ b/Libraries/Components/View/View.js @@ -28,11 +28,32 @@ const View: React.AbstractComponent< React.ElementRef, > = React.forwardRef( ({tabIndex, focusable, ...otherProps}: ViewProps, forwardedRef) => { + const accessibilityValue = { + max: + otherProps['aria-valuemax'] !== null + ? otherProps['aria-valuemax'] + : otherProps.accessibilityValue?.max, + min: + otherProps['aria-valuemin'] !== null + ? otherProps['aria-valuemin'] + : otherProps.accessibilityValue?.min, + now: + otherProps['aria-valuenow'] !== null + ? otherProps['aria-valuenow'] + : otherProps.accessibilityValue?.now, + text: + otherProps['aria-valuetext'] !== null + ? otherProps['aria-valuetext'] + : otherProps.accessibilityValue?.text, + }; + + const restWithDefaultProps = {accessibilityValue, ...otherProps}; + return ( diff --git a/Libraries/Components/View/ViewPropTypes.js b/Libraries/Components/View/ViewPropTypes.js index effed17622cfda..4d8e9548b2a1f5 100644 --- a/Libraries/Components/View/ViewPropTypes.js +++ b/Libraries/Components/View/ViewPropTypes.js @@ -472,6 +472,16 @@ export type ViewProps = $ReadOnly<{| accessibilityState?: ?AccessibilityState, accessibilityValue?: ?AccessibilityValue, + /** + * alias for accessibilityState + * + * see https://reactnative.dev/docs/accessibility#accessibilitystate + */ + 'aria-valuemax'?: ?AccessibilityValue['max'], + 'aria-valuemin'?: ?AccessibilityValue['min'], + 'aria-valuenow'?: ?AccessibilityValue['now'], + 'aria-valuetext'?: ?AccessibilityValue['text'], + /** * Provides an array of custom actions available for accessibility. * diff --git a/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js b/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js index e3c6e333300ea5..b526106af4f889 100644 --- a/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js +++ b/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js @@ -911,6 +911,91 @@ class FakeSliderExample extends React.Component<{}, FakeSliderExampleState> { } } +class FakeSliderExampleForAccessibilityValue extends React.Component< + {}, + FakeSliderExampleState, +> { + state: FakeSliderExampleState = { + current: 50, + textualValue: 'center', + }; + + increment: () => void = () => { + let newValue = this.state.current + 2; + if (newValue > 100) { + newValue = 100; + } + this.setState({ + current: newValue, + }); + }; + + decrement: () => void = () => { + let newValue = this.state.current - 2; + if (newValue < 0) { + newValue = 0; + } + this.setState({ + current: newValue, + }); + }; + + render(): React.Node { + return ( + + { + switch (event.nativeEvent.actionName) { + case 'increment': + this.increment(); + break; + case 'decrement': + this.decrement(); + break; + } + }} + aria-valuemax={100} + aria-valuemin={0} + aria-valuenow={this.state.current}> + Fake Slider + + { + switch (event.nativeEvent.actionName) { + case 'increment': + if (this.state.textualValue === 'center') { + this.setState({textualValue: 'right'}); + } else if (this.state.textualValue === 'left') { + this.setState({textualValue: 'center'}); + } + break; + case 'decrement': + if (this.state.textualValue === 'center') { + this.setState({textualValue: 'left'}); + } else if (this.state.textualValue === 'right') { + this.setState({textualValue: 'center'}); + } + break; + } + }} + accessibilityValue={{text: this.state.textualValue}}> + + Equalizer + + + + ); + } +} + class AnnounceForAccessibility extends React.Component<{}> { _handleOnPress = (): TimeoutID => setTimeout( @@ -1290,6 +1375,12 @@ exports.examples = [ return ; }, }, + { + title: 'Fake SliderExample For AccessibilityValue', + render(): React.Element { + return ; + }, + }, { title: 'Check if the display options are enabled', render(): React.Element { From e57536fdaf29aa97465910c3bdaab5db8af5de65 Mon Sep 17 00:00:00 2001 From: Daksh Bhardwaj Date: Wed, 31 Aug 2022 00:00:06 +0530 Subject: [PATCH 2/7] docs changes --- Libraries/Components/View/ViewPropTypes.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Libraries/Components/View/ViewPropTypes.js b/Libraries/Components/View/ViewPropTypes.js index 4d8e9548b2a1f5..c5c4d15dcb4aaf 100644 --- a/Libraries/Components/View/ViewPropTypes.js +++ b/Libraries/Components/View/ViewPropTypes.js @@ -474,8 +474,7 @@ export type ViewProps = $ReadOnly<{| /** * alias for accessibilityState - * - * see https://reactnative.dev/docs/accessibility#accessibilitystate + * It represents textual description of a component's value, or for range-based components, such as sliders and progress bars. */ 'aria-valuemax'?: ?AccessibilityValue['max'], 'aria-valuemin'?: ?AccessibilityValue['min'], From ec39394415ff1cbf60273441e01eb08b0e52e64a Mon Sep 17 00:00:00 2001 From: Daksh Bhardwaj Date: Thu, 1 Sep 2022 12:47:46 +0530 Subject: [PATCH 3/7] updated the snapshots for failing testcases --- .../__snapshots__/Pressable-test.js.snap | 80 +++++++++++++++++ .../TouchableHighlight-test.js.snap | 48 ++++++++++ .../TouchableNativeFeedback-test.js.snap | 88 +++++++++++++++---- .../TouchableOpacity-test.js.snap | 24 +++++ Libraries/Components/View/View.js | 2 +- .../__snapshots__/Button-test.js.snap | 72 +++++++++++++++ 6 files changed, 297 insertions(+), 17 deletions(-) diff --git a/Libraries/Components/Pressable/__tests__/__snapshots__/Pressable-test.js.snap b/Libraries/Components/Pressable/__tests__/__snapshots__/Pressable-test.js.snap index 821d4f41ea56e5..eb572858c96e03 100644 --- a/Libraries/Components/Pressable/__tests__/__snapshots__/Pressable-test.js.snap +++ b/Libraries/Components/Pressable/__tests__/__snapshots__/Pressable-test.js.snap @@ -2,6 +2,14 @@ exports[` should render as expected: should deep render when mocked (please verify output manually) 1`] = ` should render as expected: should deep render when mocked exports[` should render as expected: should deep render when not mocked (please verify output manually) 1`] = ` should be disabled when disabled is true: "disabled": true, } } + accessibilityValue={ + Object { + "max": undefined, + "min": undefined, + "now": undefined, + "text": undefined, + } + } accessible={true} collapsable={false} focusable={true} @@ -81,6 +105,14 @@ exports[` should be disabled when disabled is true: "disabled": true, } } + accessibilityValue={ + Object { + "max": undefined, + "min": undefined, + "now": undefined, + "text": undefined, + } + } accessible={true} collapsable={false} focusable={true} @@ -121,6 +153,14 @@ exports[` should be disable "disabled": true, } } + accessibilityValue={ + Object { + "max": undefined, + "min": undefined, + "now": undefined, + "text": undefined, + } + } accessible={true} collapsable={false} focusable={true} @@ -145,6 +185,14 @@ exports[` should be disable "disabled": true, } } + accessibilityValue={ + Object { + "max": undefined, + "min": undefined, + "now": undefined, + "text": undefined, + } + } accessible={true} collapsable={false} focusable={true} @@ -188,6 +236,14 @@ exports[` shou "disabled": true, } } + accessibilityValue={ + Object { + "max": undefined, + "min": undefined, + "now": undefined, + "text": undefined, + } + } accessible={true} collapsable={false} focusable={true} @@ -213,6 +269,14 @@ exports[` shou "disabled": true, } } + accessibilityValue={ + Object { + "max": undefined, + "min": undefined, + "now": undefined, + "text": undefined, + } + } accessible={true} collapsable={false} focusable={true} @@ -263,6 +327,14 @@ exports[` sh "disabled": true, } } + accessibilityValue={ + Object { + "max": undefined, + "min": undefined, + "now": undefined, + "text": undefined, + } + } accessible={true} collapsable={false} focusable={true} @@ -287,6 +359,14 @@ exports[` sh "disabled": true, } } + accessibilityValue={ + Object { + "max": undefined, + "min": undefined, + "now": undefined, + "text": undefined, + } + } accessible={true} collapsable={false} focusable={true} diff --git a/Libraries/Components/Touchable/__tests__/__snapshots__/TouchableHighlight-test.js.snap b/Libraries/Components/Touchable/__tests__/__snapshots__/TouchableHighlight-test.js.snap index 70eaabe704a1f0..35c845e97493fc 100644 --- a/Libraries/Components/Touchable/__tests__/__snapshots__/TouchableHighlight-test.js.snap +++ b/Libraries/Components/Touchable/__tests__/__snapshots__/TouchableHighlight-test.js.snap @@ -2,6 +2,14 @@ exports[`TouchableHighlight renders correctly 1`] = ` should render as expected 1`] = ` + - Touchable - +/> `; -exports[` should render as expected 1`] = ` +exports[` should overwrite accessibilityState with value of disabled prop 1`] = ` should render as expected 1`] = ` /> `; -exports[` should be disabled when disabled is true 1`] = ` +exports[` should be disabled when disabled is true and accessibilityState is empty 1`] = ` should be disabled when disab /> `; -exports[` should be disabled when disabled is true and accessibilityState is empty 1`] = ` +exports[` should keep accessibilityState when disabled is true 1`] = ` shoul /> `; -exports[` should keep accessibilityState when disabled is true 1`] = ` +exports[` should overwrite accessibilityState with value of disabled prop 1`] = ` `; -exports[` should overwrite accessibilityState with value of disabled prop 1`] = ` +exports[` should be disabled when disabled is true 1`] = ` `; -exports[` should overwrite accessibilityState with value of disabled prop 1`] = ` - +> + Touchable + `; diff --git a/Libraries/Components/Touchable/__tests__/__snapshots__/TouchableOpacity-test.js.snap b/Libraries/Components/Touchable/__tests__/__snapshots__/TouchableOpacity-test.js.snap index 4d3f11bc92ef35..bb952b5156483c 100644 --- a/Libraries/Components/Touchable/__tests__/__snapshots__/TouchableOpacity-test.js.snap +++ b/Libraries/Components/Touchable/__tests__/__snapshots__/TouchableOpacity-test.js.snap @@ -2,6 +2,14 @@ exports[`TouchableOpacity renders correctly 1`] = ` diff --git a/Libraries/Components/__tests__/__snapshots__/Button-test.js.snap b/Libraries/Components/__tests__/__snapshots__/Button-test.js.snap index fb87db9320e845..438ee5a1b0d659 100644 --- a/Libraries/Components/__tests__/__snapshots__/Button-test.js.snap +++ b/Libraries/Components/__tests__/__snapshots__/Button-test.js.snap @@ -8,6 +8,14 @@ exports[`