Search code examples
javascriptreactjspopover

Using a ternary operator in a map function is rendering both results


I'm creating a vertical timeline with cards alternating sides as you move along the timeline, I'm trying to include a Popover effect to show more info about a person/event that fills the opposite whitespace on the timeline.

I'm trying to achieve this by using a ternary operator (using modulus to alternate sides in order) in my map callback, but it's rendering/returning both possible Popover results, onClick leads to a Popover popping on both sides of the card.

render() {
    const cards = timelineObjects.map((card, i) => (
      <React.Fragment key={i}>

        {i % 2 === 0 ? (
          <VerticalTimelineElement
            className="vertical-timeline-element--work"
            key={i}
            iconStyle={{
              background: "rgb(40,49,72)",
              color: "#000"
            }}
            paddingTop="0em"

            //icon={<Print/>}
          >
            <div>
              <Card className="card">
                <CardActionArea>
                  <CardMedia
                    style={{ height: 0, paddingTop: "100%" }}
                    image={card.image}
                  />
                  <CardContent>
                    <Typography gutterBottom variant="h5" component="h2">
                      {card.title}
                    </Typography>
                    <Typography component="p">{card.subtitle}</Typography>
                  </CardContent>
                </CardActionArea>

                <Button
                  size="small"
                  color="primary"
                  component={Link}
                  //to={card.path}
                  onClick={this.handlePop}
                >
                  Learn More, index: {i}, RIGHT
                </Button>
                <Popover
                  open={this.state.popped}
                  anchorEl={this.state.anchorEl}
                  anchorOrigin={{
                    horizontal: "right",
                    vertical: "center "
                  }}
                  transformOrigin={{
                    horizontal: "right",
                    vertical: "bottom"
                  }}
                  onClose={this.handleRequestClose}
                >
                  Right popover text
                </Popover>
              </Card>
            </div>
          </VerticalTimelineElement>
        ) 
        : 

        (
          <VerticalTimelineElement
            className="vertical-timeline-element--work"
            key={i}
            iconStyle={{
              background: "rgb(40,49,72)",
              color: "#000"
            }}
            paddingTop="0em"

            //icon={<Print/>}
          >
            <div>
              <Card className="card">
                <CardActionArea>
                  <CardMedia
                    style={{ height: 0, paddingTop: "100%" }}
                    image={card.image}
                  />
                  <CardContent>
                    <Typography gutterBottom variant="h5" component="h2">
                      {card.title}
                    </Typography>
                    <Typography component="p">{card.subtitle}</Typography>
                  </CardContent>
                </CardActionArea>

                <Button
                  size="small"
                  color="primary"
                  component={Link}
                  //to={card.path}
                  onClick={this.handlePop}
                >
                  Learn More, index : {i}, LEFT
                </Button>
                <Popover
                  open={this.state.popped}
                  anchorEl={this.state.anchorEl}
                  anchorOrigin={{
                    horizontal: "left",
                    vertical: "center "
                  }}
                  transformOrigin={{
                    horizontal: "left",
                    vertical: "bottom"
                  }}
                  onClose={this.handleRequestClose}
                >
                  Left popover text
                </Popover>
              </Card>
            </div>
          </VerticalTimelineElement>
        )}
      </React.Fragment>
    ));

Here's a screen grab of the result.


Solution

  • Your popovers are all anchored to the same element (this.state.anchorEl) and are all configured to open based upon the same boolean (this.state.popped). This means if you have 2+ objects in your timeline, you render a popover for each object, and all popovers will be opened or closed together and all will be to the left/right of the only anchor element (whatever that is).

    You should probably create a new TimelineObject component that renders a single timeline object and can have its own local state and assigns its own local anchorEl to anchor its popover to. Possibly its own popped state as well. Then your map function would be more like:

    timelineObjects.map((card, i) => <TimelineObject key={i} card={card} onLeft={i%2==0} />)

    Alternatively, instead of using this.state.popped as a boolean, use it as the card index to show the popup for. And in your Popover code do:

    <Popover open={this.state.popped === i} ...

    And when you set popped set it like this.setState({popped: indexOfCardToShowPopover, anchorEl: elementOfCardToAnchorPopover });

    That way only 1 popover is ever open at a time.