Zanshin
Zanshin

Reputation: 55

Multiple hover popovers from an array using Material-UI opens only one popover

I'm trying to use multiple popovers on one page, but the only popover to open is the last one in the array, regardless of which trigger element you hover on. This is using Material-UI v1.0.0-beta.46.

class MultiplePopover extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.state = {
      open: false,
      anchorEl: null,
    };
    this.handlePopoverOpen = this.handlePopoverOpen.bind(this);
    this.handlePopoverClose = this.handlePopoverClose.bind(this);
  }
  handlePopoverOpen(event) {
    this.setState({
      anchorEl: event.target,
    });
  }
  handlePopoverClose() {
    this.setState({
      anchorEl: null,
    });
  }

  render() {
    const { classes } = this.props;
    const { anchorEl } = this.state;
    const open = !!anchorEl;


    const multi = [
      {
        _id: 0,
        name: 'name1',
        hoverText: 'text1',
        linkUrl: '#',
      },
      {
        _id: 1,
        name: 'name2',
        hoverText: 'text2',
        linkUrl: '#',
      },
      {
        _id: 2,
        name: 'name3',
        hoverText: 'text3',
        linkUrl: '#',
      },
    ]

    return (
      <div className="wrapper">
        <ul>
          {multi.map(m => (
            <li
              key={m._id}
            >
              <Typography
                onMouseEnter={this.handlePopoverOpen}
                onMouseLeave={this.handlePopoverClose}
              >
                {m.name} 
              </Typography>
              <Popover
                className={classes.popover}
                classes={{
                  paper: classes.paper,
                }}
                open={open}
                anchorEl={anchorEl}
                anchorOrigin={{
                  vertical: 'bottom',
                  horizontal: 'center',
                }}
                transformOrigin={{
                  vertical: 'bottom',
                  horizontal: 'center',
                }}
              >
                <Typography>
                  <a
                    href="{m.linkUrl}"
                    target=" /blank"
                  >
                    {m.hoverText}
                  </a>
                </Typography>
              </Popover>
            </li>
          ))}
        </ul>
      </div>
    );
  }
}

I have tried to follow the answer from this post Popover doesn't work if you have many of them on one page. How to manage them? but I couldn't get it working.

Any idea how I could get each popover to open separately?

You can see a live example here: https://codesandbox.io/s/1r1zjmj163

Upvotes: 4

Views: 10914

Answers (2)

dawg8it
dawg8it

Reputation: 41

@asiniy Why does commenting out the css 'pointerEvents' cause the popovers code to break? If there are links inside the popover this will cause problems. Should css control the events??

  popover: {
    //pointerEvents: 'none',
  },

Upvotes: 1

Alex Antonov
Alex Antonov

Reputation: 15216

You're doing it a little bit wrong.

First. open declares that only some element exists in state but it doesn't declares which one is opened. I've brought new openedPopoverId to the state (null by default). So, in this case, you have to check in your Popover component

open={this.state.openedPopoverId === m._id}

Second. You have to pass this value on mouseEnter event, so that:

<Typography
  onMouseEnter={this.handlePopoverOpen}

becomes

onMouseEnter={(e) => this.handlePopoverOpen(e, m._id)}

Third. Change event handlers as well:

  handlePopoverOpen(event, popoverId) {
    this.setState({
      openedPopoverId: popoverId,
      anchorEl: event.target,
    });
  }
  handlePopoverClose() {
    this.setState({
      openedPopoverId: null,
      anchorEl: null,
    });
  }

Final code (tested, it works):

import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from 'material-ui/styles';
import Typography from 'material-ui/Typography';
import Popover from 'material-ui/Popover';

const styles = ({
  paper: {
    padding: '20px',
    width: '14vw',
  },
  popover: {
    pointerEvents: 'none',
  },
});

class MultiplePopover extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.state = {
      open: false,
      anchorEl: null,
    };
    this.handlePopoverOpen = this.handlePopoverOpen.bind(this);
    this.handlePopoverClose = this.handlePopoverClose.bind(this);
  }
  handlePopoverOpen(event, popoverId) {
    this.setState({
      openedPopoverId: popoverId,
      anchorEl: event.target,
    });
  }
  handlePopoverClose() {
    this.setState({
      openedPopoverId: null,
      anchorEl: null,
    });
  }

  render() {
    const { classes } = this.props;
    const { anchorEl, openedPopoverId } = this.state;

    const multi = [
      {
        _id: 0,
        name: 'name1',
        hoverText: 'text1',
        linkUrl: '#',
      },
      {
        _id: 1,
        name: 'name2',
        hoverText: 'text2',
        linkUrl: '#',
      },
      {
        _id: 2,
        name: 'name3',
        hoverText: 'text3',
        linkUrl: '#',
      },
    ]

  console.log(openedPopoverId)

    return (
      <div className="wrapper">
        <ul>
          {multi.map(m => (
            <li
              key={m._id}
            >
              <Typography
                onMouseEnter={(e) => this.handlePopoverOpen(e, m._id)}
                onMouseLeave={this.handlePopoverClose}
              >
                {m.name} 
              </Typography>
              <Popover
                className={classes.popover}
                classes={{
                  paper: classes.paper,
                }}
                open={openedPopoverId === m._id}
                anchorEl={anchorEl}
                anchorOrigin={{
                  vertical: 'bottom',
                  horizontal: 'center',
                }}
                transformOrigin={{
                  vertical: 'bottom',
                  horizontal: 'center',
                }}
              >
                <Typography>
                  <a
                    href="{m.linkUrl}"
                    target=" /blank"
                  >
                    {m.hoverText}
                  </a>
                </Typography>
              </Popover>
            </li>
          ))}
        </ul>
      </div>
    );
  }
}

MultiplePopover.propTypes = {
  classes: PropTypes.object.isRequired,
};

export default withStyles(styles)(MultiplePopover);

Upvotes: 14

Related Questions