styledot
Version:
23andMe Design System
125 lines (113 loc) • 4.31 kB
JSX
// This is the layer that executes on the official react-modal component
// This is accessed by
// - modal-instance.jsx (for React usage)
// - modal-init.js (for vanilla usage)
// https://style.23andme.com/components/#modals-section for more information
import React from 'react';
import PropTypes from 'prop-types';
import ReactModal from 'react-modal';
import { v4 as uuid } from 'uuid';
ReactModal.setAppElement('body');
class Modal extends React.Component {
constructor(props) {
super(props);
this.state = {
hasScrolled: false,
};
this.handleScroll = this.handleScroll.bind(this);
this.closeModal = this.props.closeModal.bind(this);
this.ctaPosition = props.ctaPosition ? `dismiss-cta-${props.ctaPosition}` : '';
this.uid = uuid();
}
classString() {
let defaultClasses = 'sd-modal sd-elevation-2';
if (this.props.alternateModal) {
defaultClasses += ` sd-modal-${this.props.alternateModal}`;
if (this.props.useWhiteDismissButton) {
defaultClasses += ` mod-modal-white-dismiss`;
}
}
return defaultClasses;
}
overlayClassString() {
let overlayDefaultClasses = 'sd-modal-overlay';
if (this.props.alternateModal === 'interstitial') {
overlayDefaultClasses += ` mod-overlay-dark`;
}
return overlayDefaultClasses;
}
handleScroll() {
const wrapper = document.querySelector('.sd-modal-wrapper');
if (wrapper.scrollTop > 0 && !this.state.hasScrolled) {
this.setState({ hasScrolled: true });
} else if (wrapper.scrollTop === 0 && this.state.hasScrolled) {
this.setState({ hasScrolled: false });
}
}
render() {
return (
<ReactModal
isOpen={this.props.isVisible}
onRequestClose={this.props.closeModal}
className={this.classString()}
overlayClassName={this.overlayClassString()}
bodyOpenClassName="sd-modal-body"
contentLabel={this.props.contentLabel}
aria={{
describedby: `modal-contents-${this.uid}`
modal: true,
}}
>
<button
className={`sd-unbutton sd-modal-dismiss-top ${this.state.hasScrolled ? 'mod-scrolled' : ''}`}
onClick={this.props.closeModal}
aria-label="Close"
/>
<div id={`modal-contents-${this.uid}`} className="sd-modal-wrapper" onScroll={this.handleScroll}>
{this.props.contents && (
<div
// Careful here!
dangerouslySetInnerHTML={{
__html: this.props.contents,
}}
/>
)}
{this.props.children && this.props.children}
{this.props.includeDismissCta && (
<div className={`sd-modal-dismiss-cta-container ${this.ctaPosition}`}>
<button
className={`sd-button sd-modal-dismiss-cta ${this.props.extraClasses}`}
onClick={this.props.closeModal}
>
{this.props.dismissText}
</button>
</div>
)}
</div>
</ReactModal>
);
}
}
Modal.defaultProps = {
contents: '',
dismissText: 'Close',
ctaPosition: '',
extraClasses: '',
includeDismissCta: true,
alternateModal: '',
useWhiteDismissButton: false,
contentLabel: 'Modal',
};
Modal.propTypes = {
isVisible: PropTypes.bool.isRequired,
closeModal: PropTypes.func.isRequired,
contents: PropTypes.string,
dismissText: PropTypes.string,
ctaPosition: PropTypes.string,
extraClasses: PropTypes.string,
includeDismissCta: PropTypes.bool,
alternateModal: PropTypes.string,
useWhiteDismissButton: PropTypes.bool,
contentLabel: PropTypes.string,
};
export default Modal;