Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 351
Zanshin Avatar asked Oct 17 '25 04:10

Zanshin


1 Answers

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);
like image 198
asiniy Avatar answered Oct 20 '25 21:10

asiniy