Camilla
Camilla

Reputation: 63

Right way of implementing a modal in React

I would like to have a modal that will pop up when a table row is clicked. The modal is opening when I click a row in my table component. However I'm not getting the desired result with the css. I want it to overlap everything that is on the page when a row is clicked. Right now it's showing on top of the page and I cant see the content in it.

//Modal.js

import React from "react";
import Table from "react-bootstrap/Table";

 export default function Modal() {
   return (
     <div className="modalContainer">
     <Table responsive="true" size="sm" striped bordered hover>
      <thead>
        <tr>
         <th>Own Product</th>
         <th>Competitors Products</th>
        </tr>
      </thead>
     <p>Brand</p>
     <p>Category</p>
     <p>In Stock</p>
     <p>Name</p>
     <p>Price</p>
     <p>Product Code</p>
     <p>Product Link</p>
   </Table>
 </div>
 );
}


//Code from Table.js

render() {
  let { isLoaded, products } = this.state; //instead of typing 
  this.state all the time

  if (!isLoaded) {
    return <Loading />;
  } else {
    return (
      <div className="tableContainer">
      {this.props.rows}

      <Table responsive="true" size="sm" striped bordered hover>
        <thead>
          <tr>
            <th>Product ID</th>
            <th>Product Name</th>
            <th>Match ID</th>
            <th>Match Score</th>
            <th>Match Name</th>
            <th>Match Price</th>
            <th>Match State</th>
          </tr>
        </thead>

        <tbody>
          {products.map(product => (
            //use filter instead to show only the matched ones
            <tr key={product.id} onClick={() => this.toggleModal()}>
              <td>{product.id}</td>
              <td>{product.name}</td>
              <td>{product.matches[0].id}</td>
              <td>{Math.round(product.matches[0].score)}</td>
              <td>{product.matches[0].name}</td>
              <td>{product.matches[0].price}</td>
              <td>{product.matches[0].matchLabel}</td>
            </tr>
          ))}
          {this.state.modalOpen ? <Modal /> : null}
        </tbody>
      </Table>
    </div>
   );
  }
 }

 //CSS

.tableContainer {
 position: relative;
 width: 100%;
 height: 100%;
}

.modalContainer {
  margin: -30% auto;
  position: absolute;
  width: 100%;
  height: 100%;
  justify-content: center;
  border: 1px solid black;
  z-index: 1;
  left: 0;
  top: 0;
  overflow: auto;
  background-color: rgba(219, 239, 250);
}

Upvotes: 1

Views: 584

Answers (2)

ANIK ISLAM SHOJIB
ANIK ISLAM SHOJIB

Reputation: 3258

State for modal

state = {
      axiosStatus: {
                    status: '',
                    title: '',
                    details: '',
                },
                modal: false,
          }

modal handler

modalHandler = ()=> {
    this.setState({modal: !this.state.modal});
};

modal content hander

  axiosStatusHandler = (status, title, details)=>{
        let oldState = this.state.axiosStatus;
        oldState.status = status;
        oldState.title = title;
        oldState.details = details;
        this.setState({axiosStatus: oldState});
    };

Jsx for modal

  <Modal show={this.state.modal} modalClosed={this.modalHandler}>
                        <ModalContent
                            status = {this.state.axiosStatus.status}
                            title = {this.state.axiosStatus.title}
                            details = {this.state.axiosStatus.details}
                        />
                    </Modal>

Modal Component

import React from 'react';

import './Modal.css';
import Aux from '../../../hoc/Auxi';
import Backdrop from '../Backdrop/Backdrop';

const Modal = ( props ) => (
    <Aux>
        <Backdrop show={props.show} clicked={props.modalClosed} />
        <div
            className={"Modal"}
            style={{
                transform: props.show ? 'translateY(0)' : 'translateY(-100vh)',
                opacity: props.show ? '1' : '0'
            }}>
            {props.children}
        </div>
    </Aux>
);

export default Modal;

Backdrop Component

import React from 'react';

import './Backdrop.css';

const backdrop = (props) => (
    props.show ? <div className={"Backdrop"} onClick={props.clicked}></div> : null
);

export default backdrop;

Backdrop css

.Backdrop {
    width: 100%;
    height: 100%;
    position: fixed;
    z-index: 100;
    left: 0;
    top: 0;
    background-color: rgba(0, 0, 0, 0.5);
}

ModalContenet Component

import React from 'react';

const ModalContent = (props)=>{

    return (
        <div style={{textAlign: 'center'}}>
            {/* <h3 style={{color: '#FF0000'}}>Failed</h3>*/}
            <b><h2 style={{color: '#FF0000'}}>{props.title}</h2></b>
            <h2 style={{color: '#FF0000'}}>{props.details}</h2>
        </div>
    )


};

export default ModalContent;

Upvotes: 0

Will Jenkins
Will Jenkins

Reputation: 9907

The issue is that your tableContainer is position:relative, which re-sets the positioning context for its children. So, your <Modal> is absolutely positioned with respect to the tableContainer instead of the browser window.

You can either change your css to so your Modal is e.g. position:fixed or move your modal out of your tableContainer like this:

 return (
      <>
       {this.state.modalOpen ? <Modal /> : null}
       <div className="tableContainer">
          {this.props.rows}

          <Table responsive="true" size="sm" striped bordered hover>

           //....//

          </Table>
          </div>
      </>

Upvotes: 1

Related Questions