Building Components in React: Modals
Tags: javascript, react, beginners, webdev
Published: 2021-05-15 08:41:25

 
A modal is a small UI element that will appear in the foreground of a website, usually triggered as a prompt for the user to do something

Let's build an ideal modal component in react from basics to advance

___

![Enough talk](https://i.giphy.com/media/3o6nUXmbq14JmHAnn2/giphy.gif)
___

## Table Of Contents

  * [Creating a basic modal](#chapter-1)
  * [Styling modal](#chapter-2)
  * [Closing the modal](#chapter-3)
  * [Hardware backbutton to close the modal](#chapter-4)
  * [Making Modals More Usable And Accessible](#chapter-5)



### 1. Creating a basic modal <a name="chapter-1"></a>

A basic modal involves creating an overlay and inside the overlay, we render the modal component which will include the children passed by the consumer.

```javascript
const Modal = ({
  position, // set the position of modal on viewport
  isFullScreen,
  modalStyle,
  containerStyle,
  height,
  children,
}) => {
  return (
    <ModalOverlay style={containerStyle}>
      <ModalComponent
        position={position}
        isFullScreen={isFullScreen}
        customHeight={height}
        style={modalStyle}
      >
        {children}
      </ModalComponent>
    </ModalOverlay>
  );
};

Modal.defaultProps = {
  position: "center",
  isFullScreen: false,
  height: "auto",
  modalStyle: {},
  containerStyle: {},
};

Modal.propTypes = {
  position: PropTypes.oneOf(["center", "top", "bottom"]),
  isFullScreen: PropTypes.bool,
  height: PropTypes.string,
  modalStyle: PropTypes.shape({}),
  containerStyle: PropTypes.shape({}),
  children: PropTypes.node.isRequired,
};

```

### 2. Styling modal <a name="chapter-2"></a>

For styling I have used [styled-component](https://github.com/styled-components/styled-components)

Since we have props such as `position`, `height`, `isFullScreen` we need to have conditional styling.

```javascript
const ModalOverlay = styled.div`
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 100;
  opacity: 1;
  position: fixed;
  overflow-x: hidden;
  overflow-y: auto;
  background-color: rgba(34, 34, 34, 0.8);
`;

const ModalComponent = styled.div`
  position: ${(props) =>
    props.position !== "center" ? "absolute" : "relative"};
  top: ${(props) => (props.position === "top" ? "0" : "auto")};
  bottom: ${(props) => (props.position === "bottom" ? "0" : "auto")};
  height: ${(props) => (props.isFullScreen ? "100%" : props.customHeight)};
  max-height: ${(props) => (props.isFullScreen ? "100%" : props.customHeight)};
  width: 100%;
`;
```

### 3. Closing the modal <a name="chapter-3"></a>

There are three ways to close a modal 

* Pressing ESC key
* Clicking outside of the modal body
* Clicking on close icon or button on the modal body which closes the modal

```javascript
const Modal = ({ close, children }) => {
  const modalRef = useRef();
  const modalOverlayRef = useRef();
  
const handleClose = () => {
    close();
  };
  
const handleClick = (event) => {
    if (modalRef.current && !modalRef.current.contains(event.target)) {
      handleClose();
    }
  };
  
const handleKeyDown = (event) => {
    if (event.keyCode === 13) {
      return handleClose();
    }
  };
  
useEffect(() => {
    const modalOverlayRefCurrent = modalOverlayRef.current;
    modalOverlayRefCurrent.addEventListener("click", handleClick);
    document.addEventListener("keydown", handleKeyDown);
    return () => {
      document.removeEventListener("keydown", handleKeyDown);
      modalOverlayRefCurrent.removeEventListener("click", handleClick);
    };
  }, []);
  
return (
    <ModalOverlay ref={modalOverlayRef}>
      <ModalComponent ref={modalRef}>{children}</ModalComponent>
    </ModalOverlay>
  );
};

```

### 4. Hardware backbutton to close the modal <a name="chapter-4"></a>

One of the most searched questions on modals is how to close the modal by clicking the hardware back button on a mobile device.

One solution which I found to be working well is to leverage `react-router` in order to achieve it

We usually use state variable to show/hide the modal something like this

```javascript
const [isModalVisible, setIsModalVisible] = useState(false)

const handleShowModal = () => {
  setIsModalVisible(true)
}

return isModalVisible ? <Modal/> : null
```

We need to change the way we show/hide the modal component, instead of changing the state variable we will push a `new route with a state variable` like this

```javascript
import { useHistory } from 'react-router-dom'

const historyHook = useHistory()

const handleShowModal = () => {
  history.push(window.location.pathname, { isModalVisible: true })
}

return historyHook.location.state.isModalVisible ? <Modal /> : null
```

Now when the user clicks on to show the modal a `new route is pushed` with the same pathname but with a state variable named `isModalVisible`

Then, when a user clicks on the back button it will remove the route from the history stack thus closing the modal or we can simply call the below function

```javascript
window.history.back() // closes the modal
```

### 5. Making Modals More Usable And Accessible <a name="chapter-5"></a>

Basic accessibility is a prerequisite for usability.

An accessible modal dialog is one where keyboard focus is managed properly, and the correct information is exposed to screen readers. 

`HTML` and `WAI-ARIA`((Web Accessibility Initiative - Accessible Rich Internet Applications)) can be used to provide the necessary semantic information, CSS the appearance, and Javascript the behavior.

Three basics point to achieve accessibility in modal are:

#### -> Basic semantics has to be followed

The modal itself must be constructed from a combination of HTML and WAI-ARIA attributes, as in this example:

```html
<div id="dialog" role="dialog" aria-labelledby="title" aria-describedby="description">
  <h1 id="title">Title</h1>
  <p id="description">Information</p>
  <button id="close" aria-label="close">×</button>
</div>
```

Note the dialog role, which tells assistive technologies that the element is a dialog. 

The `aria-labelledby` and `aria-describedby` attributes are relationship attributes that connect the dialog to its title and description explicitly. 

So when the focus is moved to the dialog or inside it, the text within those two elements will be read in succession.

#### -> Saving last active element

When a modal window loads, the element that the user last interacted with should be saved. 

That way, when the modal window closes and the user returns to where they were, the focus on that element will have been maintained.

```javascript
let lastFocus;

function handleShowModal () {
  lastFocus = document.activeElement; // save activeElement
}

function handleCloseModal () {
  lastFocus.focus(); // place focus on the saved element
}
```

#### -> Shifting focus from main content to modal

When the modal loads, the focus should shift from the last active element either to the modal window itself or to the first interactive element in the modal, such as an input element.

```javascript
const modal = document.getElementById('modal-id');

function modalShow () {
   modal.setAttribute('tabindex', '0');
   modal.focus();
}
```

## Conclusion

Component creation often involves multiple points to be kept in mind, right from creating a basic structure to solving common and complex problems such as accessibility and usability.

The article covers most parts of a modal and its uses and can easily be integrated into a live project.