Ven Nilson
Ven Nilson

Reputation: 1019

The Dialog component is not closing correctly

I have a problem with the React.js Dialog component which contains a list of items. There is also an overlay in the Dialog. It closes automatically when the user clicks on it. But the scenario is something different. The dialog also includes a list of items that have some external links wrapped around the <a> element and some with the <div>.

But the method of closing the dialog is not working properly. The Dialog automatically closes when I click inside any list item.

Please see this:

Modal Dialog

My goal was to close the dialog whenever the user clicked on the overlay or whenever the user clicked on the list item wrapped with the <a> element. Otherwise, it should not be closed.

I still can't figure out What is the correct approach to tackle this issue?

CodeSandbox link

App.js

import React, { useState } from 'react';
import Dialog from './Dialog';
import './App.css';

const App = () => {
  const [isOpen, setIsOpen] = useState(false);

  const onClickHandler = (isFalse) => {
    if (!isFalse) {
      setIsOpen(false);
    } else {
      setIsOpen(!isOpen);
    }
  };

  return (
    <div>
      <button type="button" onClick={onClickHandler}>
        Open Dialog
      </button>
      <Dialog isOpen={isOpen} onClick={onClickHandler} />
    </div>
  );
};

export default App;

Dialog.js

import React from 'react';

const Dialog = (props) => {
  const { isOpen, onClick } = props;
  const onCloseHandle = () => {
    onClick(false);
  };

  return (
    <div
      className={isOpen ? 'dialog-wrap open' : 'dialog-wrap'}
      onClick={onCloseHandle}
    >
      <div className="dialog">
        <ul className="dialog-list">
          <li>
            <a
              href="https://reactjs.org"
              target="_blank"
              rel="noopener noreferrer"
            >
              React.js
            </a>
          </li>

          <li>
            <a
              href="https://reactjs.org"
              target="_blank"
              rel="noopener noreferrer"
            >
              Another Link
            </a>
          </li>

          <li>
            <div>Should Not Close onClick</div>
          </li>

          <li>
            <div>Should Not Close onClick</div>
          </li>
        </ul>
      </div>
    </div>
  );
};

export default Dialog;

App.css

*,:before,:after {
  box-sizing: border-box;
}

.dialog-wrap.open {
  display: flex;
}

.dialog-wrap {
  display: none;
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 999;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 16px;
  background-color: rgba(0,0,0,0.66);
}

@media (min-width: 640px) {
  .dialog-wrap {
    padding: 48px 32px;
  }
}

@media (min-width: 1024px) {
  .dialog-wrap {
    padding-left: 40px;
    padding-right: 40px;
  }
}

.dialog {
  position: relative;
  margin: auto;
  background-color: #fff;
  border: 1px solid rgba(0, 0, 0, 0.1);
  box-shadow: 0 12px 16px rgba(0, 0, 0, 0.08);
  border-radius: 4px;
  width: 500px;
}

.dialog-list {
  margin-bottom: 0;
  padding-left: 0;
  color: #333;
  list-style: none;
}

.dialog-list > li {
  border-bottom: 1px solid #eee;
  padding: 24px;
}

.dialog-list > li > a {
  color: #333;
  display: block;
  text-decoration: none;
}

.dialog-list > li > div {
  cursor: pointer;
}

Upvotes: 0

Views: 553

Answers (2)

user7663064
user7663064

Reputation:

in this situation, I would create a .dialog-background element and give onCloseHandle to that. I also hive my a elements the onCloseHandle as onClick prop as well

If you do not want to change your react tree you can decide that from the event like this:

<div
  className={isOpen ? "dialog-wrap open" : "dialog-wrap"}
  onClick={(e) => {
    if (!e.target.closest('.dialog') || e.target.closest('a')) {
      onClick(false)
    }
  }}
/>

Upvotes: 1

DBS
DBS

Reputation: 9959

I think your click event is propagating up the elements, and triggering onCloseHandle on your .dialog-wrap.

The simplest solution to this would be to use Event.stopPropagation() on the <div className="dialog">

E.g.

<div className="dialog" onClick={ e => e.stopPropagation() }>

(React technically uses "synthetic events", but they still include a stopPropagation option as long as it's being interacted with inside React)

Upvotes: 1

Related Questions