Skip to content

Commit

Permalink
fix(layering): fixes z-index in popperWrapper and modal
Browse files Browse the repository at this point in the history
Closes #241
  • Loading branch information
riyalohia committed Aug 18, 2020
1 parent 95509a7 commit c73e71b
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 67 deletions.
175 changes: 116 additions & 59 deletions core/components/molecules/modal/Modal.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import classNames from 'classnames';
const { useEffect, useState } = React;
import Backdrop from '@/components/atoms/backdrop';
import OutsideClick from '@/components/atoms/outsideClick';
import { BaseProps, extractBaseProps } from '@/utils/types';
Expand Down Expand Up @@ -36,67 +35,125 @@ export interface ModalProps extends BaseProps {
children?: React.ReactNode;
}

const Modal = (props: ModalProps) => {
const {
dimension = 'small',
children,
backdropClose,
backdrop,
className
} = props;
const [open, setOpen] = useState<boolean>(props.open);
const [animate, setAnimate] = useState<boolean>(false);

const classes = classNames({
Modal: true,
[`Modal--${dimension}`]: dimension,
'Modal--open': open,
'Modal-animation--open': animate,
'Modal-animation--close': !animate,
}, className);

const baseProps = extractBaseProps(props);

useEffect(() => {
if (props.open) {
setOpen(true);
setAnimate(true);
interface ModalState {
open: boolean;
animate: boolean;
zIndex?: number;
}

class Modal extends React.Component<ModalProps, ModalState> {
modalRef = React.createRef<HTMLDivElement>();
element: Element;

constructor(props: ModalProps) {
super(props);

let element = document.querySelector('.Modal-wrapper');
if (element === null) {
element = document.createElement('div');
element.classList.add('Modal-wrapper');
document.body.appendChild(element);
}
if (!props.open) {
setTimeout(() => {
setOpen(false);
}, 120);
setAnimate(false);

this.element = element;

this.state = {
open: props.open,
animate: props.open,
};
}

componentDidUpdate(prevProps: ModalProps) {
if (prevProps.open !== this.props.open) {
if (this.props.open) {
const zIndex = this.getUpdatedZIndex();
this.setState({
zIndex,
open: true,
animate: true
});
} else {
this.setState({
animate: false,
});
setTimeout(() => {
this.setState({
open: false
});
}, 120);
}
}
}, [props.open]);
}

getUpdatedZIndex = () => {
if (this.element === null) return;

const elements = this.element.querySelectorAll('.Modal-container');
if (elements.length <= 1) return;

const siblings = Array.from(elements).filter(el => el !== this.modalRef.current);
let zIndex = -1;

const ModalContainer = (
<div className="Modal-container">
<div {...baseProps} className={classes}>
{children}
siblings.forEach(element => {
if (element.classList.contains('Modal-container--open')) {
const prevZIndex = parseInt(window.getComputedStyle(element).zIndex || '0', 10);
zIndex = Math.max(zIndex, prevZIndex + 10);
}
});

return zIndex > 0 ? zIndex : undefined;
}

render() {
const { animate, open, zIndex } = this.state;
const { className, dimension, backdropClose } = this.props;

const classes = classNames({
Modal: true,
[`Modal--${dimension}`]: dimension,
'Modal--open': open,
'Modal-animation--open': animate,
'Modal-animation--close': !animate,
}, className);

const ContainerClass = classNames({
['Modal-container']: true,
['Modal-container--open']: open,
});

const baseProps = extractBaseProps(this.props);

const ModalContainer = (
<div
className={ContainerClass}
data-layer={true}
style={{ zIndex }}
ref={this.modalRef}
>
<div {...baseProps} className={classes}>
{this.props.children}
</div>
</div>
</div>
);

const ModalWrapper = backdrop ? (
<OutsideClick onOutsideClick={(event: Event) => open && backdropClose(event, 'OutsideClick')}>
{ModalContainer}
</OutsideClick>
) : ModalContainer;

const WrapperElement = ReactDOM.createPortal(
ModalWrapper,
document.body
);

return (
<div>
{WrapperElement}
<Backdrop open={open} />
</div>
);
};

Modal.displayName = 'Modal';
);

const ModalWrapper = this.props.backdrop ? (
<OutsideClick onOutsideClick={(event: Event) => open && backdropClose(event, 'OutsideClick')}>
{ModalContainer}
</OutsideClick>
) : ModalContainer;

const WrapperElement = ReactDOM.createPortal(
ModalWrapper,
this.element
);

return (
<div>
{WrapperElement}
<Backdrop open={this.state.open} />
</div>
);
}
}

export default Modal;
2 changes: 1 addition & 1 deletion core/components/molecules/popover/Popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export const Popover = (props: PopoverProps) => {
}, className);

const PopoverWrapper = (
<div className={classes} >
<div className={classes} data-layer={true}>
{children}
</div>
);
Expand Down
29 changes: 26 additions & 3 deletions core/utils/PopperWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ interface Props {

interface IState {
open: boolean;
zIndex?: number;
mouseLeaveDelay: number;
mouseEnterDelay: number;
}
Expand Down Expand Up @@ -134,17 +135,39 @@ class PopperWrapper extends React.Component<Props, IState> {
}
}

public componentDidUpdate() {
public componentDidUpdate(prevProps: Props) {
const { on = 'click', closeOnBackdropClick = true } = this.props;
const { open } = this.props;

if (prevProps.open !== this.props.open && this.props.open) {
const triggerElement = this.findDOMNode(this.triggerRef);
const zIndex = this.getZIndexForLayer(triggerElement);

this.setState({
zIndex: zIndex === undefined ? zIndex : zIndex + 1
});
}

if (on === 'click' && open && closeOnBackdropClick) {
document.addEventListener('mousedown', this.doesNodeContainClick);
} else if (on === 'click' && !open && closeOnBackdropClick) {
document.removeEventListener('mousedown', this.doesNodeContainClick);
}
}

public getZIndexForLayer(node: HTMLElement | null) {
if (node === null) {
return;
}

const layerNode = node.closest('[data-layer]') || document.body;
const zIndex =
layerNode === document.body
? 'auto'
: parseInt(window.getComputedStyle(layerNode).zIndex || '0', 10);
return zIndex === 'auto' || isNaN(zIndex) ? undefined : zIndex;
}

public getTriggerElement(trigger: React.ReactElement<any>, ref: React.Ref<any>, on: actionType) {
const options = on === 'hover'
? {
Expand Down Expand Up @@ -219,7 +242,7 @@ class PopperWrapper extends React.Component<Props, IState> {
>
{({ ref, style, placement }) => {
const newStyle = offset ? this.getUpdatedStyle(style, placement, offset) : style;
return this.getChildrenElement(children, ref, placement, newStyle);
return this.getChildrenElement(children, ref, placement, { ...newStyle, zIndex: this.state.zIndex });
}}
</Popper>
),
Expand All @@ -229,7 +252,7 @@ class PopperWrapper extends React.Component<Props, IState> {
<Popper placement={placement} innerRef={this.popupRef}>
{({ ref, style, placement }) => {
const newStyle = offset ? this.getUpdatedStyle(style, placement, offset) : style;
return this.getChildrenElement(children, ref, placement, newStyle);
return this.getChildrenElement(children, ref, placement, { ...newStyle, zIndex: this.state.zIndex });
}}
</Popper>
)}
Expand Down
2 changes: 1 addition & 1 deletion css/src/components/backdrop.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
top: 0;
display: none;
align-items: center;
z-index: 999999;
z-index: 1000;
overflow: auto;
flex-direction: column;
justify-content: center;
Expand Down
2 changes: 1 addition & 1 deletion css/src/components/modal.css
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
flex-direction: row;
justify-content: center;
width: 100vw;
z-index: 99999999;
z-index: 1500;
top: 0;
}

Expand Down
2 changes: 1 addition & 1 deletion css/src/components/popover.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.Popover {
border-radius: var(--spacing-m);
position: absolute;
z-index: 999999;
z-index: 500;
background: var(--white);
box-shadow: var(--shadow-l);
}
Expand Down
2 changes: 1 addition & 1 deletion css/src/components/tooltip.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
padding: var(--spacing) var(--spacing-2);
border-radius: var(--spacing-m);
position: absolute;
z-index: 999999;
z-index: 500;
background: var(--inverse);
overflow: hidden;
box-sizing: border-box;
Expand Down

0 comments on commit c73e71b

Please sign in to comment.