Add e2e test for a Modal component

Summary:
Changelog:
[General][Added] - [RNTester] Added a modal component example with onShow/onDismiss callbacks

Reviewed By: lunaleaps

Differential Revision: D27926745

fbshipit-source-id: c2f2b8a15154a090bb747bb02cdf428e90861824
This commit is contained in:
Nadiia D
2021-04-23 10:12:59 -07:00
committed by Facebook GitHub Bot
parent 7310847758
commit 23ae702d97
3 changed files with 479 additions and 340 deletions
@@ -0,0 +1,343 @@
/**
* 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.
*
* @flow strict-local
* @format
*/
import * as React from 'react';
import {
Modal,
Picker,
Platform,
StyleSheet,
Switch,
Text,
TouchableHighlight,
View,
ScrollView,
} from 'react-native';
import type {RNTesterExampleModuleItem} from '../../types/RNTesterTypes';
const Item = Picker.Item;
class Button extends React.Component<$FlowFixMeProps, $FlowFixMeState> {
state = {
active: false,
};
_onHighlight = () => {
this.setState({active: true});
};
_onUnhighlight = () => {
this.setState({active: false});
};
render() {
const colorStyle = {
color: this.state.active ? '#fff' : '#000',
};
return (
<TouchableHighlight
onHideUnderlay={this._onUnhighlight}
onPress={this.props.onPress}
onShowUnderlay={this._onHighlight}
style={[styles.button, this.props.style]}
underlayColor="#a9d9d4">
<Text style={[styles.buttonText, colorStyle]}>
{this.props.children}
</Text>
</TouchableHighlight>
);
}
}
const supportedOrientationsPickerValues = [
['portrait'],
['landscape'],
['landscape-left'],
['portrait', 'landscape-right'],
['portrait', 'landscape'],
[],
];
class ModalExample extends React.Component<{...}, $FlowFixMe> {
state: $FlowFixMe = {
animationType: 'none',
modalVisible: false,
transparent: false,
hardwareAccelerated: false,
statusBarTranslucent: false,
presentationStyle: 'fullScreen',
selectedSupportedOrientation: '0',
currentOrientation: 'unknown',
action: '',
};
_setModalVisible = visible => {
this.setState({modalVisible: visible});
};
_setAnimationType = type => {
this.setState({animationType: type});
};
_toggleTransparent = () => {
this.setState({transparent: !this.state.transparent});
};
_toggleHardwareAccelerated = () => {
this.setState({hardwareAccelerated: !this.state.hardwareAccelerated});
};
_toggleStatusBarTranslucent = () => {
this.setState({statusBarTranslucent: !this.state.statusBarTranslucent});
};
renderSwitch(): React.Node {
if (Platform.isTV) {
return null;
}
if (Platform.OS === 'android') {
return (
<>
<Text style={styles.rowTitle}>Hardware Accelerated</Text>
<Switch
value={this.state.hardwareAccelerated}
onValueChange={this._toggleHardwareAccelerated}
/>
<Text style={styles.rowTitle}>Status Bar Translucent</Text>
<Switch
value={this.state.statusBarTranslucent}
onValueChange={this._toggleStatusBarTranslucent}
/>
<Text style={styles.rowTitle}>Transparent</Text>
<Switch
value={this.state.transparent}
onValueChange={this._toggleTransparent}
/>
</>
);
}
return (
<Switch
value={this.state.transparent}
onValueChange={this._toggleTransparent}
/>
);
}
render(): React.Node {
const modalBackgroundStyle = {
backgroundColor: this.state.transparent
? 'rgba(0, 0, 0, 0.5)'
: '#f5fcff',
};
const innerContainerTransparentStyle = this.state.transparent
? {backgroundColor: '#fff', padding: 20}
: null;
const activeButtonStyle = {
backgroundColor: '#ddd',
};
return (
<ScrollView contentContainerStyle={styles.ScrollView}>
<Modal
animationType={this.state.animationType}
presentationStyle={this.state.presentationStyle}
transparent={this.state.transparent}
hardwareAccelerated={this.state.hardwareAccelerated}
statusBarTranslucent={this.state.statusBarTranslucent}
visible={this.state.modalVisible}
onRequestClose={() => this._setModalVisible(false)}
supportedOrientations={
supportedOrientationsPickerValues[
Number(this.state.selectedSupportedOrientation)
]
}
onOrientationChange={evt =>
this.setState({currentOrientation: evt.nativeEvent.orientation})
}
onDismiss={() => {
if (this.state.action === 'onDismiss') {
alert(this.state.action);
}
}}
onShow={() => {
if (this.state.action === 'onShow') {
alert(this.state.action);
}
}}>
<View style={[styles.container, modalBackgroundStyle]}>
<View
style={[styles.innerContainer, innerContainerTransparentStyle]}>
<Text>
This modal was presented{' '}
{this.state.animationType === 'none' ? 'without' : 'with'}{' '}
animation.
</Text>
<Text>
It is currently displayed in {this.state.currentOrientation}{' '}
mode.
</Text>
<Button
onPress={this._setModalVisible.bind(this, false)}
style={styles.modalButton}>
Close
</Button>
</View>
</View>
</Modal>
<View style={styles.row}>
<Text style={styles.rowTitle}>Animation Type</Text>
<Button
onPress={this._setAnimationType.bind(this, 'none')}
style={
this.state.animationType === 'none' ? activeButtonStyle : {}
}>
none
</Button>
<Button
onPress={this._setAnimationType.bind(this, 'slide')}
style={
this.state.animationType === 'slide' ? activeButtonStyle : {}
}>
slide
</Button>
<Button
onPress={this._setAnimationType.bind(this, 'fade')}
style={
this.state.animationType === 'fade' ? activeButtonStyle : {}
}>
fade
</Button>
</View>
<View style={styles.row}>{this.renderSwitch()}</View>
{this.renderPickers()}
<Button onPress={this._setModalVisible.bind(this, true)}>
Present
</Button>
</ScrollView>
);
}
renderPickers(): React.Node {
if (Platform.isTV) {
return null;
}
return (
<View>
<View>
<Text style={styles.rowTitle}>Presentation style</Text>
<Picker
selectedValue={this.state.presentationStyle}
onValueChange={presentationStyle =>
this.setState({presentationStyle})
}
itemStyle={styles.pickerItem}>
<Item label="Full Screen" value="fullScreen" />
<Item label="Page Sheet" value="pageSheet" />
<Item label="Form Sheet" value="formSheet" />
<Item label="Over Full Screen" value="overFullScreen" />
<Item label="Default presentationStyle" value={null} />
</Picker>
</View>
<View>
<Text style={styles.rowTitle}>Supported orientations</Text>
<Picker
selectedValue={this.state.selectedSupportedOrientation}
onValueChange={(_, i) =>
this.setState({selectedSupportedOrientation: i.toString()})
}
itemStyle={styles.pickerItem}>
<Item label="Portrait" value={'0'} />
<Item label="Landscape" value={'1'} />
<Item label="Landscape left" value={'2'} />
<Item label="Portrait and landscape right" value={'3'} />
<Item label="Portrait and landscape" value={'4'} />
<Item label="Default supportedOrientations" value={'5'} />
</Picker>
</View>
<View>
<Text style={styles.rowTitle}>Actions</Text>
{Platform.OS === 'ios' ? (
<Picker
selectedValue={this.state.action}
onValueChange={action => this.setState({action})}
itemStyle={styles.pickerItem}>
<Item label="None" value="" />
<Item label="On Dismiss" value="onDismiss" />
<Item label="On Show" value="onShow" />
</Picker>
) : (
<Picker
selectedValue={this.state.action}
onValueChange={action => this.setState({action})}
itemStyle={styles.pickerItem}>
<Item label="None" value="" />
<Item label="On Show" value="onShow" />
</Picker>
)}
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
padding: 20,
},
innerContainer: {
borderRadius: 10,
alignItems: 'center',
},
row: {
alignItems: 'center',
flex: 1,
flexDirection: 'row',
marginBottom: 20,
},
rowTitle: {
flex: 1,
fontWeight: 'bold',
},
button: {
borderRadius: 5,
flexGrow: 1,
height: 44,
alignSelf: 'stretch',
justifyContent: 'center',
overflow: 'hidden',
},
buttonText: {
fontSize: 18,
margin: 5,
textAlign: 'center',
},
modalButton: {
marginTop: 10,
},
pickerItem: {
fontSize: 16,
},
ScrollView: {
paddingTop: 10,
paddingBottom: 100,
},
});
export default ({
title: 'Modal Presentation',
name: 'basic',
description: 'Modals can be presented with or without animation',
render: (): React.Node => <ModalExample />,
}: RNTesterExampleModuleItem);
@@ -4,347 +4,21 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow strict-local
* @format
*/
'use strict';
import ModalCustomizable from './ModalCustomizable';
import ModalOnShow from './ModalOnShow';
import type {RNTesterExampleModuleItem} from '../../types/RNTesterTypes';
const React = require('react');
const {
Modal,
Picker,
Platform,
StyleSheet,
Switch,
Text,
TouchableHighlight,
View,
ScrollView,
} = require('react-native');
const Item = Picker.Item;
exports.displayName = (undefined: ?string);
exports.framework = 'React';
exports.title = 'Modal';
exports.category = 'UI';
exports.documentationURL = 'https://reactnative.dev/docs/modal';
exports.description = 'Component for presenting modal views.';
class Button extends React.Component<$FlowFixMeProps, $FlowFixMeState> {
state = {
active: false,
};
_onHighlight = () => {
this.setState({active: true});
};
_onUnhighlight = () => {
this.setState({active: false});
};
render() {
const colorStyle = {
color: this.state.active ? '#fff' : '#000',
};
return (
<TouchableHighlight
onHideUnderlay={this._onUnhighlight}
onPress={this.props.onPress}
onShowUnderlay={this._onHighlight}
style={[styles.button, this.props.style]}
underlayColor="#a9d9d4">
<Text style={[styles.buttonText, colorStyle]}>
{this.props.children}
</Text>
</TouchableHighlight>
);
}
}
const supportedOrientationsPickerValues = [
['portrait'],
['landscape'],
['landscape-left'],
['portrait', 'landscape-right'],
['portrait', 'landscape'],
[],
];
class ModalExample extends React.Component<{...}, $FlowFixMeState> {
state = {
animationType: 'none',
modalVisible: false,
transparent: false,
hardwareAccelerated: false,
statusBarTranslucent: false,
presentationStyle: 'fullScreen',
selectedSupportedOrientation: '0',
currentOrientation: 'unknown',
action: '',
};
_setModalVisible = visible => {
this.setState({modalVisible: visible});
};
_setAnimationType = type => {
this.setState({animationType: type});
};
_toggleTransparent = () => {
this.setState({transparent: !this.state.transparent});
};
_toggleHardwareAccelerated = () => {
this.setState({hardwareAccelerated: !this.state.hardwareAccelerated});
};
_toggleStatusBarTranslucent = () => {
this.setState({statusBarTranslucent: !this.state.statusBarTranslucent});
};
renderSwitch() {
if (Platform.isTV) {
return null;
}
if (Platform.OS === 'android') {
return (
<>
<Text style={styles.rowTitle}>Hardware Accelerated</Text>
<Switch
value={this.state.hardwareAccelerated}
onValueChange={this._toggleHardwareAccelerated}
/>
<Text style={styles.rowTitle}>Status Bar Translucent</Text>
<Switch
value={this.state.statusBarTranslucent}
onValueChange={this._toggleStatusBarTranslucent}
/>
<Text style={styles.rowTitle}>Transparent</Text>
<Switch
value={this.state.transparent}
onValueChange={this._toggleTransparent}
/>
</>
);
}
return (
<Switch
value={this.state.transparent}
onValueChange={this._toggleTransparent}
/>
);
}
render() {
const modalBackgroundStyle = {
backgroundColor: this.state.transparent
? 'rgba(0, 0, 0, 0.5)'
: '#f5fcff',
};
const innerContainerTransparentStyle = this.state.transparent
? {backgroundColor: '#fff', padding: 20}
: null;
const activeButtonStyle = {
backgroundColor: '#ddd',
};
return (
<ScrollView contentContainerStyle={styles.ScrollView}>
<Modal
animationType={this.state.animationType}
presentationStyle={this.state.presentationStyle}
transparent={this.state.transparent}
hardwareAccelerated={this.state.hardwareAccelerated}
statusBarTranslucent={this.state.statusBarTranslucent}
visible={this.state.modalVisible}
onRequestClose={() => this._setModalVisible(false)}
supportedOrientations={
supportedOrientationsPickerValues[
Number(this.state.selectedSupportedOrientation)
]
}
onOrientationChange={evt =>
this.setState({currentOrientation: evt.nativeEvent.orientation})
}
onDismiss={() => {
if (this.state.action === 'onDismiss') alert(this.state.action);
}}
onShow={() => {
if (this.state.action === 'onShow') alert(this.state.action);
}}>
<View style={[styles.container, modalBackgroundStyle]}>
<View
style={[styles.innerContainer, innerContainerTransparentStyle]}>
<Text>
This modal was presented{' '}
{this.state.animationType === 'none' ? 'without' : 'with'}{' '}
animation.
</Text>
<Text>
It is currently displayed in {this.state.currentOrientation}{' '}
mode.
</Text>
<Button
onPress={this._setModalVisible.bind(this, false)}
style={styles.modalButton}>
Close
</Button>
</View>
</View>
</Modal>
<View style={styles.row}>
<Text style={styles.rowTitle}>Animation Type</Text>
<Button
onPress={this._setAnimationType.bind(this, 'none')}
style={
this.state.animationType === 'none' ? activeButtonStyle : {}
}>
none
</Button>
<Button
onPress={this._setAnimationType.bind(this, 'slide')}
style={
this.state.animationType === 'slide' ? activeButtonStyle : {}
}>
slide
</Button>
<Button
onPress={this._setAnimationType.bind(this, 'fade')}
style={
this.state.animationType === 'fade' ? activeButtonStyle : {}
}>
fade
</Button>
</View>
<View style={styles.row}>{this.renderSwitch()}</View>
{this.renderPickers()}
<Button onPress={this._setModalVisible.bind(this, true)}>
Present
</Button>
</ScrollView>
);
}
renderPickers() {
if (Platform.isTV) {
return null;
}
return (
<View>
<View>
<Text style={styles.rowTitle}>Presentation style</Text>
<Picker
selectedValue={this.state.presentationStyle}
onValueChange={presentationStyle =>
this.setState({presentationStyle})
}
itemStyle={styles.pickerItem}>
<Item label="Full Screen" value="fullScreen" />
<Item label="Page Sheet" value="pageSheet" />
<Item label="Form Sheet" value="formSheet" />
<Item label="Over Full Screen" value="overFullScreen" />
<Item label="Default presentationStyle" value={null} />
</Picker>
</View>
<View>
<Text style={styles.rowTitle}>Supported orientations</Text>
<Picker
selectedValue={this.state.selectedSupportedOrientation}
onValueChange={(_, i) =>
this.setState({selectedSupportedOrientation: i.toString()})
}
itemStyle={styles.pickerItem}>
<Item label="Portrait" value={'0'} />
<Item label="Landscape" value={'1'} />
<Item label="Landscape left" value={'2'} />
<Item label="Portrait and landscape right" value={'3'} />
<Item label="Portrait and landscape" value={'4'} />
<Item label="Default supportedOrientations" value={'5'} />
</Picker>
</View>
<View>
<Text style={styles.rowTitle}>Actions</Text>
{Platform.OS === 'ios' ? (
<Picker
selectedValue={this.state.action}
onValueChange={action => this.setState({action})}
itemStyle={styles.pickerItem}>
<Item label="None" value="" />
<Item label="On Dismiss" value="onDismiss" />
<Item label="On Show" value="onShow" />
</Picker>
) : (
<Picker
selectedValue={this.state.action}
onValueChange={action => this.setState({action})}
itemStyle={styles.pickerItem}>
<Item label="None" value="" />
<Item label="On Show" value="onShow" />
</Picker>
)}
</View>
</View>
);
}
}
exports.examples = [
{
title: 'Modal Presentation',
name: 'basic',
description: 'Modals can be presented with or without animation',
render: (): React.Node => <ModalExample />,
},
];
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
padding: 20,
},
innerContainer: {
borderRadius: 10,
alignItems: 'center',
},
row: {
alignItems: 'center',
flex: 1,
flexDirection: 'row',
marginBottom: 20,
},
rowTitle: {
flex: 1,
fontWeight: 'bold',
},
button: {
borderRadius: 5,
flexGrow: 1,
height: 44,
alignSelf: 'stretch',
justifyContent: 'center',
overflow: 'hidden',
},
buttonText: {
fontSize: 18,
margin: 5,
textAlign: 'center',
},
modalButton: {
marginTop: 10,
},
pickerItem: {
fontSize: 16,
},
ScrollView: {
paddingTop: 10,
paddingBottom: 100,
},
});
export const displayName = (undefined: ?string);
export const framework = 'React';
export const title = 'Modal';
export const category = 'UI';
export const documentationURL = 'https://reactnative.dev/docs/modal';
export const description = 'Component for presenting modal views.';
export const examples = ([
ModalCustomizable,
ModalOnShow,
]: Array<RNTesterExampleModuleItem>);
@@ -0,0 +1,122 @@
/**
* 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.
*
* @flow strict-local
* @format
*/
import * as React from 'react';
import {Modal, Pressable, StyleSheet, Text, View} from 'react-native';
import type {RNTesterExampleModuleItem} from '../../types/RNTesterTypes';
function ModalOnShowOnDismiss(): React.Node {
const [modalVisible, setModalVisible] = React.useState(false);
const [onShowCount, setOnShowCount] = React.useState(0);
const [onDismissCount, setOnDismissCount] = React.useState(0);
return (
<View style={styles.container}>
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
onShow={() => {
setOnShowCount(onShowCount + 1);
}}
onDismiss={() => {
setOnDismissCount(onDismissCount + 1);
}}
onRequestClose={() => {
setModalVisible(false);
}}>
<View style={[styles.centeredView, styles.modalBackdrop]}>
<View style={styles.modalView}>
<Text testID="modal-on-show-count">
onShow is called {onShowCount} times
</Text>
<Text testID="modal-on-dismiss-count">
onDismiss is called {onDismissCount} times
</Text>
<Pressable
style={[styles.button, styles.buttonClose]}
onPress={() => setModalVisible(false)}>
<Text testID="dismiss-modal" style={styles.textStyle}>
Hide Modal
</Text>
</Pressable>
</View>
</View>
</Modal>
<Text testID="on-show-count">onShow is called {onShowCount} times</Text>
<Text testID="on-dismiss-count">
onDismiss is called {onDismissCount} times
</Text>
<Pressable
style={[styles.button, styles.buttonOpen]}
onPress={() => setModalVisible(true)}>
<Text testID="open-modal" style={styles.textStyle}>
Show Modal
</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
container: {
display: 'flex',
alignItems: 'center',
paddingVertical: 30,
},
centeredView: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
modalBackdrop: {
backgroundColor: 'rgba(0, 0, 0, 0.5)',
},
modalView: {
margin: 20,
backgroundColor: 'white',
borderRadius: 20,
padding: 35,
alignItems: 'center',
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
button: {
borderRadius: 20,
padding: 10,
marginVertical: 20,
elevation: 2,
},
buttonOpen: {
backgroundColor: '#F194FF',
},
buttonClose: {
backgroundColor: '#2196F3',
},
textStyle: {
color: 'white',
fontWeight: 'bold',
textAlign: 'center',
},
});
export default ({
title: "Modal's onShow/onDismiss",
name: 'onShow',
description:
'onShow and onDismiss (iOS only) callbacks are called when modals is shown/dissmissed',
render: (): React.Node => <ModalOnShowOnDismiss />,
}: RNTesterExampleModuleItem);