mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
feat(rn-tester): Improve the Modal tester (#38977)
Summary: ## Summary: The modal tester in the sandbox was lacking a bit of functionality that the Modal component exposes -- especially on Android. This change revamps the Modal page to more-closely resemble the API documentation by exposing all of the options, and annotating which ones are reserved for the different platforms. Additionally, this change puts the modal controls into the created modal itself. This allows the user to more-easily test what happens if the props change during the lifespan of the modal (related PR: https://github.com/facebook/react-native/issues/38947). ## Changelog: <!-- Help reviewers and the release process by writing your own changelog entry. Pick one each for the category and type tags: [ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message For more details, see: https://reactnative.dev/contributing/changelogs-in-pull-requests --> [INTERNAL] [CHANGED] - Revamp Modal tester in rn-tester Pull Request resolved: https://github.com/facebook/react-native/pull/38977 Test Plan: | Control page | Modal (with `transparent=true` on Android) | |:--:|:--:| |  |  | Reviewed By: yungsters Differential Revision: D48414351 Pulled By: NickGerleman fbshipit-source-id: 54bece639f4c64132dfb21c54c91d46972f5a335
This commit is contained in:
committed by
Facebook GitHub Bot
parent
3eeee11d7a
commit
04fb0122b2
@@ -42,7 +42,7 @@ export default function RNTOption(props: Props): React.Node {
|
||||
: props.selected
|
||||
}
|
||||
hitSlop={4}
|
||||
onPress={props.onPress}
|
||||
onPress={props.disabled === true ? undefined : props.onPress}
|
||||
onPressIn={() => setPressed(true)}
|
||||
onPressOut={() => setPressed(false)}
|
||||
testID={props.testID}>
|
||||
|
||||
@@ -13,18 +13,10 @@
|
||||
import * as React from 'react';
|
||||
import {Modal, Platform, StyleSheet, Switch, Text, View} from 'react-native';
|
||||
import type {RNTesterModuleExample} from '../../types/RNTesterTypes';
|
||||
import type {Props as ModalProps} from 'react-native/Libraries/Modal/Modal';
|
||||
import RNTOption from '../../components/RNTOption';
|
||||
const RNTesterButton = require('../../components/RNTesterButton');
|
||||
|
||||
const supportedOrientations = {
|
||||
Portrait: ['portrait'],
|
||||
Landscape: ['landscape'],
|
||||
'Landscape Left': ['landscape-left'],
|
||||
'Portrait and Landscape Right': ['portrait', 'landscape-right'],
|
||||
'Portrait and Landscape': ['portrait', 'landscape'],
|
||||
Default: [],
|
||||
};
|
||||
|
||||
const animationTypes = ['slide', 'none', 'fade'];
|
||||
const presentationStyles = [
|
||||
'fullScreen',
|
||||
@@ -32,76 +24,220 @@ const presentationStyles = [
|
||||
'formSheet',
|
||||
'overFullScreen',
|
||||
];
|
||||
const iOSActions = ['None', 'On Dismiss', 'On Show'];
|
||||
const noniOSActions = ['None', 'On Show'];
|
||||
const supportedOrientations = [
|
||||
'portrait',
|
||||
'portrait-upside-down',
|
||||
'landscape',
|
||||
'landscape-left',
|
||||
'landscape-right',
|
||||
];
|
||||
|
||||
function ModalPresentation() {
|
||||
const [animationType, setAnimationType] = React.useState('none');
|
||||
const [transparent, setTransparent] = React.useState(false);
|
||||
const [visible, setVisible] = React.useState(false);
|
||||
const [hardwareAccelerated, setHardwareAccelerated] = React.useState(false);
|
||||
const [statusBarTranslucent, setStatusBarTranslucent] = React.useState(false);
|
||||
const [presentationStyle, setPresentationStyle] =
|
||||
React.useState('fullScreen');
|
||||
const [supportedOrientationKey, setSupportedOrientationKey] =
|
||||
React.useState('Portrait');
|
||||
const onDismiss = React.useCallback(() => {
|
||||
alert('onDismiss');
|
||||
}, []);
|
||||
|
||||
const onShow = React.useCallback(() => {
|
||||
alert('onShow');
|
||||
}, []);
|
||||
|
||||
const onRequestClose = React.useCallback(() => {
|
||||
console.log('onRequestClose');
|
||||
}, []);
|
||||
|
||||
const [props, setProps] = React.useState<ModalProps>({
|
||||
animationType: 'none',
|
||||
transparent: false,
|
||||
hardwareAccelerated: false,
|
||||
statusBarTranslucent: false,
|
||||
presentationStyle: Platform.select({
|
||||
ios: 'fullScreen',
|
||||
default: undefined,
|
||||
}),
|
||||
supportedOrientations: Platform.select({
|
||||
ios: ['portrait'],
|
||||
default: undefined,
|
||||
}),
|
||||
onDismiss: undefined,
|
||||
onShow: undefined,
|
||||
visible: false,
|
||||
});
|
||||
const presentationStyle = props.presentationStyle;
|
||||
const hardwareAccelerated = props.hardwareAccelerated;
|
||||
const statusBarTranslucent = props.statusBarTranslucent;
|
||||
|
||||
const [currentOrientation, setCurrentOrientation] = React.useState('unknown');
|
||||
const [action, setAction] = React.useState('None');
|
||||
const actions = Platform.OS === 'ios' ? iOSActions : noniOSActions;
|
||||
const onDismiss = () => {
|
||||
setVisible(false);
|
||||
if (action === 'onDismiss') {
|
||||
alert('onDismiss');
|
||||
}
|
||||
};
|
||||
|
||||
const onShow = () => {
|
||||
if (action === 'onShow') {
|
||||
alert('onShow');
|
||||
}
|
||||
};
|
||||
/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's
|
||||
* LTI update could not be added via codemod */
|
||||
const onOrientationChange = event =>
|
||||
setCurrentOrientation(event.nativeEvent.orientation);
|
||||
const modalBackgroundStyle = {
|
||||
backgroundColor: transparent ? 'rgba(0, 0, 0, 0.5)' : '#f5fcff',
|
||||
};
|
||||
const innerContainerTransparentStyle = transparent
|
||||
? {backgroundColor: '#fff', padding: 20}
|
||||
: null;
|
||||
|
||||
const controls = (
|
||||
<>
|
||||
<View style={styles.inlineBlock}>
|
||||
<Text style={styles.title}>Status Bar Translucent 🟢</Text>
|
||||
<Switch
|
||||
value={statusBarTranslucent}
|
||||
onValueChange={enabled =>
|
||||
setProps(prev => ({...prev, statusBarTranslucent: enabled}))
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.inlineBlock}>
|
||||
<Text style={styles.title}>Hardware Acceleration 🟢</Text>
|
||||
<Switch
|
||||
value={hardwareAccelerated}
|
||||
onValueChange={enabled =>
|
||||
setProps(prev => ({
|
||||
...prev,
|
||||
hardwareAccelerated: enabled,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.block}>
|
||||
<Text style={styles.title}>Presentation Style ⚫️</Text>
|
||||
<View style={styles.row}>
|
||||
{presentationStyles.map(type => (
|
||||
<RNTOption
|
||||
key={type}
|
||||
disabled={Platform.OS !== 'ios'}
|
||||
style={styles.option}
|
||||
label={type}
|
||||
multiSelect={true}
|
||||
onPress={() =>
|
||||
setProps(prev => {
|
||||
if (type === 'overFullScreen' && prev.transparent === true) {
|
||||
return {
|
||||
...prev,
|
||||
presentationStyle: type,
|
||||
transparent: false,
|
||||
};
|
||||
}
|
||||
return {
|
||||
...prev,
|
||||
presentationStyle:
|
||||
type === prev.presentationStyle ? undefined : type,
|
||||
};
|
||||
})
|
||||
}
|
||||
selected={type === presentationStyle}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.block}>
|
||||
<View style={styles.rowWithSpaceBetween}>
|
||||
<Text style={styles.title}>Transparent</Text>
|
||||
<Switch
|
||||
value={props.transparent}
|
||||
onValueChange={enabled =>
|
||||
setProps(prev => ({...prev, transparent: enabled}))
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
{Platform.OS === 'ios' && presentationStyle !== 'overFullScreen' ? (
|
||||
<Text style={styles.warning}>
|
||||
iOS Modal can only be transparent with 'overFullScreen' Presentation
|
||||
Style
|
||||
</Text>
|
||||
) : null}
|
||||
</View>
|
||||
<View style={styles.block}>
|
||||
<Text style={styles.title}>Supported Orientation ⚫️</Text>
|
||||
<View style={styles.row}>
|
||||
{supportedOrientations.map(orientation => (
|
||||
<RNTOption
|
||||
key={orientation}
|
||||
disabled={Platform.OS !== 'ios'}
|
||||
style={styles.option}
|
||||
label={orientation}
|
||||
multiSelect={true}
|
||||
onPress={() =>
|
||||
setProps(prev => {
|
||||
if (prev.supportedOrientations?.includes(orientation)) {
|
||||
return {
|
||||
...prev,
|
||||
supportedOrientations: prev.supportedOrientations?.filter(
|
||||
o => o !== orientation,
|
||||
),
|
||||
};
|
||||
}
|
||||
return {
|
||||
...prev,
|
||||
supportedOrientations: [
|
||||
...(prev.supportedOrientations ?? []),
|
||||
orientation,
|
||||
],
|
||||
};
|
||||
})
|
||||
}
|
||||
selected={props.supportedOrientations?.includes(orientation)}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.block}>
|
||||
<Text style={styles.title}>Actions</Text>
|
||||
<View style={styles.row}>
|
||||
<RNTOption
|
||||
key="onShow"
|
||||
style={styles.option}
|
||||
label="onShow"
|
||||
multiSelect={true}
|
||||
onPress={() =>
|
||||
setProps(prev => ({
|
||||
...prev,
|
||||
onShow: prev.onShow ? undefined : onShow,
|
||||
}))
|
||||
}
|
||||
selected={!!props.onShow}
|
||||
/>
|
||||
<RNTOption
|
||||
key="onDismiss"
|
||||
style={styles.option}
|
||||
label="onDismiss ⚫️"
|
||||
disabled={Platform.OS !== 'ios'}
|
||||
onPress={() =>
|
||||
setProps(prev => ({
|
||||
...prev,
|
||||
onDismiss: prev.onDismiss ? undefined : onDismiss,
|
||||
}))
|
||||
}
|
||||
selected={!!props.onDismiss}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<View>
|
||||
<RNTesterButton onPress={() => setVisible(true)}>
|
||||
<RNTesterButton
|
||||
onPress={() => setProps(prev => ({...prev, visible: true}))}>
|
||||
Show Modal
|
||||
</RNTesterButton>
|
||||
<Modal
|
||||
animationType={animationType}
|
||||
presentationStyle={presentationStyle}
|
||||
transparent={transparent}
|
||||
hardwareAccelerated={hardwareAccelerated}
|
||||
statusBarTranslucent={statusBarTranslucent}
|
||||
visible={visible}
|
||||
onRequestClose={onDismiss}
|
||||
supportedOrientations={supportedOrientations[supportedOrientationKey]}
|
||||
onOrientationChange={onOrientationChange}
|
||||
onDismiss={onDismiss}
|
||||
onShow={onShow}>
|
||||
<View style={[styles.modalContainer, modalBackgroundStyle]}>
|
||||
<View
|
||||
style={[
|
||||
styles.modalInnerContainer,
|
||||
innerContainerTransparentStyle,
|
||||
]}>
|
||||
{...props}
|
||||
onRequestClose={onRequestClose}
|
||||
onOrientationChange={onOrientationChange}>
|
||||
<View style={styles.modalContainer}>
|
||||
<View style={styles.modalInnerContainer}>
|
||||
<Text>
|
||||
This modal was presented with animationType: '{animationType}'
|
||||
This modal was presented with animationType: '
|
||||
{props.animationType}'
|
||||
</Text>
|
||||
{Platform.OS === 'ios' ? (
|
||||
<Text>
|
||||
It is currently displayed in {currentOrientation} mode.
|
||||
</Text>
|
||||
) : null}
|
||||
<RNTesterButton onPress={onDismiss}>Close</RNTesterButton>
|
||||
<RNTesterButton
|
||||
onPress={() => setProps(prev => ({...prev, visible: false}))}>
|
||||
Close
|
||||
</RNTesterButton>
|
||||
{controls}
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
@@ -113,101 +249,13 @@ function ModalPresentation() {
|
||||
key={type}
|
||||
style={styles.option}
|
||||
label={type}
|
||||
onPress={() => setAnimationType(type)}
|
||||
selected={type === animationType}
|
||||
onPress={() => setProps(prev => ({...prev, animationType: type}))}
|
||||
selected={type === props.animationType}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
{Platform.OS === 'android' && Platform.isTV !== true ? (
|
||||
<>
|
||||
<View style={styles.inlineBlock}>
|
||||
<Text style={styles.title}>Status Bar Translucent</Text>
|
||||
<Switch
|
||||
value={statusBarTranslucent}
|
||||
onValueChange={() =>
|
||||
setStatusBarTranslucent(!statusBarTranslucent)
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.inlineBlock}>
|
||||
<Text style={styles.title}>Hardware Acceleration</Text>
|
||||
<Switch
|
||||
value={hardwareAccelerated}
|
||||
onValueChange={() => setHardwareAccelerated(!hardwareAccelerated)}
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
) : null}
|
||||
{Platform.isTV !== true ? (
|
||||
<>
|
||||
{Platform.OS === 'ios' ? (
|
||||
<View style={styles.block}>
|
||||
<Text style={styles.title}>Presentation Style</Text>
|
||||
<View style={styles.row}>
|
||||
{presentationStyles.map(type => (
|
||||
<RNTOption
|
||||
key={type}
|
||||
style={styles.option}
|
||||
label={type}
|
||||
onPress={() => {
|
||||
if (type !== 'overFullScreen' && transparent) {
|
||||
setTransparent(false);
|
||||
}
|
||||
setPresentationStyle(type);
|
||||
}}
|
||||
selected={type === presentationStyle}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
) : null}
|
||||
<View style={styles.block}>
|
||||
<View style={styles.rowWithSpaceBetween}>
|
||||
<Text style={styles.title}>Transparent</Text>
|
||||
<Switch
|
||||
value={transparent}
|
||||
disabled={presentationStyle !== 'overFullScreen'}
|
||||
onValueChange={() => setTransparent(!transparent)}
|
||||
/>
|
||||
</View>
|
||||
{Platform.OS === 'ios' && presentationStyle !== 'overFullScreen' ? (
|
||||
<Text style={styles.warning}>
|
||||
iOS Modal can only be transparent with 'overFullScreen'
|
||||
Presentation Style
|
||||
</Text>
|
||||
) : null}
|
||||
</View>
|
||||
<View style={styles.block}>
|
||||
<Text style={styles.title}>Supported Orientation</Text>
|
||||
<View style={styles.row}>
|
||||
{Object.keys(supportedOrientations).map(label => (
|
||||
<RNTOption
|
||||
key={label}
|
||||
style={styles.option}
|
||||
label={label}
|
||||
onPress={() => setSupportedOrientationKey(label)}
|
||||
selected={label === supportedOrientationKey}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.block}>
|
||||
<Text style={styles.title}>Actions</Text>
|
||||
<View style={styles.row}>
|
||||
{actions.map(value => (
|
||||
<RNTOption
|
||||
key={value}
|
||||
style={styles.option}
|
||||
label={value}
|
||||
onPress={() => setAction(value)}
|
||||
selected={value === action}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
) : null}
|
||||
{controls}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -249,7 +297,8 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
modalInnerContainer: {
|
||||
borderRadius: 10,
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#fff',
|
||||
padding: 10,
|
||||
},
|
||||
warning: {
|
||||
margin: 3,
|
||||
|
||||
Reference in New Issue
Block a user