From e873c597bb14251966863fe022f68f8a4393e95e Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Mon, 17 Aug 2020 22:04:24 -0700 Subject: [PATCH] Allow passing props through to determine modal options --- .../components/dashboard/ApiKeyModal.tsx | 2 +- .../components/elements/ConfirmationModal.tsx | 14 ++++---- .../scripts/components/elements/Modal.tsx | 14 ++++---- resources/scripts/hoc/asModal.tsx | 35 +++++++++++++------ 4 files changed, 39 insertions(+), 26 deletions(-) diff --git a/resources/scripts/components/dashboard/ApiKeyModal.tsx b/resources/scripts/components/dashboard/ApiKeyModal.tsx index db511edb8..e73274309 100644 --- a/resources/scripts/components/dashboard/ApiKeyModal.tsx +++ b/resources/scripts/components/dashboard/ApiKeyModal.tsx @@ -32,7 +32,7 @@ const ApiKeyModal = ({ apiKey }: Props) => { ApiKeyModal.displayName = 'ApiKeyModal'; -export default asModal({ +export default asModal({ closeOnEscape: false, closeOnBackground: false, })(ApiKeyModal); diff --git a/resources/scripts/components/elements/ConfirmationModal.tsx b/resources/scripts/components/elements/ConfirmationModal.tsx index 48562d9f1..d67c74d1a 100644 --- a/resources/scripts/components/elements/ConfirmationModal.tsx +++ b/resources/scripts/components/elements/ConfirmationModal.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useEffect } from 'react'; +import React, { useContext } from 'react'; import tw from 'twin.macro'; import Button from '@/components/elements/Button'; import asModal from '@/hoc/asModal'; @@ -12,12 +12,8 @@ type Props = { showSpinnerOverlay?: boolean; }; -const ConfirmationModal = ({ title, children, buttonText, onConfirmed, showSpinnerOverlay }: Props) => { - const { dismiss, toggleSpinner } = useContext(ModalContext); - - useEffect(() => { - toggleSpinner(showSpinnerOverlay); - }, [ showSpinnerOverlay ]); +const ConfirmationModal = ({ title, children, buttonText, onConfirmed }: Props) => { + const { dismiss } = useContext(ModalContext); return ( <> @@ -37,4 +33,6 @@ const ConfirmationModal = ({ title, children, buttonText, onConfirmed, showSpinn ConfirmationModal.displayName = 'ConfirmationModal'; -export default asModal()(ConfirmationModal); +export default asModal(props => ({ + showSpinnerOverlay: props.showSpinnerOverlay, +}))(ConfirmationModal); diff --git a/resources/scripts/components/elements/Modal.tsx b/resources/scripts/components/elements/Modal.tsx index de0be05e6..395f7a5a1 100644 --- a/resources/scripts/components/elements/Modal.tsx +++ b/resources/scripts/components/elements/Modal.tsx @@ -86,12 +86,14 @@ const Modal: React.FC = ({ visible, appear, dismissable, showSpinner } {showSpinnerOverlay && -
- -
+ +
+ +
+
}
{children} diff --git a/resources/scripts/hoc/asModal.tsx b/resources/scripts/hoc/asModal.tsx index 2311b1441..7db437c14 100644 --- a/resources/scripts/hoc/asModal.tsx +++ b/resources/scripts/hoc/asModal.tsx @@ -1,6 +1,7 @@ import React from 'react'; import Modal, { ModalProps } from '@/components/elements/Modal'; import ModalContext from '@/context/ModalContext'; +import isEqual from 'react-fast-compare'; export interface AsModalProps { visible: boolean; @@ -12,26 +13,34 @@ type ExtendedModalProps = Omit interface State { render: boolean; visible: boolean; - showSpinnerOverlay: boolean; + modalProps: ExtendedModalProps | undefined; } -function asModal (modalProps?: ExtendedModalProps) { - // eslint-disable-next-line @typescript-eslint/ban-types - return function (Component: React.ComponentType) { - return class extends React.PureComponent { +type ExtendedComponentType = (C: React.ComponentType) => React.ComponentType; + +// eslint-disable-next-line @typescript-eslint/ban-types +function asModal

(modalProps?: ExtendedModalProps | ((props: P) => ExtendedModalProps)): ExtendedComponentType

{ + return function (Component) { + return class extends React.PureComponent

{ static displayName = `asModal(${Component.displayName})`; - constructor (props: T & AsModalProps) { + constructor (props: P & AsModalProps) { super(props); this.state = { render: props.visible, visible: props.visible, - showSpinnerOverlay: modalProps?.showSpinnerOverlay || false, + modalProps: typeof modalProps === 'function' ? modalProps(this.props) : modalProps, }; } - componentDidUpdate (prevProps: Readonly) { + componentDidUpdate (prevProps: Readonly

) { + const mapped = typeof modalProps === 'function' ? modalProps(this.props) : modalProps; + if (!isEqual(this.state.modalProps, mapped)) { + // noinspection JSPotentiallyInvalidUsageOfThis + this.setState({ modalProps: mapped }); + } + if (prevProps.visible && !this.props.visible) { // noinspection JSPotentiallyInvalidUsageOfThis this.setState({ visible: false }); @@ -43,7 +52,12 @@ function asModal (modalProps?: ExtendedModalProps) { dismiss = () => this.setState({ visible: false }); - toggleSpinner = (value?: boolean) => this.setState({ showSpinnerOverlay: value || false }); + toggleSpinner = (value?: boolean) => this.setState(s => ({ + modalProps: { + ...s.modalProps, + showSpinnerOverlay: value || false, + }, + })); render () { return ( @@ -58,13 +72,12 @@ function asModal (modalProps?: ExtendedModalProps) { this.setState({ render: false }, () => { if (typeof this.props.onModalDismissed === 'function') { this.props.onModalDismissed(); } })} - {...modalProps} + {...this.state.modalProps} >