diff --git a/src/components/Modal/BaseModal.js b/src/components/Modal/BaseModal.js index faf5a4ecb65e..a00b9d142dce 100644 --- a/src/components/Modal/BaseModal.js +++ b/src/components/Modal/BaseModal.js @@ -1,15 +1,28 @@ import React, {PureComponent} from 'react'; import {View} from 'react-native'; +import PropTypes from 'prop-types'; import ReactNativeModal from 'react-native-modal'; import {SafeAreaInsetsContext} from 'react-native-safe-area-context'; import KeyboardShortcut from '../../libs/KeyboardShortcut'; import styles, {getModalPaddingStyles, getSafeAreaPadding} from '../../styles/styles'; import themeColors from '../../styles/themes/default'; -import {propTypes, defaultProps} from './ModalPropTypes'; +import {propTypes as modalPropTypes, defaultProps as modalDefaultProps} from './ModalPropTypes'; import getModalStyles from '../../styles/getModalStyles'; import {setModalVisibility} from '../../libs/actions/Modal'; +const propTypes = { + ...modalPropTypes, + + /** The ref to the modal container */ + forwardedRef: PropTypes.func, +}; + +const defaultProps = { + ...modalDefaultProps, + forwardedRef: () => {}, +}; + class BaseModal extends PureComponent { constructor(props) { super(props); @@ -94,6 +107,8 @@ class BaseModal extends PureComponent { backdropColor={themeColors.modalBackdrop} backdropOpacity={hideBackdrop ? 0 : 0.5} backdropTransitionOutTiming={0} + hasBackdrop={this.props.fullscreen} + coverScreen={this.props.fullscreen} style={modalStyle} deviceHeight={this.props.windowHeight} deviceWidth={this.props.windowWidth} @@ -128,6 +143,7 @@ class BaseModal extends PureComponent { ...modalContainerStyle, ...modalPaddingStyles, }} + ref={this.props.forwardedRef} > {this.props.children} @@ -142,4 +158,7 @@ class BaseModal extends PureComponent { BaseModal.propTypes = propTypes; BaseModal.defaultProps = defaultProps; BaseModal.displayName = 'BaseModal'; -export default BaseModal; +export default React.forwardRef((props, ref) => ( + // eslint-disable-next-line react/jsx-props-no-spreading + +)); diff --git a/src/components/Modal/ModalPropTypes.js b/src/components/Modal/ModalPropTypes.js index 8da6403308a3..b0b554ff4c21 100644 --- a/src/components/Modal/ModalPropTypes.js +++ b/src/components/Modal/ModalPropTypes.js @@ -4,6 +4,12 @@ import CONST from '../../CONST'; import {windowDimensionsPropTypes} from '../withWindowDimensions'; const propTypes = { + /** Decides whether the modal should cover fullscreen. FullScreen modal has backdrop */ + fullscreen: PropTypes.bool, + + /** Should we close modal on outside click */ + shouldCloseOnOutsideClick: PropTypes.bool, + /** Should we announce the Modal visibility changes? */ shouldSetModalVisibility: PropTypes.bool, @@ -52,6 +58,8 @@ const propTypes = { }; const defaultProps = { + fullscreen: true, + shouldCloseOnOutsideClick: false, shouldSetModalVisibility: true, onSubmit: null, type: '', diff --git a/src/components/Modal/index.js b/src/components/Modal/index.js index 169b63d794e6..f74fdd6137dc 100644 --- a/src/components/Modal/index.js +++ b/src/components/Modal/index.js @@ -1,14 +1,47 @@ -import React from 'react'; +import React, {Component} from 'react'; import withWindowDimensions from '../withWindowDimensions'; import BaseModal from './BaseModal'; import {propTypes, defaultProps} from './ModalPropTypes'; -const Modal = props => ( - // eslint-disable-next-line react/jsx-props-no-spreading - - {props.children} - -); +class Modal extends Component { + constructor(props) { + super(props); + this.closeOnOutsideClick = this.closeOnOutsideClick.bind(this); + } + + componentDidMount() { + if (this.props.shouldCloseOnOutsideClick) { + document.addEventListener('mousedown', this.closeOnOutsideClick); + } + } + + componentWillUnmount() { + if (this.props.shouldCloseOnOutsideClick) { + document.removeEventListener('mousedown', this.closeOnOutsideClick); + } + } + + closeOnOutsideClick(event) { + if (this.props.isVisible + && this.baseModalRef + && !this.baseModalRef.contains(event.target) + && this.props.shouldCloseOnOutsideClick) { + this.props.onClose(); + } + } + + render() { + return ( + this.baseModalRef = el} + // eslint-disable-next-line react/jsx-props-no-spreading + {...this.props} + > + {this.props.children} + + ); + } +} Modal.propTypes = propTypes; Modal.defaultProps = defaultProps; diff --git a/src/components/Popover/PopoverPropTypes.js b/src/components/Popover/PopoverPropTypes.js index ae11632acc0e..acc030ad80c0 100644 --- a/src/components/Popover/PopoverPropTypes.js +++ b/src/components/Popover/PopoverPropTypes.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import {propTypes as modalPropTypes, defaultProps as defaultModalProps} from '../Modal/ModalPropTypes'; const propTypes = { - ...(_.omit(modalPropTypes, 'type', 'popoverAnchorPosition')), + ...(_.omit(modalPropTypes, ['type', 'popoverAnchorPosition'])), /** The anchor position of the popover */ anchorPosition: PropTypes.shape({ @@ -15,7 +15,7 @@ const propTypes = { }; const defaultProps = { - ...(_.omit(defaultModalProps, 'type', 'popoverAnchorPosition')), + ...(_.omit(defaultModalProps, ['type', 'popoverAnchorPosition'])), // Anchor position is optional only because it is not relevant on mobile anchorPosition: {}, diff --git a/src/components/Popover/index.js b/src/components/Popover/index.js index bfb412f53a2d..e2ea99d50b2b 100644 --- a/src/components/Popover/index.js +++ b/src/components/Popover/index.js @@ -1,4 +1,5 @@ import React from 'react'; +import {createPortal} from 'react-dom'; import {propTypes, defaultProps} from './PopoverPropTypes'; import CONST from '../../CONST'; import Modal from '../Modal'; @@ -8,16 +9,32 @@ import withWindowDimensions from '../withWindowDimensions'; * This is a convenience wrapper around the Modal component for a responsive Popover. * On small screen widths, it uses BottomDocked modal type, and a Popover type on wide screen widths. */ -const Popover = props => ( - -); +const Popover = (props) => { + if (!props.fullscreen && !props.isSmallScreenWidth) { + return createPortal( + , + document.body, + ); + } + return ( + + ); +}; Popover.propTypes = propTypes; Popover.defaultProps = defaultProps; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 9b8b7d6d0947..305d1b442c24 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -279,6 +279,7 @@ class ReportActionItem extends Component { animationOutTiming={1} measureContent={this.measureContent} shouldSetModalVisibility={false} + fullscreen={false} >