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