mirror of
https://github.com/status-im/react-native.git
synced 2025-01-19 05:51:01 +00:00
4f187074bf
Summary: With accessibility prop set to true, it is not possible to access childern elements. This makes it impossible to test Components with appium and releatives. This commit adds support to change accessiblity property on Touchable elements to overcome the aforementioned issue. Closes https://github.com/facebook/react-native/pull/8243 Differential Revision: D3462949 Pulled By: javache fbshipit-source-id: 65ce6507a1619218ab5b527c970a74af197ef462
267 lines
7.8 KiB
JavaScript
267 lines
7.8 KiB
JavaScript
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule TouchableHighlight
|
|
* @noflow
|
|
*/
|
|
'use strict';
|
|
|
|
// Note (avik): add @flow when Flow supports spread properties in propTypes
|
|
|
|
var ColorPropType = require('ColorPropType');
|
|
var NativeMethodsMixin = require('NativeMethodsMixin');
|
|
var React = require('React');
|
|
var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
|
|
var StyleSheet = require('StyleSheet');
|
|
var TimerMixin = require('react-timer-mixin');
|
|
var Touchable = require('Touchable');
|
|
var TouchableWithoutFeedback = require('TouchableWithoutFeedback');
|
|
var View = require('View');
|
|
|
|
var ensureComponentIsNative = require('ensureComponentIsNative');
|
|
var ensurePositiveDelayProps = require('ensurePositiveDelayProps');
|
|
var keyOf = require('fbjs/lib/keyOf');
|
|
var merge = require('merge');
|
|
var onlyChild = require('onlyChild');
|
|
|
|
type Event = Object;
|
|
|
|
var DEFAULT_PROPS = {
|
|
activeOpacity: 0.8,
|
|
underlayColor: 'black',
|
|
};
|
|
|
|
var PRESS_RETENTION_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};
|
|
|
|
/**
|
|
* A wrapper for making views respond properly to touches.
|
|
* On press down, the opacity of the wrapped view is decreased, which allows
|
|
* the underlay color to show through, darkening or tinting the view. The
|
|
* underlay comes from adding a view to the view hierarchy, which can sometimes
|
|
* cause unwanted visual artifacts if not used correctly, for example if the
|
|
* backgroundColor of the wrapped view isn't explicitly set to an opaque color.
|
|
*
|
|
* Example:
|
|
*
|
|
* ```
|
|
* renderButton: function() {
|
|
* return (
|
|
* <TouchableHighlight onPress={this._onPressButton}>
|
|
* <Image
|
|
* style={styles.button}
|
|
* source={require('image!myButton')}
|
|
* />
|
|
* </TouchableHighlight>
|
|
* );
|
|
* },
|
|
* ```
|
|
* > **NOTE**: TouchableHighlight supports only one child
|
|
* >
|
|
* > If you wish to have several child components, wrap them in a View.
|
|
*/
|
|
|
|
var TouchableHighlight = React.createClass({
|
|
propTypes: {
|
|
...TouchableWithoutFeedback.propTypes,
|
|
/**
|
|
* Determines what the opacity of the wrapped view should be when touch is
|
|
* active.
|
|
*/
|
|
activeOpacity: React.PropTypes.number,
|
|
/**
|
|
* The color of the underlay that will show through when the touch is
|
|
* active.
|
|
*/
|
|
underlayColor: ColorPropType,
|
|
style: View.propTypes.style,
|
|
/**
|
|
* Called immediately after the underlay is shown
|
|
*/
|
|
onShowUnderlay: React.PropTypes.func,
|
|
/**
|
|
* Called immediately after the underlay is hidden
|
|
*/
|
|
onHideUnderlay: React.PropTypes.func,
|
|
},
|
|
|
|
mixins: [NativeMethodsMixin, TimerMixin, Touchable.Mixin],
|
|
|
|
getDefaultProps: () => DEFAULT_PROPS,
|
|
|
|
// Performance optimization to avoid constantly re-generating these objects.
|
|
_computeSyntheticState: function(props) {
|
|
return {
|
|
activeProps: {
|
|
style: {
|
|
opacity: props.activeOpacity,
|
|
}
|
|
},
|
|
activeUnderlayProps: {
|
|
style: {
|
|
backgroundColor: props.underlayColor,
|
|
}
|
|
},
|
|
underlayStyle: [
|
|
INACTIVE_UNDERLAY_PROPS.style,
|
|
props.style,
|
|
]
|
|
};
|
|
},
|
|
|
|
getInitialState: function() {
|
|
return merge(
|
|
this.touchableGetInitialState(), this._computeSyntheticState(this.props)
|
|
);
|
|
},
|
|
|
|
componentDidMount: function() {
|
|
ensurePositiveDelayProps(this.props);
|
|
ensureComponentIsNative(this.refs[CHILD_REF]);
|
|
},
|
|
|
|
componentDidUpdate: function() {
|
|
ensureComponentIsNative(this.refs[CHILD_REF]);
|
|
},
|
|
|
|
componentWillReceiveProps: function(nextProps) {
|
|
ensurePositiveDelayProps(nextProps);
|
|
if (nextProps.activeOpacity !== this.props.activeOpacity ||
|
|
nextProps.underlayColor !== this.props.underlayColor ||
|
|
nextProps.style !== this.props.style) {
|
|
this.setState(this._computeSyntheticState(nextProps));
|
|
}
|
|
},
|
|
|
|
viewConfig: {
|
|
uiViewClassName: 'RCTView',
|
|
validAttributes: ReactNativeViewAttributes.RCTView
|
|
},
|
|
|
|
/**
|
|
* `Touchable.Mixin` self callbacks. The mixin will invoke these if they are
|
|
* defined on your component.
|
|
*/
|
|
touchableHandleActivePressIn: function(e: Event) {
|
|
this.clearTimeout(this._hideTimeout);
|
|
this._hideTimeout = null;
|
|
this._showUnderlay();
|
|
this.props.onPressIn && this.props.onPressIn(e);
|
|
},
|
|
|
|
touchableHandleActivePressOut: function(e: Event) {
|
|
if (!this._hideTimeout) {
|
|
this._hideUnderlay();
|
|
}
|
|
this.props.onPressOut && this.props.onPressOut(e);
|
|
},
|
|
|
|
touchableHandlePress: function(e: Event) {
|
|
this.clearTimeout(this._hideTimeout);
|
|
this._showUnderlay();
|
|
this._hideTimeout = this.setTimeout(this._hideUnderlay,
|
|
this.props.delayPressOut || 100);
|
|
this.props.onPress && this.props.onPress(e);
|
|
},
|
|
|
|
touchableHandleLongPress: function(e: Event) {
|
|
this.props.onLongPress && this.props.onLongPress(e);
|
|
},
|
|
|
|
touchableGetPressRectOffset: function() {
|
|
return this.props.pressRetentionOffset || PRESS_RETENTION_OFFSET;
|
|
},
|
|
|
|
touchableGetHitSlop: function() {
|
|
return this.props.hitSlop;
|
|
},
|
|
|
|
touchableGetHighlightDelayMS: function() {
|
|
return this.props.delayPressIn;
|
|
},
|
|
|
|
touchableGetLongPressDelayMS: function() {
|
|
return this.props.delayLongPress;
|
|
},
|
|
|
|
touchableGetPressOutDelayMS: function() {
|
|
return this.props.delayPressOut;
|
|
},
|
|
|
|
_showUnderlay: function() {
|
|
if (!this.isMounted() || !this._hasPressHandler()) {
|
|
return;
|
|
}
|
|
|
|
this.refs[UNDERLAY_REF].setNativeProps(this.state.activeUnderlayProps);
|
|
this.refs[CHILD_REF].setNativeProps(this.state.activeProps);
|
|
this.props.onShowUnderlay && this.props.onShowUnderlay();
|
|
},
|
|
|
|
_hideUnderlay: function() {
|
|
this.clearTimeout(this._hideTimeout);
|
|
this._hideTimeout = null;
|
|
if (this._hasPressHandler() && this.refs[UNDERLAY_REF]) {
|
|
this.refs[CHILD_REF].setNativeProps(INACTIVE_CHILD_PROPS);
|
|
this.refs[UNDERLAY_REF].setNativeProps({
|
|
...INACTIVE_UNDERLAY_PROPS,
|
|
style: this.state.underlayStyle,
|
|
});
|
|
this.props.onHideUnderlay && this.props.onHideUnderlay();
|
|
}
|
|
},
|
|
|
|
_hasPressHandler: function() {
|
|
return !!(
|
|
this.props.onPress ||
|
|
this.props.onPressIn ||
|
|
this.props.onPressOut ||
|
|
this.props.onLongPress
|
|
);
|
|
},
|
|
|
|
render: function() {
|
|
return (
|
|
<View
|
|
accessible={this.props.accessible !== false}
|
|
accessibilityLabel={this.props.accessibilityLabel}
|
|
accessibilityComponentType={this.props.accessibilityComponentType}
|
|
accessibilityTraits={this.props.accessibilityTraits}
|
|
ref={UNDERLAY_REF}
|
|
style={this.state.underlayStyle}
|
|
onLayout={this.props.onLayout}
|
|
hitSlop={this.props.hitSlop}
|
|
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
|
|
onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest}
|
|
onResponderGrant={this.touchableHandleResponderGrant}
|
|
onResponderMove={this.touchableHandleResponderMove}
|
|
onResponderRelease={this.touchableHandleResponderRelease}
|
|
onResponderTerminate={this.touchableHandleResponderTerminate}
|
|
testID={this.props.testID}>
|
|
{React.cloneElement(
|
|
onlyChild(this.props.children),
|
|
{
|
|
ref: CHILD_REF,
|
|
}
|
|
)}
|
|
{Touchable.renderDebugView({color: 'green', hitSlop: this.props.hitSlop})}
|
|
</View>
|
|
);
|
|
}
|
|
});
|
|
|
|
var CHILD_REF = keyOf({childRef: null});
|
|
var UNDERLAY_REF = keyOf({underlayRef: null});
|
|
var INACTIVE_CHILD_PROPS = {
|
|
style: StyleSheet.create({x: {opacity: 1.0}}).x,
|
|
};
|
|
var INACTIVE_UNDERLAY_PROPS = {
|
|
style: StyleSheet.create({x: {backgroundColor: 'transparent'}}).x,
|
|
};
|
|
|
|
module.exports = TouchableHighlight;
|