From 04fb0122b2a378d5be339b8cf3f950104e02cda8 Mon Sep 17 00:00:00 2001 From: Evan Charlton Date: Thu, 17 Aug 2023 23:58:28 -0700 Subject: [PATCH] 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: [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) | |:--:|:--:| | ![image](https://github.com/facebook/react-native/assets/418560/ef49cb94-14d8-4ebe-ade5-699b8e67ba5c) | ![image](https://github.com/facebook/react-native/assets/418560/d4bb6ef5-7d8c-4d5f-b4d8-37b8d736b19a) | Reviewed By: yungsters Differential Revision: D48414351 Pulled By: NickGerleman fbshipit-source-id: 54bece639f4c64132dfb21c54c91d46972f5a335 --- packages/rn-tester/js/components/RNTOption.js | 2 +- .../js/examples/Modal/ModalPresentation.js | 351 ++++++++++-------- 2 files changed, 201 insertions(+), 152 deletions(-) diff --git a/packages/rn-tester/js/components/RNTOption.js b/packages/rn-tester/js/components/RNTOption.js index 98993c4247d..0761424e17d 100644 --- a/packages/rn-tester/js/components/RNTOption.js +++ b/packages/rn-tester/js/components/RNTOption.js @@ -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}> diff --git a/packages/rn-tester/js/examples/Modal/ModalPresentation.js b/packages/rn-tester/js/examples/Modal/ModalPresentation.js index b2b2aab6bb6..7d4c42293dd 100644 --- a/packages/rn-tester/js/examples/Modal/ModalPresentation.js +++ b/packages/rn-tester/js/examples/Modal/ModalPresentation.js @@ -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({ + 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 = ( + <> + + Status Bar Translucent 🟢 + + setProps(prev => ({...prev, statusBarTranslucent: enabled})) + } + /> + + + Hardware Acceleration 🟢 + + setProps(prev => ({ + ...prev, + hardwareAccelerated: enabled, + })) + } + /> + + + Presentation Style ⚫️ + + {presentationStyles.map(type => ( + + 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} + /> + ))} + + + + + Transparent + + setProps(prev => ({...prev, transparent: enabled})) + } + /> + + {Platform.OS === 'ios' && presentationStyle !== 'overFullScreen' ? ( + + iOS Modal can only be transparent with 'overFullScreen' Presentation + Style + + ) : null} + + + Supported Orientation ⚫️ + + {supportedOrientations.map(orientation => ( + + 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)} + /> + ))} + + + + Actions + + + setProps(prev => ({ + ...prev, + onShow: prev.onShow ? undefined : onShow, + })) + } + selected={!!props.onShow} + /> + + setProps(prev => ({ + ...prev, + onDismiss: prev.onDismiss ? undefined : onDismiss, + })) + } + selected={!!props.onDismiss} + /> + + + + ); + return ( - setVisible(true)}> + setProps(prev => ({...prev, visible: true}))}> Show Modal - - + {...props} + onRequestClose={onRequestClose} + onOrientationChange={onOrientationChange}> + + - This modal was presented with animationType: '{animationType}' + This modal was presented with animationType: ' + {props.animationType}' {Platform.OS === 'ios' ? ( It is currently displayed in {currentOrientation} mode. ) : null} - Close + setProps(prev => ({...prev, visible: false}))}> + Close + + {controls} @@ -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} /> ))} - {Platform.OS === 'android' && Platform.isTV !== true ? ( - <> - - Status Bar Translucent - - setStatusBarTranslucent(!statusBarTranslucent) - } - /> - - - Hardware Acceleration - setHardwareAccelerated(!hardwareAccelerated)} - /> - - - ) : null} - {Platform.isTV !== true ? ( - <> - {Platform.OS === 'ios' ? ( - - Presentation Style - - {presentationStyles.map(type => ( - { - if (type !== 'overFullScreen' && transparent) { - setTransparent(false); - } - setPresentationStyle(type); - }} - selected={type === presentationStyle} - /> - ))} - - - ) : null} - - - Transparent - setTransparent(!transparent)} - /> - - {Platform.OS === 'ios' && presentationStyle !== 'overFullScreen' ? ( - - iOS Modal can only be transparent with 'overFullScreen' - Presentation Style - - ) : null} - - - Supported Orientation - - {Object.keys(supportedOrientations).map(label => ( - setSupportedOrientationKey(label)} - selected={label === supportedOrientationKey} - /> - ))} - - - - Actions - - {actions.map(value => ( - setAction(value)} - selected={value === action} - /> - ))} - - - - ) : null} + {controls} ); } @@ -249,7 +297,8 @@ const styles = StyleSheet.create({ }, modalInnerContainer: { borderRadius: 10, - alignItems: 'center', + backgroundColor: '#fff', + padding: 10, }, warning: { margin: 3,