<div id="app"></div>
body {
  margin: 0;
}

#modal {
  margin: 0;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 50%;
  height: 50%;
  background-color: white;
  border: 2px solid black;
  padding: 7px;
  minheight: 100px;
}

@media only screen and (max-width: 768px) {
  #modal {
    width: 100%;
    height: 100%;
  }
}

.centered {
  margin: 0;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
  display: flex;
  flex-direction: column;
}

.gray {
  background-color: gray;
}
/*
 * https://frontendeval.com/questions/modal-overlay
 *
 * Build a modal control and overlay
 */
const { useState, useRef, useEffect } = React;

const App = () => {
  const offerNumberStrings = ["one", "two"];
  const defaultAcceptedOffers = { one: false, two: false };

  const [modalVisible, setModalVisible] = useState(false);
  const [modalOfferNumber, setModalOfferNumber] = useState("one");
  const [acceptedOffers, setAcceptedOffers] = useState(defaultAcceptedOffers);
  const modalRef = useRef();

  useEffect(() => {
    const outsideModalClickHandler = (event) => {
      console.log("triggered click handler");
      if (!modalRef.current?.contains(event.target)) {
        setModalVisible(false);
      }
    };
    const escapeModalKeypressHandler = (event) => {
      console.log("triggered escape handler");
      if (event.keyCode === 27) {
        setModalVisible(false);
      }
    };
    document
      .getElementById("container")
      .addEventListener("click", outsideModalClickHandler);
    document.addEventListener("keydown", escapeModalKeypressHandler);
    return () => {
      document
        .getElementById("container")
        .removeEventListener("click", outsideModalClickHandler);
      document.removeEventListener("keydown", escapeModalKeypressHandler);
    };
  }, [modalRef]);

  const handleClickShow = (e) => {
    const offerNumber = e.target.innerHTML.split(" ")[2];
    setModalOfferNumber(offerNumber);
    setModalVisible(true);
    event.stopPropagation();
  };

  const handleClickX = () => {
    setModalVisible(false);
  };

  const handleClickAccept = (e) => {
    const offerNumber = e.target.innerHTML.slice(-3);
    const updatedAcceptedOffers = {
      ...acceptedOffers,
      [modalOfferNumber]: true
    };
    console.log(updatedAcceptedOffers);
    setAcceptedOffers(updatedAcceptedOffers);
    setModalVisible(false);
  };

  const ShowOfferButton = ({ offerNumberString, offerAccepted }) => {
    return offerAccepted ? (
      <p style={{ marginBlock: "0px", marginBottom: "18px" }}>
        Offer {offerNumberString} accepted
      </p>
    ) : (
      <button onClick={handleClickShow}>Show offer {offerNumberString}</button>
    );
  };

  const Modal = ({ modalOfferNumber }) => {
    return (
      <div id="modal" ref={modalRef}>
        <div
          style={{
            height: "100%",
            display: "flex",
            flexDirection: "column",
            justifyContent: "space-between"
          }}
        >
          <button style={{ width: "fit-content" }} onClick={handleClickX}>
            x
          </button>
          <p
            style={{
              textAlign: "center"
            }}
          >
            Click the button below to accept our amazing offer!
          </p>
          <button
            style={{
              width: "fit-content",
              margin: "10px",
              alignSelf: "center"
            }}
            onClick={handleClickAccept}
          >
            Accept offer {modalOfferNumber}
          </button>
        </div>
      </div>
    );
  };

  return (
    <div
      id="container"
      class={modalVisible && "gray"}
      style={{ height: "100vh", width: "100%" }}
    >
      {modalVisible ? (
        <Modal modalOfferNumber={modalOfferNumber} />
      ) : (
        <div class="centered">
          {offerNumberStrings.map((offerNumberString) => {
            return (
              <ShowOfferButton
                offerNumberString={offerNumberString}
                offerAccepted={acceptedOffers[offerNumberString]}
              />
            );
          })}
        </div>
      )}
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("app"));
View Compiled
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js