diff --git a/packages/rn-tester/js/examples/Modal/ModalCustomizable.js b/packages/rn-tester/js/examples/Modal/ModalCustomizable.js
new file mode 100644
index 00000000000..2b9c72d37d3
--- /dev/null
+++ b/packages/rn-tester/js/examples/Modal/ModalCustomizable.js
@@ -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 (
+
+
+ {this.props.children}
+
+
+ );
+ }
+}
+
+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 (
+ <>
+ Hardware Accelerated
+
+ Status Bar Translucent
+
+ Transparent
+
+ >
+ );
+ }
+ return (
+
+ );
+ }
+
+ 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 (
+
+ 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);
+ }
+ }}>
+
+
+
+ This modal was presented{' '}
+ {this.state.animationType === 'none' ? 'without' : 'with'}{' '}
+ animation.
+
+
+ It is currently displayed in {this.state.currentOrientation}{' '}
+ mode.
+
+
+
+
+
+
+ Animation Type
+
+
+
+
+
+ {this.renderSwitch()}
+ {this.renderPickers()}
+
+
+ );
+ }
+ renderPickers(): React.Node {
+ if (Platform.isTV) {
+ return null;
+ }
+ return (
+
+
+ Presentation style
+
+ this.setState({presentationStyle})
+ }
+ itemStyle={styles.pickerItem}>
+
+
+
+
+
+
+
+
+
+ Supported orientations
+
+ this.setState({selectedSupportedOrientation: i.toString()})
+ }
+ itemStyle={styles.pickerItem}>
+
+
+
+
+
+
+
+
+
+
+ Actions
+ {Platform.OS === 'ios' ? (
+ this.setState({action})}
+ itemStyle={styles.pickerItem}>
+
+
+
+
+ ) : (
+ this.setState({action})}
+ itemStyle={styles.pickerItem}>
+
+
+
+ )}
+
+
+ );
+ }
+}
+
+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 => ,
+}: RNTesterExampleModuleItem);
diff --git a/packages/rn-tester/js/examples/Modal/ModalExample.js b/packages/rn-tester/js/examples/Modal/ModalExample.js
index 1cfae6d4d93..1c0405029ff 100644
--- a/packages/rn-tester/js/examples/Modal/ModalExample.js
+++ b/packages/rn-tester/js/examples/Modal/ModalExample.js
@@ -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 (
-
-
- {this.props.children}
-
-
- );
- }
-}
-
-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 (
- <>
- Hardware Accelerated
-
- Status Bar Translucent
-
- Transparent
-
- >
- );
- }
- return (
-
- );
- }
-
- 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 (
-
- 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);
- }}>
-
-
-
- This modal was presented{' '}
- {this.state.animationType === 'none' ? 'without' : 'with'}{' '}
- animation.
-
-
- It is currently displayed in {this.state.currentOrientation}{' '}
- mode.
-
-
-
-
-
-
- Animation Type
-
-
-
-
-
- {this.renderSwitch()}
- {this.renderPickers()}
-
-
- );
- }
- renderPickers() {
- if (Platform.isTV) {
- return null;
- }
- return (
-
-
- Presentation style
-
- this.setState({presentationStyle})
- }
- itemStyle={styles.pickerItem}>
-
-
-
-
-
-
-
-
-
- Supported orientations
-
- this.setState({selectedSupportedOrientation: i.toString()})
- }
- itemStyle={styles.pickerItem}>
-
-
-
-
-
-
-
-
-
-
- Actions
- {Platform.OS === 'ios' ? (
- this.setState({action})}
- itemStyle={styles.pickerItem}>
-
-
-
-
- ) : (
- this.setState({action})}
- itemStyle={styles.pickerItem}>
-
-
-
- )}
-
-
- );
- }
-}
-
-exports.examples = [
- {
- title: 'Modal Presentation',
- name: 'basic',
- description: 'Modals can be presented with or without animation',
- render: (): React.Node => ,
- },
-];
-
-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);
diff --git a/packages/rn-tester/js/examples/Modal/ModalOnShow.js b/packages/rn-tester/js/examples/Modal/ModalOnShow.js
new file mode 100644
index 00000000000..dc24ace791a
--- /dev/null
+++ b/packages/rn-tester/js/examples/Modal/ModalOnShow.js
@@ -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 (
+
+ {
+ setOnShowCount(onShowCount + 1);
+ }}
+ onDismiss={() => {
+ setOnDismissCount(onDismissCount + 1);
+ }}
+ onRequestClose={() => {
+ setModalVisible(false);
+ }}>
+
+
+
+ onShow is called {onShowCount} times
+
+
+ onDismiss is called {onDismissCount} times
+
+ setModalVisible(false)}>
+
+ Hide Modal
+
+
+
+
+
+ onShow is called {onShowCount} times
+
+ onDismiss is called {onDismissCount} times
+
+ setModalVisible(true)}>
+
+ Show Modal
+
+
+
+ );
+}
+
+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 => ,
+}: RNTesterExampleModuleItem);