Files
react-native/RNTester/js/examples/Touchable/TouchableExample.js
T
Chad Smith 14fcda880c use fb images in RNTester app
Summary:
This test has been failing internally because it attempts to access external images during CI execution, which it is blocked from doing. Because Detox synchronizes with the app to make sure network requests and animations are complete before continuing, the test waits indefinitely for these images to be fetched.

This diff makes the following changes
* External images are replaced with fb-hosted images
* Timeout threshold is reduced
* Detox initialization is broken out into two steps to help debug any future instances of this occurring
* If Detox is blocked for over 20 seconds, diagnostic messages will be printed indicating why it's being blocked

Changelog: [Internal]

Reviewed By: TheSavior

Differential Revision: D19671592

fbshipit-source-id: 368c683101ed7fc68578fc7758061b31c96c241d
2020-02-03 21:18:46 -08:00

615 lines
17 KiB
JavaScript

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/
'use strict';
const React = require('react');
const {
Animated,
Image,
StyleSheet,
Text,
TouchableHighlight,
TouchableOpacity,
Platform,
TouchableNativeFeedback,
TouchableWithoutFeedback,
View,
} = require('react-native');
import {useEffect, useRef, useState} from 'react';
const forceTouchAvailable =
(Platform.OS === 'ios' && Platform.constants.forceTouchAvailable) || false;
class TouchableHighlightBox extends React.Component<{...}, $FlowFixMeState> {
state = {
timesPressed: 0,
};
touchableOnPress = () => {
this.setState({
timesPressed: this.state.timesPressed + 1,
});
};
render() {
let textLog = '';
if (this.state.timesPressed > 1) {
textLog = this.state.timesPressed + 'x TouchableHighlight onPress';
} else if (this.state.timesPressed > 0) {
textLog = 'TouchableHighlight onPress';
}
return (
<View>
<View style={styles.row}>
<TouchableHighlight
style={styles.wrapper}
testID="touchable_highlight_image_button"
onPress={this.touchableOnPress}>
<Image source={remoteImage} style={styles.image} />
</TouchableHighlight>
<TouchableHighlight
style={styles.wrapper}
testID="touchable_highlight_text_button"
activeOpacity={1}
underlayColor="rgb(210, 230, 255)"
onPress={this.touchableOnPress}>
<View style={styles.wrapperCustom}>
<Text style={styles.text}>Tap Here For Custom Highlight!</Text>
</View>
</TouchableHighlight>
</View>
<View style={styles.logBox}>
<Text testID="touchable_highlight_console">{textLog}</Text>
</View>
</View>
);
}
}
class TouchableWithoutFeedbackBox extends React.Component<
{...},
$FlowFixMeState,
> {
state = {
timesPressed: 0,
};
textOnPress = () => {
this.setState({
timesPressed: this.state.timesPressed + 1,
});
};
render() {
let textLog = '';
if (this.state.timesPressed > 1) {
textLog = this.state.timesPressed + 'x TouchableWithoutFeedback onPress';
} else if (this.state.timesPressed > 0) {
textLog = 'TouchableWithoutFeedback onPress';
}
return (
<View>
<TouchableWithoutFeedback
onPress={this.textOnPress}
testID="touchable_without_feedback_button">
<View style={styles.wrapperCustom}>
<Text style={styles.text}>Tap Here For No Feedback!</Text>
</View>
</TouchableWithoutFeedback>
<View style={styles.logBox}>
<Text testID="touchable_without_feedback_console">{textLog}</Text>
</View>
</View>
);
}
}
class TextOnPressBox extends React.Component<{...}, $FlowFixMeState> {
state = {
timesPressed: 0,
};
textOnPress = () => {
this.setState({
timesPressed: this.state.timesPressed + 1,
});
};
render() {
let textLog = '';
if (this.state.timesPressed > 1) {
textLog = this.state.timesPressed + 'x text onPress';
} else if (this.state.timesPressed > 0) {
textLog = 'text onPress';
}
return (
<View>
<Text
style={styles.textBlock}
testID="tappable_text"
onPress={this.textOnPress}>
Text has built-in onPress handling
</Text>
<View style={styles.logBox}>
<Text testID="tappable_text_console">{textLog}</Text>
</View>
</View>
);
}
}
class TouchableFeedbackEvents extends React.Component<{...}, $FlowFixMeState> {
state = {
eventLog: [],
};
render() {
return (
<View testID="touchable_feedback_events">
<View style={[styles.row, styles.centered]}>
<TouchableOpacity
style={styles.wrapper}
testID="touchable_feedback_events_button"
accessibilityLabel="touchable feedback events"
accessibilityRole="button"
onPress={() => this._appendEvent('press')}
onPressIn={() => this._appendEvent('pressIn')}
onPressOut={() => this._appendEvent('pressOut')}
onLongPress={() => this._appendEvent('longPress')}>
<Text style={styles.button}>Press Me</Text>
</TouchableOpacity>
</View>
<View
testID="touchable_feedback_events_console"
style={styles.eventLogBox}>
{this.state.eventLog.map((e, ii) => (
<Text key={ii}>{e}</Text>
))}
</View>
</View>
);
}
_appendEvent = eventName => {
const limit = 6;
const eventLog = this.state.eventLog.slice(0, limit - 1);
eventLog.unshift(eventName);
this.setState({eventLog});
};
}
class TouchableDelayEvents extends React.Component<{...}, $FlowFixMeState> {
state = {
eventLog: [],
};
render() {
return (
<View testID="touchable_delay_events">
<View style={[styles.row, styles.centered]}>
<TouchableOpacity
style={styles.wrapper}
testID="touchable_delay_events_button"
onPress={() => this._appendEvent('press')}
delayPressIn={400}
onPressIn={() => this._appendEvent('pressIn - 400ms delay')}
delayPressOut={1000}
onPressOut={() => this._appendEvent('pressOut - 1000ms delay')}
delayLongPress={800}
onLongPress={() => this._appendEvent('longPress - 800ms delay')}>
<Text style={styles.button}>Press Me</Text>
</TouchableOpacity>
</View>
<View
style={styles.eventLogBox}
testID="touchable_delay_events_console">
{this.state.eventLog.map((e, ii) => (
<Text key={ii}>{e}</Text>
))}
</View>
</View>
);
}
_appendEvent = eventName => {
const limit = 6;
const eventLog = this.state.eventLog.slice(0, limit - 1);
eventLog.unshift(eventName);
this.setState({eventLog});
};
}
class ForceTouchExample extends React.Component<{...}, $FlowFixMeState> {
state = {
force: 0,
};
_renderConsoleText = () => {
return forceTouchAvailable
? 'Force: ' + this.state.force.toFixed(3)
: '3D Touch is not available on this device';
};
render() {
return (
<View testID="touchable_3dtouch_event">
<View style={styles.forceTouchBox} testID="touchable_3dtouch_output">
<Text>{this._renderConsoleText()}</Text>
</View>
<View style={[styles.row, styles.centered]}>
<View
style={styles.wrapper}
testID="touchable_3dtouch_button"
onStartShouldSetResponder={() => true}
onResponderMove={event =>
this.setState({force: event.nativeEvent.force})
}
onResponderRelease={event => this.setState({force: 0})}>
<Text style={styles.button}>Press Me</Text>
</View>
</View>
</View>
);
}
}
class TouchableHitSlop extends React.Component<{...}, $FlowFixMeState> {
state = {
timesPressed: 0,
};
onPress = () => {
this.setState({
timesPressed: this.state.timesPressed + 1,
});
};
render() {
let log = '';
if (this.state.timesPressed > 1) {
log = this.state.timesPressed + 'x onPress';
} else if (this.state.timesPressed > 0) {
log = 'onPress';
}
return (
<View testID="touchable_hit_slop">
<View style={[styles.row, styles.centered]}>
<TouchableOpacity
onPress={this.onPress}
style={styles.hitSlopWrapper}
hitSlop={{top: 30, bottom: 30, left: 60, right: 60}}
testID="touchable_hit_slop_button">
<Text style={styles.hitSlopButton}>Press Outside This View</Text>
</TouchableOpacity>
</View>
<View style={styles.logBox}>
<Text>{log}</Text>
</View>
</View>
);
}
}
function TouchableNativeMethodChecker<
T: React.AbstractComponent<any, any>,
>(props: {|Component: T, name: string|}): React.Node {
const [status, setStatus] = useState<?boolean>(null);
const ref = useRef<?React.ElementRef<T>>(null);
useEffect(() => {
setStatus(ref.current != null && typeof ref.current.measure === 'function');
}, []);
return (
<View style={[styles.row, styles.block]}>
<props.Component ref={ref}>
<View />
</props.Component>
<Text>
{props.name + ': '}
{status == null
? 'Missing Ref!'
: status === true
? 'Native Methods Exist'
: 'Native Methods Missing!'}
</Text>
</View>
);
}
function TouchableNativeMethods() {
return (
<View>
<TouchableNativeMethodChecker
Component={TouchableHighlight}
name="TouchableHighlight"
/>
<TouchableNativeMethodChecker
Component={TouchableOpacity}
name="TouchableOpacity"
/>
</View>
);
}
class TouchableDisabled extends React.Component<{...}> {
render() {
return (
<View>
<TouchableOpacity disabled={true} style={[styles.row, styles.block]}>
<Text style={styles.disabledButton}>Disabled TouchableOpacity</Text>
</TouchableOpacity>
<TouchableOpacity disabled={false} style={[styles.row, styles.block]}>
<Text style={styles.button}>Enabled TouchableOpacity</Text>
</TouchableOpacity>
<TouchableHighlight
activeOpacity={1}
disabled={true}
underlayColor="rgb(210, 230, 255)"
style={[styles.row, styles.block]}
onPress={() => console.log('custom THW text - highlight')}>
<Text style={styles.disabledButton}>Disabled TouchableHighlight</Text>
</TouchableHighlight>
<TouchableHighlight
activeOpacity={1}
underlayColor="rgb(210, 230, 255)"
style={[styles.row, styles.block]}
onPress={() => console.log('custom THW text - highlight')}>
<Text style={styles.button}>Enabled TouchableHighlight</Text>
</TouchableHighlight>
<TouchableWithoutFeedback
onPress={() => console.log('TWOF has been clicked')}
disabled={true}>
<View style={styles.wrapperCustom}>
<Text
style={[
styles.button,
styles.nativeFeedbackButton,
styles.disabledButton,
]}>
Disabled TouchableWithoutFeedback
</Text>
</View>
</TouchableWithoutFeedback>
<TouchableWithoutFeedback
onPress={() => console.log('TWOF has been clicked')}
disabled={false}>
<View style={styles.wrapperCustom}>
<Text style={[styles.button, styles.nativeFeedbackButton]}>
Enabled TouchableWithoutFeedback
</Text>
</View>
</TouchableWithoutFeedback>
{Platform.OS === 'android' && (
<TouchableNativeFeedback
onPress={() => console.log('custom TNF has been clicked')}
background={TouchableNativeFeedback.SelectableBackground()}>
<View style={[styles.row, styles.block]}>
<Text style={[styles.button, styles.nativeFeedbackButton]}>
Enabled TouchableNativeFeedback
</Text>
</View>
</TouchableNativeFeedback>
)}
{Platform.OS === 'android' && (
<TouchableNativeFeedback
disabled={true}
onPress={() => console.log('custom TNF has been clicked')}
background={TouchableNativeFeedback.SelectableBackground()}>
<View style={[styles.row, styles.block]}>
<Text
style={[styles.disabledButton, styles.nativeFeedbackButton]}>
Disabled TouchableNativeFeedback
</Text>
</View>
</TouchableNativeFeedback>
)}
</View>
);
}
}
const remoteImage = {
uri: 'https://www.facebook.com/favicon.ico',
};
const styles = StyleSheet.create({
row: {
justifyContent: 'center',
flexDirection: 'row',
},
centered: {
justifyContent: 'center',
},
image: {
width: 50,
height: 50,
},
text: {
fontSize: 16,
},
block: {
padding: 10,
},
button: {
color: '#007AFF',
},
disabledButton: {
color: '#007AFF',
opacity: 0.5,
},
nativeFeedbackButton: {
textAlign: 'center',
margin: 10,
},
hitSlopButton: {
color: 'white',
},
wrapper: {
borderRadius: 8,
},
wrapperCustom: {
borderRadius: 8,
padding: 6,
},
hitSlopWrapper: {
backgroundColor: 'red',
marginVertical: 30,
},
logBox: {
padding: 20,
margin: 10,
borderWidth: StyleSheet.hairlineWidth,
borderColor: '#f0f0f0',
backgroundColor: '#f9f9f9',
},
eventLogBox: {
padding: 10,
margin: 10,
height: 120,
borderWidth: StyleSheet.hairlineWidth,
borderColor: '#f0f0f0',
backgroundColor: '#f9f9f9',
},
forceTouchBox: {
padding: 10,
margin: 10,
borderWidth: StyleSheet.hairlineWidth,
borderColor: '#f0f0f0',
backgroundColor: '#f9f9f9',
alignItems: 'center',
},
textBlock: {
fontWeight: '500',
color: 'blue',
},
});
exports.displayName = (undefined: ?string);
exports.description = 'Touchable and onPress examples.';
exports.title = '<Touchable*> and onPress';
exports.examples = [
{
title: '<TouchableHighlight>',
description: ('TouchableHighlight works by adding an extra view with a ' +
'black background under the single child view. This works best when the ' +
'child view is fully opaque, although it can be made to work as a simple ' +
'background color change as well with the activeOpacity and ' +
'underlayColor props.': string),
render: function(): React.Node {
return <TouchableHighlightBox />;
},
},
{
title: '<TouchableWithoutFeedback>',
render: function(): React.Node {
return <TouchableWithoutFeedbackBox />;
},
},
{
title: 'TouchableNativeFeedback with Animated child',
description: ('TouchableNativeFeedback can have an AnimatedComponent as a' +
'direct child.': string),
platform: 'android',
render: function(): React.Node {
const mScale = new Animated.Value(1);
Animated.timing(mScale, {
toValue: 0.3,
duration: 1000,
useNativeDriver: false,
}).start();
const style = {
backgroundColor: 'rgb(180, 64, 119)',
width: 200,
height: 100,
transform: [{scale: mScale}],
};
return (
<View>
<View style={styles.row}>
<TouchableNativeFeedback>
<Animated.View style={style} />
</TouchableNativeFeedback>
</View>
</View>
);
},
},
{
title: '<Text onPress={fn}> with highlight',
render: function(): React.Element<any> {
return <TextOnPressBox />;
},
},
{
title: 'Touchable feedback events',
description: ('<Touchable*> components accept onPress, onPressIn, ' +
'onPressOut, and onLongPress as props.': string),
render: function(): React.Element<any> {
return <TouchableFeedbackEvents />;
},
},
{
title: 'Touchable delay for events',
description: ('<Touchable*> components also accept delayPressIn, ' +
'delayPressOut, and delayLongPress as props. These props impact the ' +
'timing of feedback events.': string),
render: function(): React.Element<any> {
return <TouchableDelayEvents />;
},
},
{
title: '3D Touch / Force Touch',
description:
'iPhone 8 and 8 plus support 3D touch, which adds a force property to touches',
render: function(): React.Element<any> {
return <ForceTouchExample />;
},
platform: 'ios',
},
{
title: 'Touchable Hit Slop',
description: ('<Touchable*> components accept hitSlop prop which extends the touch area ' +
'without changing the view bounds.': string),
render: function(): React.Element<any> {
return <TouchableHitSlop />;
},
},
{
title: 'Touchable Native Methods',
description: ('Some <Touchable*> components expose native methods like `measure`.': string),
render: function(): React.Element<any> {
return <TouchableNativeMethods />;
},
},
{
title: 'Disabled Touchable*',
description: ('<Touchable*> components accept disabled prop which prevents ' +
'any interaction with component': string),
render: function(): React.Element<any> {
return <TouchableDisabled />;
},
},
];