React - modal from a link in a popover

Refresh

April 2019

Views

14 time

0

I would like to launch a modal from a link in a popover in my React component. The modal works if I add the onClick function to any text/element on the page but if I add it to any element inside the popover it doesn't trigger. I added a console log to check this and the onClick function is never called if it's inside the popover.

PopoverWithModal.jsx

import React from 'react'
import Portal from './Portal.jsx'
import { string } from 'prop-types'

class PopoverWithModal extends React.Component {
  constructor (props) {
    super(props)

    this.state = {
      showPortal: false
    }
    this.changePortal = this.changePortal.bind(this)
  }

  componentDidMount () {
    $('[data-toggle=popover]').popover({
        html : true,
        content: function() {
          var content = $(this).attr('data-popover-content');
          return $(content).children('.popover-body').html();
        },
        container: 'body',
        trigger: 'hover',
        delay: {hide: 800},
        backdrop: 'static'
    });
  }

  changePortal (e) {
    const { showPortal } = this.state
    console.log('SHOWPORTAL:', showPortal, e)

    this.setState({
      showPortal: true
    })
  }

  render () {
    const { id, buttonText, modalContent } = this.props
    const { showPortal } = this.state

    return (
      <React.Fragment>
        <button className='btn' tabIndex='0' data-toggle='popover' data-popover-content={`#${id}`} data-placement='bottom'>{buttonText}</button>
        <div id={id} className='hidden'>
            <div className='popover-body'>
              <p>{modalContent}</p>
              <span className='btn' onClick={this.changePortal} data-toggle='modal'>Read more</span>
              <Portal
                open={showPortal}
                buttonText={buttonText}
                onClose={() =>
                  this.setState({
                    showPortal: false
                  })
                }
                modalContent={modalContent}
              >
              </Portal>
            </div>
        </div>
      </React.Fragment>
    )
  }
}

PopoverWithModal.propTypes = {
  buttonText: string.isRequired,
  modalContent: string.isRequired
}

export default PopoverWithModal

Portal.jsx

import Modal from './Modal.jsx'
import React from 'react'
import ReactDOM from 'react-dom'

class Portal extends React.Component {
  constructor(props) {
    super(props)
    this.isMounted = false
    this.rootSelector = null
    this.container = null

    this.state = {
      open: false
    }

    this.hideModal = this.hideModal.bind(this)
  }

  componentDidMount() {
    this.isMounted = true
  }

  componentWillUnmount() {
    this.rootSelector.removeChild(this.container)
  }

  hideModal() {
    const { open } = this.state
    this.setState({
      open: !open
    })
  }

  render() {
    const { buttonText, modalContent, open } = this.props

    if (this._isMounted) {
      this.rootSelector = document.getElementById('modal-root')
      this.container = document.createElement('div')

      this.rootSelector.appendChild(this.container)
    }

    return this.isMounted && ReactDOM.createPortal(<Modal visible={open} closeCallBack={this.hideModal} heading={buttonText} content={modalContent} />, this.container)
  }
}

export default Portal

0 answers