Use TypeScript for modal
[MAILPOET-2777]
This commit is contained in:
@@ -1,13 +1,19 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
type Props = {
|
||||||
|
fullScreen?: boolean,
|
||||||
|
role?: string,
|
||||||
|
className?: string,
|
||||||
|
children: React.ReactNode,
|
||||||
|
};
|
||||||
|
|
||||||
function ModalFrame({
|
function ModalFrame({
|
||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
role,
|
role,
|
||||||
fullScreen,
|
fullScreen,
|
||||||
}) {
|
}: Props) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classnames(
|
className={classnames(
|
||||||
@@ -16,20 +22,13 @@ function ModalFrame({
|
|||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
role={role}
|
role={role}
|
||||||
tabIndex="-1"
|
tabIndex={-1}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ModalFrame.propTypes = {
|
|
||||||
fullScreen: PropTypes.bool,
|
|
||||||
role: PropTypes.string,
|
|
||||||
className: PropTypes.string,
|
|
||||||
children: PropTypes.node.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
ModalFrame.defaultProps = {
|
ModalFrame.defaultProps = {
|
||||||
role: 'dialog',
|
role: 'dialog',
|
||||||
fullScreen: false,
|
fullScreen: false,
|
@@ -1,9 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import Heading from 'common/typography/heading/heading';
|
import Heading from 'common/typography/heading/heading';
|
||||||
|
|
||||||
const ModalHeader = ({ title }) => (
|
type Props = {
|
||||||
|
title: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
const ModalHeader = ({ title }: Props) => (
|
||||||
<div className="mailpoet-modal-header">
|
<div className="mailpoet-modal-header">
|
||||||
<Heading level={3}>
|
<Heading level={3}>
|
||||||
{ title }
|
{ title }
|
||||||
@@ -11,8 +14,4 @@ const ModalHeader = ({ title }) => (
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
ModalHeader.propTypes = {
|
|
||||||
title: PropTypes.string.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ModalHeader;
|
export default ModalHeader;
|
@@ -1,11 +1,23 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import ModalFrame from './frame.jsx';
|
import ModalFrame from './frame';
|
||||||
import ModalHeader from './header.jsx';
|
import ModalHeader from './header';
|
||||||
import ModalOverlay from './overlay.jsx';
|
import ModalOverlay from './overlay';
|
||||||
import closeIcon from './close_icon';
|
import ModalCloseIcon from './close_icon';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
isDismissible?: boolean,
|
||||||
|
contentClassName?: string,
|
||||||
|
overlayClassName?: string,
|
||||||
|
title?: string,
|
||||||
|
onRequestClose?: () => void,
|
||||||
|
fullScreen?: boolean,
|
||||||
|
shouldCloseOnEsc?: boolean,
|
||||||
|
shouldCloseOnClickOutside?: boolean,
|
||||||
|
role?: string,
|
||||||
|
children: React.ReactNode,
|
||||||
|
};
|
||||||
|
|
||||||
function Modal({
|
function Modal({
|
||||||
onRequestClose,
|
onRequestClose,
|
||||||
@@ -18,7 +30,7 @@ function Modal({
|
|||||||
contentClassName,
|
contentClassName,
|
||||||
overlayClassName,
|
overlayClassName,
|
||||||
fullScreen,
|
fullScreen,
|
||||||
}) {
|
}: Props) {
|
||||||
return createPortal(
|
return createPortal(
|
||||||
<ModalOverlay
|
<ModalOverlay
|
||||||
onRequestClose={onRequestClose}
|
onRequestClose={onRequestClose}
|
||||||
@@ -35,7 +47,7 @@ function Modal({
|
|||||||
<ModalHeader title={title} />
|
<ModalHeader title={title} />
|
||||||
) }
|
) }
|
||||||
{ isDismissible && (
|
{ isDismissible && (
|
||||||
<button type="button" onClick={onRequestClose} className="mailpoet-modal-close">{closeIcon}</button>
|
<button type="button" onClick={onRequestClose} className="mailpoet-modal-close">{ModalCloseIcon}</button>
|
||||||
) }
|
) }
|
||||||
<div
|
<div
|
||||||
className="mailpoet-modal-content"
|
className="mailpoet-modal-content"
|
||||||
@@ -49,19 +61,6 @@ function Modal({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Modal.propTypes = {
|
|
||||||
children: PropTypes.node,
|
|
||||||
isDismissible: PropTypes.bool,
|
|
||||||
contentClassName: PropTypes.string,
|
|
||||||
overlayClassName: PropTypes.string,
|
|
||||||
title: PropTypes.string,
|
|
||||||
onRequestClose: PropTypes.func,
|
|
||||||
fullScreen: PropTypes.bool,
|
|
||||||
shouldCloseOnEsc: PropTypes.bool,
|
|
||||||
shouldCloseOnClickOutside: PropTypes.bool,
|
|
||||||
role: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
Modal.defaultProps = {
|
Modal.defaultProps = {
|
||||||
bodyOpenClassName: 'modal-open',
|
bodyOpenClassName: 'modal-open',
|
||||||
onRequestClose: () => {},
|
onRequestClose: () => {},
|
@@ -1,16 +1,23 @@
|
|||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
const ESCAPE = 27;
|
const ESCAPE = 27;
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onRequestClose?: (event: React.SyntheticEvent) => void,
|
||||||
|
shouldCloseOnEsc?: boolean,
|
||||||
|
shouldCloseOnClickOutside?: boolean,
|
||||||
|
className?: string,
|
||||||
|
children: React.ReactNode,
|
||||||
|
};
|
||||||
|
|
||||||
function ModalOverlay({
|
function ModalOverlay({
|
||||||
onRequestClose,
|
onRequestClose,
|
||||||
shouldCloseOnEsc,
|
shouldCloseOnEsc,
|
||||||
shouldCloseOnClickOutside,
|
shouldCloseOnClickOutside,
|
||||||
className,
|
className,
|
||||||
children,
|
children,
|
||||||
}) {
|
}: Props) {
|
||||||
const overlayRef = useRef(null);
|
const overlayRef = useRef(null);
|
||||||
|
|
||||||
// get focus on render so keys such as ESC work immediately
|
// get focus on render so keys such as ESC work immediately
|
||||||
@@ -18,27 +25,27 @@ function ModalOverlay({
|
|||||||
overlayRef.current.focus();
|
overlayRef.current.focus();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function onClose(event) {
|
function onClose(event: React.SyntheticEvent) {
|
||||||
if (onRequestClose) {
|
if (onRequestClose) {
|
||||||
onRequestClose(event);
|
onRequestClose(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFocusOutside(event) {
|
function handleFocusOutside(event: React.MouseEvent) {
|
||||||
// filter only to clicks on overlay
|
// filter only to clicks on overlay
|
||||||
if (shouldCloseOnClickOutside && overlayRef.current === event.target) {
|
if (shouldCloseOnClickOutside && overlayRef.current === event.target) {
|
||||||
onClose(event);
|
onClose(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEscapeKeyDown(event) {
|
function handleEscapeKeyDown(event: React.KeyboardEvent) {
|
||||||
if (shouldCloseOnEsc) {
|
if (shouldCloseOnEsc) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
onClose(event);
|
onClose(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeyDown(event) {
|
function handleKeyDown(event: React.KeyboardEvent) {
|
||||||
if (event.keyCode === ESCAPE) {
|
if (event.keyCode === ESCAPE) {
|
||||||
handleEscapeKeyDown(event);
|
handleEscapeKeyDown(event);
|
||||||
}
|
}
|
||||||
@@ -54,21 +61,13 @@ function ModalOverlay({
|
|||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
onClick={handleFocusOutside}
|
onClick={handleFocusOutside}
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex="0"
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ModalOverlay.propTypes = {
|
|
||||||
onRequestClose: PropTypes.func,
|
|
||||||
shouldCloseOnEsc: PropTypes.bool,
|
|
||||||
shouldCloseOnClickOutside: PropTypes.bool,
|
|
||||||
className: PropTypes.string,
|
|
||||||
children: PropTypes.node.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
ModalOverlay.defaultProps = {
|
ModalOverlay.defaultProps = {
|
||||||
onRequestClose: () => {},
|
onRequestClose: () => {},
|
||||||
shouldCloseOnEsc: true,
|
shouldCloseOnEsc: true,
|
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
|||||||
import ReactStringReplace from 'react-string-replace';
|
import ReactStringReplace from 'react-string-replace';
|
||||||
import jQuery from 'jquery';
|
import jQuery from 'jquery';
|
||||||
import MailPoet from 'mailpoet';
|
import MailPoet from 'mailpoet';
|
||||||
import Modal from 'common/modal/modal.jsx';
|
import Modal from 'common/modal/modal';
|
||||||
import { GlobalContext } from 'context';
|
import { GlobalContext } from 'context';
|
||||||
|
|
||||||
const mailPoetApiVersion = (window as any).mailpoet_api_version as string;
|
const mailPoetApiVersion = (window as any).mailpoet_api_version as string;
|
||||||
|
@@ -8,7 +8,7 @@ import { Spinner, SelectControl } from '@wordpress/components';
|
|||||||
import { useDispatch, useSelect } from '@wordpress/data';
|
import { useDispatch, useSelect } from '@wordpress/data';
|
||||||
|
|
||||||
import Preview from 'common/preview/preview.jsx';
|
import Preview from 'common/preview/preview.jsx';
|
||||||
import Modal from 'common/modal/modal.jsx';
|
import Modal from 'common/modal/modal';
|
||||||
import BelowPostsSettings from './below_posts_settings';
|
import BelowPostsSettings from './below_posts_settings';
|
||||||
import PopUpSettings from './popup_settings';
|
import PopUpSettings from './popup_settings';
|
||||||
import OtherSettings from './other_settings';
|
import OtherSettings from './other_settings';
|
||||||
|
Reference in New Issue
Block a user