From c8dd83aebe2ba7c0a69f7b7513dacfbff2e30dce Mon Sep 17 00:00:00 2001
From: Oswaldo Acauan <oswaldoacauan@gmail.com>
Date: Wed, 5 Apr 2017 14:25:26 +0000
Subject: [PATCH] Split modal into two separated components and move state to a
 hoc on the base

---
 .../imports/ui/components/about/component.jsx |  2 +-
 .../breakout-join-confirmation/component.jsx  |  4 +-
 .../logout-confirmation/component.jsx         |  2 +-
 .../ui/components/modal/base/component.jsx    | 90 ++++++++++++++++---
 .../ui/components/modal/base/styles.scss      | 10 +--
 .../modal/{ => fullscreen}/component.jsx      | 67 ++++----------
 .../modal/{ => fullscreen}/styles.scss        |  2 +-
 .../ui/components/modal/simple/component.jsx  | 64 +++++++++++++
 .../ui/components/modal/simple/styles.scss    | 44 +++++++++
 .../ui/components/settings/component.jsx      |  2 +-
 10 files changed, 211 insertions(+), 76 deletions(-)
 rename bigbluebutton-html5/imports/ui/components/modal/{ => fullscreen}/component.jsx (54%)
 rename bigbluebutton-html5/imports/ui/components/modal/{ => fullscreen}/styles.scss (93%)
 create mode 100644 bigbluebutton-html5/imports/ui/components/modal/simple/component.jsx
 create mode 100644 bigbluebutton-html5/imports/ui/components/modal/simple/styles.scss

diff --git a/bigbluebutton-html5/imports/ui/components/about/component.jsx b/bigbluebutton-html5/imports/ui/components/about/component.jsx
index 02d7944750..9fb8d48508 100644
--- a/bigbluebutton-html5/imports/ui/components/about/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/about/component.jsx
@@ -1,6 +1,6 @@
 import React, { Component } from 'react';
 import { defineMessages, injectIntl } from 'react-intl';
-import Modal from '/imports/ui/components/modal/component';
+import Modal from '/imports/ui/components/modal/simple/component';
 
 const intlMessages = defineMessages({
   title: {
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/component.jsx b/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/component.jsx
index e23f29fd11..29d514a69d 100644
--- a/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/component.jsx
@@ -1,8 +1,8 @@
 import React, { Component } from 'react';
 import { defineMessages, injectIntl } from 'react-intl';
 import { clearModal } from '/imports/ui/components/app/service';
-import { exitAudio } from '/imports/api/phone'
-import Modal from '/imports/ui/components/modal/component';
+import { exitAudio } from '/imports/api/phone';
+import Modal from '/imports/ui/components/modal/fullscreen/component';
 
 const intlMessages = defineMessages({
   title: {
diff --git a/bigbluebutton-html5/imports/ui/components/logout-confirmation/component.jsx b/bigbluebutton-html5/imports/ui/components/logout-confirmation/component.jsx
index 7bcfd43d4a..5450ca3aa6 100755
--- a/bigbluebutton-html5/imports/ui/components/logout-confirmation/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/logout-confirmation/component.jsx
@@ -1,7 +1,7 @@
 import React, { Component } from 'react';
 import { withRouter } from 'react-router';
 import { defineMessages, injectIntl } from 'react-intl';
-import Modal from '/imports/ui/components/modal/component';
+import Modal from '/imports/ui/components/modal/fullscreen/component';
 
 const intlMessages = defineMessages({
   title: {
diff --git a/bigbluebutton-html5/imports/ui/components/modal/base/component.jsx b/bigbluebutton-html5/imports/ui/components/modal/base/component.jsx
index c1b82ce8e8..9bfe25e927 100755
--- a/bigbluebutton-html5/imports/ui/components/modal/base/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/modal/base/component.jsx
@@ -1,16 +1,22 @@
 import React, { Component, PropTypes } from 'react';
 import ReactModal from 'react-modal';
 import styles from './styles.scss';
-import cx from 'classnames';
 
 const propTypes = {
+  modalClassName: PropTypes.string.isRequired,
+  overlayClassName: PropTypes.string.isRequired,
+  portalClassName: PropTypes.string.isRequired,
+  contentLabel: PropTypes.string.isRequired,
   isOpen: PropTypes.bool.isRequired,
   onShow: PropTypes.func,
   onHide: PropTypes.func,
-  isTransparent: PropTypes.bool
 };
 
 const defaultProps = {
+  modalClassName: styles.modal,
+  overlayClassName: styles.overlay,
+  portalClassName: styles.portal,
+  contentLabel: 'Modal',
   isOpen: false,
 };
 
@@ -41,20 +47,23 @@ export default class ModalBase extends Component {
       isOpen,
       onShow,
       onHide,
-      className,
-      isTransparent,
+      contentLabel,
+      shouldCloseOnOverlayClick,
+      modalClassName,
+      overlayClassName,
+      portalClassName,
     } = this.props;
 
-    let styleOverlay = (isTransparent) ? styles.transparentOverlay : styles.overlay;
-
     return (
       <ReactModal
-      className={cx(styles.modal, className)}
-      overlayClassName={styleOverlay}
-      portalClassName={styles.portal}
-      isOpen={isOpen}
-      onAfterOpen={this.handleAfterOpen}
-      onRequestClose={this.handleRequestClose}>
+        className={modalClassName}
+        contentLabel={contentLabel}
+        shouldCloseOnOverlayClick={shouldCloseOnOverlayClick}
+        overlayClassName={overlayClassName}
+        portalClassName={portalClassName}
+        isOpen={isOpen}
+        onAfterOpen={this.handleAfterOpen}
+        onRequestClose={this.handleRequestClose}>
         {this.props.children}
       </ReactModal>
     );
@@ -63,3 +72,60 @@ export default class ModalBase extends Component {
 
 ModalBase.propTypes = propTypes;
 ModalBase.defaultProps = defaultProps;
+
+export const withModalBase = (ComponentToWrap) =>
+  class ModalStateWrapper extends Component {
+    constructor(props) {
+      super(props);
+
+      this.state = {
+        isOpen: props.isOpen || true,
+      };
+
+      this.hide = this.hide.bind(this);
+    }
+
+    hide(cb) {
+      this.setState({ isOpen: false }, cb);
+    }
+
+    show(cb) {
+      this.setState({ isOpen: true }, cb);
+    }
+
+    componentDidUpdate(prevProps, prevState) {
+      if (prevState.isOpen !== this.props.isOpen
+        && this.state.isOpen !== this.props.isOpen) {
+        this.setState({ isOpen: this.props.isOpen });
+      }
+    }
+
+    render() {
+      const { isOpen } = this.state;
+      const {
+        modalClassName,
+        overlayClassName,
+        portalClassName,
+        contentLabel,
+        shouldCloseOnOverlayClick,
+        onShow,
+        onHide,
+        ...modalProps,
+      } = this.props;
+
+      return (
+        <ModalBase {...{
+          isOpen,
+          modalClassName,
+          overlayClassName,
+          portalClassName,
+          contentLabel,
+          shouldCloseOnOverlayClick,
+          onShow,
+          onHide,
+        }}>
+          <ComponentToWrap {...modalProps} hide={this.hide} show={this.show}/>
+        </ModalBase>
+      );
+    }
+  };
diff --git a/bigbluebutton-html5/imports/ui/components/modal/base/styles.scss b/bigbluebutton-html5/imports/ui/components/modal/base/styles.scss
index d3384dee01..b1dd764206 100755
--- a/bigbluebutton-html5/imports/ui/components/modal/base/styles.scss
+++ b/bigbluebutton-html5/imports/ui/components/modal/base/styles.scss
@@ -20,11 +20,7 @@
   }
 }
 
-.transparentOverlay {
-  background: transparentize($color-gray-dark, .40) !important;
-}
-
-.overlay, .transparentOverlay {
+.overlay {
   z-index: 1000;
   background: #fff;
   display: flex;
@@ -37,4 +33,6 @@
   bottom: 0;
 }
 
-.portal {}
+.portal {
+  overflow: hidden;
+}
diff --git a/bigbluebutton-html5/imports/ui/components/modal/component.jsx b/bigbluebutton-html5/imports/ui/components/modal/fullscreen/component.jsx
similarity index 54%
rename from bigbluebutton-html5/imports/ui/components/modal/component.jsx
rename to bigbluebutton-html5/imports/ui/components/modal/fullscreen/component.jsx
index 3720a77f6a..ec29ba877f 100755
--- a/bigbluebutton-html5/imports/ui/components/modal/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/modal/fullscreen/component.jsx
@@ -1,12 +1,10 @@
 import React, { Component, PropTypes } from 'react';
-import { clearModal } from '/imports/ui/components/app/service';
-import ModalBase from './base/component';
-import Button from '../button/component';
+import { withModalBase } from '../base/component';
+import Button from '/imports/ui/components/button/component';
 import styles from './styles.scss';
 import cx from 'classnames';
 
 const propTypes = {
-  isOpen: PropTypes.bool.isRequired,
   title: PropTypes.string.isRequired,
   confirm: PropTypes.shape({
     callback: PropTypes.func.isRequired,
@@ -21,7 +19,7 @@ const propTypes = {
 };
 
 const defaultProps = {
-  isOpen: true,
+  shouldCloseOnOverlayClick: false,
   confirm: {
     label: 'Done',
     description: 'Saves changes and closes the modal',
@@ -32,40 +30,10 @@ const defaultProps = {
   },
 };
 
-export default class Modal extends Component {
-  constructor(props) {
-    super(props);
-
-    this.state = {
-      isOpen: this.props.isOpen,
-    };
-
-    this.handleDismiss = this.handleDismiss.bind(this);
-    this.handleConfirm = this.handleConfirm.bind(this);
-  }
-
-  handleDismiss() {
-    const { dismiss } = this.props;
-    if (dismiss && dismiss.callback) {
-      dismiss.callback(...arguments);
-    }
-
-    this.setState({ isOpen: false });
-    clearModal();
-  }
-
-  handleConfirm() {
-    const { confirm } = this.props;
-    confirm.callback(...arguments);
-    this.setState({ isOpen: false });
-    clearModal();
-  }
-
-  componentDidUpdate(prevProps, prevState) {
-    if (prevState.isOpen !== this.props.isOpen
-      && this.state.isOpen !== this.props.isOpen) {
-      this.setState({ isOpen: this.props.isOpen });
-    }
+class ModalFullscreen extends Component {
+  handleAction(name) {
+    const action = this.props[name];
+    this.props.hide(action.callback);
   }
 
   render() {
@@ -75,29 +43,22 @@ export default class Modal extends Component {
       confirm,
     } = this.props;
 
-    const { isOpen } = this.state;
-
     return (
-      <ModalBase
-        className={styles.modal}
-        isOpen={isOpen}
-        onHide={dismiss.callback}
-        onShow={this.props.onShow}
-      >
+      <div>
         <header className={styles.header}>
           <h1 className={styles.title}>{title}</h1>
           <div className={styles.actions}>
             <Button
               className={styles.dismiss}
               label={dismiss.label}
-              onClick={this.handleDismiss}
+              onClick={this.handleAction.bind(this, 'dismiss')}
               aria-describedby={'modalDismissDescription'}
               tabIndex={0} />
             <Button
               color={'primary'}
               className={styles.confirm}
               label={confirm.label}
-              onClick={this.handleConfirm}
+              onClick={this.handleAction.bind(this, 'confirm')}
               aria-describedby={'modalConfirmDescription'}
               tabIndex={0} />
           </div>
@@ -107,10 +68,12 @@ export default class Modal extends Component {
         </div>
         <div id="modalDismissDescription" hidden>{dismiss.description}</div>
         <div id="modalConfirmDescription" hidden>{confirm.description}</div>
-      </ModalBase>
+      </div>
     );
   }
 };
 
-Modal.propTypes = propTypes;
-Modal.defaultProps = defaultProps;
+ModalFullscreen.propTypes = propTypes;
+ModalFullscreen.defaultProps = defaultProps;
+
+export default withModalBase(ModalFullscreen);
diff --git a/bigbluebutton-html5/imports/ui/components/modal/styles.scss b/bigbluebutton-html5/imports/ui/components/modal/fullscreen/styles.scss
similarity index 93%
rename from bigbluebutton-html5/imports/ui/components/modal/styles.scss
rename to bigbluebutton-html5/imports/ui/components/modal/fullscreen/styles.scss
index f611dd9f00..55917b4c0b 100755
--- a/bigbluebutton-html5/imports/ui/components/modal/styles.scss
+++ b/bigbluebutton-html5/imports/ui/components/modal/fullscreen/styles.scss
@@ -1,4 +1,4 @@
-@import "../../stylesheets/variables/_all";
+@import "../../../stylesheets/variables/_all";
 
 .modal {
   display: flex;
diff --git a/bigbluebutton-html5/imports/ui/components/modal/simple/component.jsx b/bigbluebutton-html5/imports/ui/components/modal/simple/component.jsx
new file mode 100644
index 0000000000..0e0518dec2
--- /dev/null
+++ b/bigbluebutton-html5/imports/ui/components/modal/simple/component.jsx
@@ -0,0 +1,64 @@
+import React, { Component, PropTypes } from 'react';
+import { withModalBase } from '../base/component';
+import Button from '/imports/ui/components/button/component';
+import styles from './styles.scss';
+import cx from 'classnames';
+
+const propTypes = {
+  title: PropTypes.string.isRequired,
+  dismiss: PropTypes.shape({
+    callback: PropTypes.func,
+    label: PropTypes.string.isRequired,
+    description: PropTypes.string,
+  }),
+};
+
+const defaultProps = {
+  overlayClassName: styles.overlay,
+  dismiss: {
+    label: 'Close',
+    description: 'Closes the modal',
+  },
+};
+
+class ModalSimple extends Component {
+  handleAction(name) {
+    const action = this.props[name];
+    this.props.close(action.callback);
+  }
+
+  render() {
+    const {
+      title,
+      dismiss,
+    } = this.props;
+
+    return (
+      <div>
+        <header className={styles.header}>
+          <h1 className={styles.title}>{title}</h1>
+          <div className={styles.actions}>
+            <Button
+              className={styles.dismiss}
+              onClick={() => this.props.hide(dismiss.callback)}
+              label={dismiss.label}
+              aria-describedby={'modalDismissDescription'}
+              icon={'close'}
+              size={'lg'}
+              hideLabel={true}
+              tabIndex={0} />
+          </div>
+        </header>
+        <div className={styles.content}>
+          {this.props.children}
+        </div>
+        <div id="modalDismissDescription" hidden>{dismiss.description}</div>
+      </div>
+    );
+  }
+};
+
+ModalSimple.propTypes = propTypes;
+ModalSimple.defaultProps = defaultProps;
+
+export default withModalBase(ModalSimple);
diff --git a/bigbluebutton-html5/imports/ui/components/modal/simple/styles.scss b/bigbluebutton-html5/imports/ui/components/modal/simple/styles.scss
new file mode 100644
index 0000000000..62c304389d
--- /dev/null
+++ b/bigbluebutton-html5/imports/ui/components/modal/simple/styles.scss
@@ -0,0 +1,44 @@
+@import "../../../stylesheets/variables/_all";
+
+.modal {
+  display: flex;
+  flex-direction: column;
+  align-self: flex-start;
+  padding: ($line-height-computed / 2) $line-height-computed;
+}
+
+.content {
+  overflow: auto;
+  color: $color-text;
+  font-weight: normal;
+  padding: $line-height-computed 0;
+}
+
+.header {
+  display: flex;
+  padding: $line-height-computed 0;
+  border-bottom: $border-size solid $color-gray-lighter;
+  flex-shrink: 0;
+}
+
+.actions {
+  flex: 0 1 35%;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.title {
+  @extend %text-elipsis;
+  flex: 1;
+  margin: 0;
+  font-weight: 400;
+}
+
+.dismiss {
+  flex: 0 1 48%;
+}
+
+.overlay {
+  background-color: rgba(0, 0, 0, .5);
+}
diff --git a/bigbluebutton-html5/imports/ui/components/settings/component.jsx b/bigbluebutton-html5/imports/ui/components/settings/component.jsx
index 51aa3e4c5f..c02b349e20 100644
--- a/bigbluebutton-html5/imports/ui/components/settings/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/settings/component.jsx
@@ -1,5 +1,5 @@
 import React, { Component } from 'react';
-import Modal from '/imports/ui/components/modal/component';
+import Modal from '/imports/ui/components/modal/fullscreen/component';
 import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
 
 import ClosedCaptions from '/imports/ui/components/settings/submenus/closed-captions/component';
-- 
GitLab