Refactor modal components

1. Split out base modal stylings from iframe modal stylings
2. Move iframe-specific stylings into a separate copmonent
3. Move width/height iframe stylings to scss
4. Change global stylings for modals to match #980
5. Update styles to use trello style guide
This commit is contained in:
Matthew Taylor 2016-11-08 14:43:10 -05:00
parent fa1b6c6c00
commit c9de1d73d9
18 changed files with 280 additions and 149 deletions

View file

@ -72,7 +72,7 @@
"react": "15.1.0", "react": "15.1.0",
"react-dom": "15.0.1", "react-dom": "15.0.1",
"react-intl": "2.1.2", "react-intl": "2.1.2",
"react-modal": "1.3.0", "react-modal": "1.5.2",
"react-onclickoutside": "4.1.1", "react-onclickoutside": "4.1.1",
"react-redux": "4.4.5", "react-redux": "4.4.5",
"react-responsive": "1.1.4", "react-responsive": "1.1.4",

View file

@ -1,16 +1,13 @@
var connect = require('react-redux').connect; var connect = require('react-redux').connect;
var omit = require('lodash.omit');
var React = require('react'); var React = require('react');
var sessionActions = require('../../redux/session.js'); var sessionActions = require('../../redux/session.js');
var Modal = require('../modal/modal.jsx'); var IframeModal = require('../modal/iframe/modal.jsx');
var Registration = require('../registration/registration.jsx'); var Registration = require('../registration/registration.jsx');
require('./intro.scss'); require('./intro.scss');
Modal.setAppElement(document.getElementById('view'));
var Intro = React.createClass({ var Intro = React.createClass({
type: 'Intro', type: 'Intro',
getDefaultProps: function () { getDefaultProps: function () {
@ -52,11 +49,6 @@ var Intro = React.createClass({
this.closeRegistration(); this.closeRegistration();
}, },
render: function () { render: function () {
var frameProps = {
width: 570,
height: 357,
padding: 15
};
return ( return (
<div className="intro"> <div className="intro">
<div className="content"> <div className="content">
@ -130,15 +122,12 @@ var Intro = React.createClass({
<img src="//cdn.scratch.mit.edu/scratchr2/static/images/hp-video-screenshot.png" <img src="//cdn.scratch.mit.edu/scratchr2/static/images/hp-video-screenshot.png"
alt="Intro Video" /> alt="Intro Video" />
</div> </div>
<Modal <IframeModal
className="video-modal" className="mod-intro-video"
isOpen={this.state.videoOpen} isOpen={this.state.videoOpen}
onRequestClose={this.closeVideo} onRequestClose={this.closeVideo}
style={{content:frameProps}}> src="//player.vimeo.com/video/65583694?title=0&amp;byline=0&amp;portrait=0"
<iframe />
src="//player.vimeo.com/video/65583694?title=0&amp;byline=0&amp;portrait=0"
{...omit(frameProps, 'padding')} />
</Modal>
</div> </div>
); );
} }

View file

@ -241,3 +241,13 @@
} }
} }
} }
.modal-content.mod-intro-video {
padding: 15px;
width: 35.625rem;
}
.modal-content-iframe.mod-intro-video {
width: 35.625rem;
min-height: 22.3125rem;
}

View file

@ -0,0 +1,58 @@
var classNames = require('classnames');
var omit = require('lodash.omit');
var React = require('react');
var ReactModal = require('react-modal');
require('./modal.scss');
/**
* Container for pop up windows (See: registration window)
*/
var Modal = React.createClass({
type: 'Modal',
statics: {
setAppElement: ReactModal.setAppElement
},
propTypes: {
className: React.PropTypes.string,
overlayClassName: React.PropTypes.string
},
getDefaultProps: function () {
return {
className: '',
overlayClassName: ''
};
},
requestClose: function () {
return this.refs.modal.portal.requestClose();
},
render: function () {
var modalClasses = classNames(
'modal-content',
this.props.className
);
var overlayClasses = classNames(
'modal-overlay',
this.props.overlayClassName
);
return (
<ReactModal
ref="modal"
className={modalClasses}
overlayClassName={overlayClasses}
{...omit(this.props, ['className', 'overlayClassName'])}
>
<div className="modal-content-close" onClick={this.requestClose}>
<img
className="modal-content-close-img"
src="/svgs/modal/close-x.svg"
alt="close-icon"
/>
</div>
{this.props.children}
</ReactModal>
);
}
});
module.exports = Modal;

View file

@ -0,0 +1,61 @@
@import "../../../colors";
@import "../../../frameless";
.modal-content {
position: relative;
margin: 3.75rem auto;
border-radius: 1rem;
box-shadow: 0 0 0 1px $active-gray;
background-color: $ui-white;
padding: 0;
width: 48.75rem;
}
.modal-overlay {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 100;
background-color: transparentize($ui-blue, .3);
}
.ReactModal__Content:focus {
outline: none;
}
$modal-close-size: 1rem;
.modal-content-close {
position: absolute;
top: $modal-close-size / 2;
right: $modal-close-size / 2;
border-radius: $modal-close-size;
background-color: $active-dark-gray;
cursor: pointer;
width: $modal-close-size * 2;
height: $modal-close-size * 2;
text-align: center;
line-height: $modal-close-size * 2;
}
.modal-content-close-img {
padding-top: $modal-close-size / 2;
}
@media only screen and (max-width: $desktop - 1) {
.modal-content {
top: 0;
left: 0;
margin-top: 0;
border-radius: 0;
box-shadow: none;
width: 100%;
height: 100%;
overflow: scroll;
}
.modal-content-close {
position: fixed;
}
}

View file

@ -0,0 +1,47 @@
var classNames = require('classnames');
var React = require('react');
var Modal = require('../base/modal.jsx');
require('./modal.scss');
Modal.setAppElement(document.getElementById('view'));
var IframeModal = React.createClass({
propTypes: {
componentKey: React.PropTypes.string,
isOpen: React.PropTypes.bool,
onRequestClose: React.PropTypes.func,
className: React.PropTypes.string,
componentRef: React.PropTypes.string,
src: React.PropTypes.string
},
getDefaultProps: function () {
return {
className: '',
iframeClassName: ''
};
},
render: function () {
var iframeClasses = classNames(
'modal-content-iframe',
this.props.className
);
return (
<Modal
key={this.props.componentKey}
isOpen={this.props.isOpen}
onRequestClose={this.props.onRequestClose}
className={this.props.className}
>
<iframe
ref={this.props.componentRef}
src={this.props.src}
className={iframeClasses}
/>
</Modal>
);
}
});
module.exports = IframeModal;

View file

@ -0,0 +1,5 @@
@import "../../../frameless";
.modal-content-iframe {
border: 0;
}

View file

@ -1,64 +0,0 @@
var clone = require('lodash.clone');
var defaultsDeep = require('lodash.defaultsdeep');
var React = require('react');
var ReactModal = require('react-modal');
require('./modal.scss');
var defaultStyle = {
overlay: {
zIndex: 100,
backgroundColor: 'rgba(0, 0, 0, .75)'
},
content: {
position: 'absolute',
overflow: 'visible',
borderRadius: '6px',
width: 500,
height: 250,
padding: 0,
top: '50%',
right: 'auto',
bottom: 'auto',
left: '50%',
marginTop: -125,
marginLeft: -250
}
};
/**
* Container for pop up windows (See: registration window)
*/
var Modal = React.createClass({
type: 'Modal',
statics: {
setAppElement: ReactModal.setAppElement
},
getDefaultProps: function () {
return {
style: defaultStyle
};
},
calculateStyle: function () {
var style = clone(this.props.style, true);
defaultsDeep(style, defaultStyle);
style.content.marginTop = (style.content.height + style.content.padding*2) / -2;
style.content.marginLeft = (style.content.width + style.content.padding*2) / -2;
return style;
},
requestClose: function () {
return this.refs.modal.portal.requestClose();
},
render: function () {
return (
<ReactModal ref="modal"
{...this.props}
style={this.calculateStyle()}>
<div className="modal-close" onClick={this.requestClose}></div>
{this.props.children}
</ReactModal>
);
}
});
module.exports = Modal;

View file

@ -1,34 +0,0 @@
@import "../../colors";
.ReactModal__Content {
&:focus {
outline: none;
}
iframe {
border: 0;
}
}
.modal-close {
$modal-close-size: 20px;
position: absolute;
top: 0;
right: 0;
margin-top: -$modal-close-size / 2;
margin-right: -$modal-close-size / 2;
border: 2px solid $ui-border;
border-radius: $modal-close-size / 2;
background-color: $active-dark-gray;
cursor: pointer;
width: $modal-close-size;
height: $modal-close-size;
text-align: center;
line-height: $modal-close-size;
color: $type-white;
font-size: $modal-close-size;
&:before {
content: "x";
}
}

View file

@ -1,6 +1,6 @@
var React = require('react'); var React = require('react');
var NavigationBox = require('../container/navigation.jsx'); var NavigationBox = require('../base/navigation.jsx');
require('./navigation.scss'); require('./navigation.scss');

View file

@ -15,8 +15,8 @@ var Form = require('../../forms/form.jsx');
var Input = require('../../forms/input.jsx'); var Input = require('../../forms/input.jsx');
var log = require('../../../lib/log.js'); var log = require('../../../lib/log.js');
var Login = require('../../login/login.jsx'); var Login = require('../../login/login.jsx');
var Modal = require('../../modal/modal.jsx'); var Modal = require('../../modal/base/modal.jsx');
var NavigationBox = require('../container/navigation.jsx'); var NavigationBox = require('../base/navigation.jsx');
var Registration = require('../../registration/registration.jsx'); var Registration = require('../../registration/registration.jsx');
require('./navigation.scss'); require('./navigation.scss');

View file

@ -1,10 +1,8 @@
var React = require('react'); var React = require('react');
var Modal = require('../modal/modal.jsx'); var IframeModal = require('../modal/iframe/modal.jsx');
require('./registration.scss'); require('./registration.scss');
Modal.setAppElement(document.getElementById('view'));
var Registration = React.createClass({ var Registration = React.createClass({
propTypes: { propTypes: {
isOpen: React.PropTypes.bool, isOpen: React.PropTypes.bool,
@ -36,18 +34,14 @@ var Registration = React.createClass({
this.toggleMessageListener(false); this.toggleMessageListener(false);
}, },
render: function () { render: function () {
var frameProps = {
width: 610,
height: 438
};
return ( return (
<Modal <IframeModal
isOpen={this.props.isOpen} isOpen={this.props.isOpen}
onRequestClose={this.props.onRequestClose} onRequestClose={this.props.onRequestClose}
className="registration" className="mod-registration"
style={{content:frameProps}}> componentRef="registrationIframe"
<iframe ref="registrationIframe" src="/accounts/standalone-registration/" {...frameProps} /> src="/accounts/standalone-registration/"
</Modal> />
); );
} }
}); });

View file

@ -1,3 +1,22 @@
.registration { @import "../../frameless";
.modal-content.mod-registration {
width: 38.125rem;
overflow: hidden; overflow: hidden;
} }
.modal-content-iframe.mod-registration {
width: 38.125rem;
min-height: 27.375rem;
}
@media only screen and (max-width: $tablet - 1) {
.modal-content.mod-registration {
width: 100%;
overflow: scroll;
}
.modal-content-iframe.mod-registration {
height: 27.375rem;
}
}

View file

@ -1,6 +1,5 @@
var connect = require('react-redux').connect; var connect = require('react-redux').connect;
var injectIntl = require('react-intl').injectIntl; var injectIntl = require('react-intl').injectIntl;
var omit = require('lodash.omit');
var React = require('react'); var React = require('react');
var api = require('../../lib/api'); var api = require('../../lib/api');
@ -16,7 +15,7 @@ var Box = require('../../components/box/box.jsx');
var Button = require('../../components/forms/button.jsx'); var Button = require('../../components/forms/button.jsx');
var Carousel = require('../../components/carousel/carousel.jsx'); var Carousel = require('../../components/carousel/carousel.jsx');
var Intro = require('../../components/intro/intro.jsx'); var Intro = require('../../components/intro/intro.jsx');
var Modal = require('../../components/modal/modal.jsx'); var IframeModal = require('../../components/modal/iframe/modal.jsx');
var News = require('../../components/news/news.jsx'); var News = require('../../components/news/news.jsx');
var Page = require('../../components/page/www/page.jsx'); var Page = require('../../components/page/www/page.jsx');
var TeacherBanner = require('../../components/teacher-banner/teacher-banner.jsx'); var TeacherBanner = require('../../components/teacher-banner/teacher-banner.jsx');
@ -308,7 +307,6 @@ var Splash = injectIntl(React.createClass({
}, },
render: function () { render: function () {
var featured = this.renderHomepageRows(); var featured = this.renderHomepageRows();
var emailConfirmationStyle = {width: 500, height: 330, padding: 1};
var homepageCacheState = this.getHomepageRefreshStatus(); var homepageCacheState = this.getHomepageRefreshStatus();
var formatHTMLMessage = this.props.intl.formatHTMLMessage; var formatHTMLMessage = this.props.intl.formatHTMLMessage;
@ -344,21 +342,23 @@ var Splash = injectIntl(React.createClass({
return ( return (
<div className="splash"> <div className="splash">
{this.shouldShowEmailConfirmation() ? [ {this.shouldShowEmailConfirmation() ? [
<DropdownBanner key="confirmedEmail" <DropdownBanner
className="warning" key="confirmedEmail"
onRequestDismiss={this.handleDismiss.bind(this, 'confirmed_email')}> className="warning"
onRequestDismiss={this.handleDismiss.bind(this, 'confirmed_email')}
>
<a href="#" onClick={this.showEmailConfirmationModal}>Confirm your email</a> <a href="#" onClick={this.showEmailConfirmationModal}>Confirm your email</a>
{' '}to enable sharing.{' '} {' '}to enable sharing.{' '}
<a href="/info/faq/#accounts">Having trouble?</a> <a href="/info/faq/#accounts">Having trouble?</a>
</DropdownBanner>, </DropdownBanner>,
<Modal key="emailConfirmationModal" <IframeModal
isOpen={this.state.emailConfirmationModalOpen} componentKey="emailConfirmationModal"
onRequestClose={this.hideEmailConfirmationModal} isOpen={this.state.emailConfirmationModalOpen}
style={{content: emailConfirmationStyle}}> onRequestClose={this.hideEmailConfirmationModal}
<iframe ref="emailConfirmationiFrame" className="mod-confirmation"
src="/accounts/email_resend_standalone/" componentRef="emailConfirmationiFrame"
{...omit(emailConfirmationStyle, 'padding')} /> src="/accounts/email_resend_standalone/"
</Modal> />
] : []} ] : []}
{this.props.permissions.educator ? [ {this.props.permissions.educator ? [
<TeacherBanner key="teacherbanner" messages={messages} /> <TeacherBanner key="teacherbanner" messages={messages} />

View file

@ -42,6 +42,16 @@
} }
} }
.modal-content.mod-confirmation {
width: 31.25rem;
}
.modal-content-iframe.mod-confirmation {
border-radius: 1rem;
width: 31.25rem;
min-height: 20.625rem;
}
//4 columns //4 columns
@media only screen and (max-width: $mobile - 1) { @media only screen and (max-width: $mobile - 1) {
.splash { .splash {
@ -54,6 +64,15 @@
} }
} }
} }
.modal-content.mod-confirmation {
width: 100%;
overflow: scroll;
}
.modal-content-iframe.mod-confirmation {
border-radius: 0;
}
} }
//6 columns //6 columns
@ -68,6 +87,16 @@
} }
} }
} }
.modal-content.mod-confirmation {
width: 100%;
overflow: scroll;
}
.modal-content-iframe.mod-confirmation {
border-radius: 0;
width: $tablet - 1;
}
} }
//6 columns //6 columns

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<g>
<g>
<path class="st0" d="M12.5,10.3c0.6,0.6,0.6,1.5,0,2.1c-0.3,0.3-0.7,0.4-1.1,0.4c-0.4,0-0.8-0.1-1.1-0.4L8,10.1l-2.3,2.3
c-0.3,0.3-0.7,0.4-1.1,0.4c-0.4,0-0.8-0.1-1.1-0.4c-0.6-0.6-0.6-1.5,0-2.1L5.9,8L3.5,5.7C3,5.1,3,4.1,3.5,3.5
C4.1,3,5.1,3,5.7,3.5L8,5.9l2.3-2.3c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1L10.1,8L12.5,10.3z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 801 B