Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DES-882: Modal now resizes when browser is resized #25

Merged
merged 6 commits into from
Jan 18, 2018
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion demo/ModalPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ class ModalPage extends Component {
footerVisible={false}
cancelBtnHandler={() => this.setState({secondModalIsShown:false})}
successBtnHandler={() => this.setState({secondModalIsShown:false})}>
<p>{text.bodyText}</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Molestias reprehenderit illum, incidunt corrupti laborum. Qui necessitatibus quisquam incidunt. Quos, inventore ullam? Odio delectus eum, quisquam nisi dolor eveniet laboriosam ab?
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Molestias reprehenderit illum, incidunt corrupti laborum. Qui necessitatibus quisquam incidunt. Quos, inventore ullam? Odio delectus eum, quisquam nisi dolor eveniet laboriosam ab?
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Molestias reprehenderit illum, incidunt corrupti laborum. Qui necessitatibus quisquam incidunt. Quos, inventore ullam? Odio delectus eum, quisquam nisi dolor eveniet laboriosam ab?
</p>
</ModalWithOutFooter>

<ModalWithoutClose
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
],
"dependencies": {
"ally.js": "^1.4.1",
"lodash.debounce": "^4.0.8",
"npm-scripts": "^1.4.0",
"pearson-compounds": "0.14.x",
"react-modal": "3.1.7"
Expand Down
282 changes: 137 additions & 145 deletions src/js/Modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import { default as BaseModal } from 'react-modal';
import { Icon } from 'pearson-compounds';
import ally from 'ally.js';
import debounce from 'lodash.debounce';

import '../scss/Modal.scss';

Expand All @@ -16,17 +17,6 @@ export default class Modal extends Component {
shiftTab: false,
tab: false
};

this.onClose = _onClose.bind(this);
this.renderFooter = _renderFooter.bind(this);
this.afterOpen = _afterOpen.bind(this);
this.applyWrapper = _applyWrapper.bind(this);
this.removeWrapper = _removeWrapper.bind(this);
this.successBtnHandler = _successBtnHandler.bind(this);
this.cancelBtnHandler = _cancelBtnHandler.bind(this);
this.removeOverlayStyle = _removeOverlayStyle.bind(this);
this.handleKeyDown = _handleKeyDown.bind(this);

};

componentWillReceiveProps(nextProps) {
Expand All @@ -35,9 +25,143 @@ export default class Modal extends Component {
this.removeWrapper();
}
}


componentWillUnmount() {
window.removeEventListener("resize", this.resizeModal);
}

handleKeyDown = (event) => {
if (!event.shiftKey && event.which === 9 && !this.state.tab) {
this.state.tab = true;
return;
}

if (event.shiftKey && event.which === 9 && !this.state.shiftTab && !this.state.tab) {
event.preventDefault();
this.state.shiftTab = true;
const tabbableConfig = { context: '.modalContent' };
const tabbableElements = ally.query.tabbable(tabbableConfig);
tabbableElements[tabbableElements.length-1].focus();
}
};

afterOpen = () => {
const modalContent = document.getElementsByClassName('modalContent')[0];

// apply accessibility wrapper if no appElement is given
if (!this.props.appElement) {
this.applyWrapper();
}

// apply Focus to close button on open...
modalContent.focus();
modalContent.addEventListener('keydown', this.handleKeyDown);

window.addEventListener("resize", this.resizeModal);
this.setDimensions();
};

onClose = () => {
this.cancelBtnHandler();
this.state.shiftTab = false;
this.state.tab = false;
window.removeEventListener("resize", this.resizeModal);
};

successBtnHandler = () => {
this.removeOverlayStyle();
this.removeWrapper();
this.props.successBtnHandler.call(this);
};

cancelBtnHandler = () => {
this.removeOverlayStyle();
this.removeWrapper();
this.props.cancelBtnHandler.call(this);
};

removeOverlayStyle = () => {
const modalBody = document.getElementsByClassName('modalBody')[0];
const modalOverlay = document.getElementsByClassName('modalOverlay')[0];

modalBody.style.maxHeight = '';
modalOverlay.style.paddingTop = '';
modalOverlay.style.paddingBottom = '';
};

resizeModal = debounce(() => {
this.setDimensions();
}, 100);

setDimensions = () => {
const modalBody = document.getElementsByClassName('modalBody')[0];
const headerCloseButton = document.getElementsByClassName('modalClose')[0];
const modalContent = document.getElementsByClassName('modalContent')[0];
const modalOverlay = document.getElementsByClassName('modalOverlay')[0];
const header = document.getElementsByClassName('modalHeader')[0];
const footer = document.getElementsByClassName('modalFooter')[0];

// apply padding based on clientHeight...
const windowHeight = window.innerHeight;
const contentHeight = modalContent.offsetHeight;
const paddingHeight = (windowHeight - contentHeight) / 2;
const headerHeight = header.getBoundingClientRect().height;
const footerHeight = footer ? footer.getBoundingClientRect().height : 0;

modalBody.style.maxHeight = this.props.scrollWithPage ? 'none' : `${windowHeight - (headerHeight + footerHeight + 120)}px`;
modalOverlay.style.paddingTop = paddingHeight > 0 ? `${paddingHeight}px` : '2%';

// conditional borders on modalbody if scrollbar is present...
modalBody.className = (contentHeight < modalBody.scrollHeight && !headerCloseButton) ? 'modalBody modalBody_border' : 'modalBody modalBody_border_normal';
};

applyWrapper = () => {
if (!document.getElementById('wrapper')) {
const wrapper = document.createElement('div');
wrapper.id = 'wrapper';
wrapper.setAttribute('aria-hidden', true);

const excludedElement = document.getElementsByClassName('modalOverlay')[0].parentElement;

while (document.body.firstChild) {
wrapper.appendChild(document.body.firstChild);
}

document.body.appendChild(wrapper);
document.body.appendChild(excludedElement);
}
};

removeWrapper = () => {
const wrapper = document.getElementById('wrapper');
if (!wrapper) { return; }

wrapper.setAttribute('aria-hidden', false);

const excludedElement = document.getElementsByClassName('modalOverlay')[0].parentElement;

while (wrapper.firstChild) {
document.body.appendChild(wrapper.firstChild);
}

document.body.removeChild(wrapper);
document.body.appendChild(excludedElement);
};

renderFooter = (footerVisible, text, disableSuccessBtn) => {
if (footerVisible) {
return (
<div className="modalFooter">
<button onClick={this.cancelBtnHandler}
className="modalCancel pe-btn--btn_large">{text.modalCancelButtonText}</button>
<button onClick={this.successBtnHandler} className="modalSave pe-btn__primary--btn_large"
disabled={disableSuccessBtn}>{text.modalSaveButtonText}</button>
</div>
)
}
};

render() {

const { isShown, footerVisible, text, children, disableSuccessBtn,
shouldCloseOnOverlayClick, hideCloseButton, srHeaderText, headerClass,
scrollWithPage } = this.props;
Expand Down Expand Up @@ -116,137 +240,5 @@ Modal.defaultProps = {
shouldCloseOnOverlayClick: true,
headerClass: '',
scrollWithPage: false
}

function _handleKeyDown(event) {

if (!event.shiftKey && event.which === 9 && !this.state.tab) {
this.state.tab = true;
return;
}

if (event.shiftKey && event.which === 9 && !this.state.shiftTab && !this.state.tab) {
event.preventDefault();
this.state.shiftTab = true;
const tabbableConfig = { context: '.modalContent' };
const tabbableElements = ally.query.tabbable(tabbableConfig);
tabbableElements[tabbableElements.length-1].focus();
}

}

export function _onClose() {
this.cancelBtnHandler();
this.state.shiftTab = false;
this.state.tab = false;
}

export function _successBtnHandler() {
this.removeOverlayStyle();
this.removeWrapper();
this.props.successBtnHandler.call(this);
}

export function _cancelBtnHandler() {
this.removeOverlayStyle();
this.removeWrapper();
this.props.cancelBtnHandler.call(this);
}

export function _removeOverlayStyle() {
const modalBody = document.getElementsByClassName('modalBody')[0];
const modalOverlay = document.getElementsByClassName('modalOverlay')[0];

modalBody.style.maxHeight = '';
modalOverlay.style.paddingTop = '';
modalOverlay.style.paddingBottom = '';
}

export function _afterOpen() {

const headerCloseButton = document.getElementsByClassName('modalClose')[0];
const modalBody = document.getElementsByClassName('modalBody')[0];
const modalContent = document.getElementsByClassName('modalContent')[0];
const modalOverlay = document.getElementsByClassName('modalOverlay')[0];
const header = document.getElementsByClassName('modalHeader')[0];
const footer = document.getElementsByClassName('modalFooter')[0];

// apply accessibility wrapper if no appElement is given
if (!this.props.appElement) {
this.applyWrapper();
}

// apply Focus to close button on open...
modalContent.focus();
modalContent.addEventListener('keydown', this.handleKeyDown);

// apply padding based on clientHeight...
const windowHeight = window.innerHeight;
const contentHeight = modalContent.offsetHeight;
const paddingHeight = (windowHeight - contentHeight) / 2;
const padding = paddingHeight > 60 ? paddingHeight : 60;
const headerHeight = header.getBoundingClientRect().height;
const footerHeight = footer ? footer.getBoundingClientRect().height : 0;

// modalBody.style.maxHeight = !headerCloseButton ? `${windowHeight - (headerHeight + footerHeight + 120)}px` : '';
// modalOverlay.style.overflow = !headerCloseButton ? '' :'scroll';
modalBody.style.maxHeight = this.props.scrollWithPage ? 'none' : `${windowHeight - (headerHeight + footerHeight + 120)}px`;
modalOverlay.style.paddingTop = `${padding}px`;
modalOverlay.style.paddingBottom = `${padding}px`;

// conditional borders on modalbody if scrollbar is present...
modalBody.className = (modalBody.offsetHeight < modalBody.scrollHeight && !headerCloseButton) ? 'modalBody modalBody_border' : 'modalBody modalBody_border_normal';

window.onresize = () => {
modalBody.className = (modalBody.offsetHeight < modalBody.scrollHeight && !headerCloseButton) ? 'modalBody modalBody_border' : 'modalBody modalBody_border_normal';
}

};

export function _applyWrapper() {

if (!document.getElementById('wrapper')) {

const wrapper = document.createElement('div');
wrapper.id = 'wrapper';
wrapper.setAttribute('aria-hidden', true);

const excludedElement = document.getElementsByClassName('modalOverlay')[0].parentElement;

while (document.body.firstChild) {
wrapper.appendChild(document.body.firstChild);
}

document.body.appendChild(wrapper);
document.body.appendChild(excludedElement);
}

};

export function _removeWrapper() {
const wrapper = document.getElementById('wrapper');
if (!wrapper) { return; }

wrapper.setAttribute('aria-hidden', false);

const excludedElement = document.getElementsByClassName('modalOverlay')[0].parentElement;

while (wrapper.firstChild) {
document.body.appendChild(wrapper.firstChild);
}

document.body.removeChild(wrapper);
document.body.appendChild(excludedElement);
};

export function _renderFooter(footerVisible, text, disableSuccessBtn) {
if (footerVisible) {
return(
<div className="modalFooter" >
<button onClick={this.cancelBtnHandler} className="modalCancel pe-btn--btn_large">{text.modalCancelButtonText}</button>
<button onClick={this.successBtnHandler} className="modalSave pe-btn__primary--btn_large" disabled={disableSuccessBtn}>{text.modalSaveButtonText}</button>
</div>
)
};

};