React Component - Modal

React Component - Modal

2020, Jan 15    

Throughout the course of my career in Web Development there has always been a need for modal dialog boxes, but also more importantly the need to support accessibility. While working on a current project, the team decided to use a 3rd party modal component to keep things moving quickly. Like with all easy things through, we ran into problems later. We discovered that the version of the component we were using did NOT support accessibility. This was something that had been corrected since we initially added the component to the project, but again, like all easy things, you pay the price later. There were breaking changes in updating the component. With all that, I held true to my philosophy of “Sometimes it is just better to write it yourself”.

So here we go…

This component renders a Modal component, controllable through events. To use the component, import Modal only once and then display it by passing a boolean value to the isVisible attribute.

Create the component

// modal.js

const Modal = ({ isVisible = false, title, children, footer, onClose }) => {

  React.useEffect(() => {
    document.addEventListener('keydown', keydownHandler);
    return () => document.removeEventListener('keydown', keydownHandler);
  }, []);

  function keydownHandler({ key }) {
    switch (key) {
      case 'Escape':
        onClose();
        break;
      default:
    }
  }

  return !isVisible ? null : (
    <div className="modal" onClick={onClose}>
      <div className="modal-dialog" onClick={e => e.stopPropagation()}>
        <div className="modal-header">
          <div className="modal-title">{title}</div>
          <span className="modal-close" onClick={onClose}>&times;</span>
        </div>
        <div className="modal-body">
          <div className="modal-content">{children}</div>
        </div>
        {footer && <div className="modal-footer">{footer}</div>}
      </div>
    </div>
  );
}

We use useEffect to setup an event listener on the document to listen for the esc key to be pressed. This is acting much like componentDidMount on a class-based component. Since we do not want this event listener lingering after we are done using the component, return a function that removes the event listen which will called when the component is destroyed.

Style the modal

// modal.css
.modal {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  width: 100%;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: rgba(0, 0, 0, 0.25);
  animation-name: appear;
  animation-duration: 300ms;
}

.modal-dialog {
  border-radius: 5px 5px;
  width: 100%;
  max-width: 550px;
  background: white;
  position: relative;
  margin: 0 20px;
  max-height: calc(100vh - 40px);
  text-align: left;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
  -webkit-animation-name: animatetop;
  -webkit-animation-duration: 0.4s;
  animation-name: slide-in;
  animation-duration: 0.5s;
}

.modal-header,
.modal-footer {
  display: flex;
  align-items: center;
  padding: 1rem;
}
.modal-header {
  border-bottom: 1px solid #dbdbdb;
  justify-content: space-between;
}
.modal-footer {
  border-top: 1px solid #dbdbdb;
  justify-content: flex-end;
}
.modal-close {
  cursor: pointer;
  padding: 1rem;
  margin: -1rem -1rem -1rem auto;
}
.modal-body {
  overflow: auto;
}
.modal-content {
  padding: 1rem;
}

@keyframes appear {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
@keyframes slide-in {
  from {
    transform: translateY(-150px);
  }
  to {
    transform: translateY(0);
  }
}

.btn {
	box-shadow:inset 0px -3px 7px 0px #29bbff;
	background-color:#2dabf9;
	border-radius:3px;
	border:1px solid #0b0e07;
	display:inline-block;
	cursor:pointer;
	color:#ffffff;
	font-family:Arial;
	font-size:15px;
	padding:6px 20px;
	text-decoration:none;
	text-shadow:0px 1px 0px #263666;
}
.btn:hover {
	background-color:#0688fa;
}
.btn:active {
	position:relative;
	top:1px;
}

       

See it in action

See the Pen React Component: Modal by Jerhemy Waldon (@jswaldon) on CodePen.