how to make a popup linkable with mapbox and Next.js?

Viewed 1218

I'm using react-map-gl to render a map showing differents locations with markers.
When clicking on this markers it shows a popup with all the details from this location.
I'd like when I click on the popup that is redirects me to the page of the location.

My code for the markers and popup :

return (
    <ReactMapGL
      {...viewport}
      mapboxApiAccessToken={mapboxKey}
      onViewportChange={(viewport) => {
        setViewport(viewport);
      }}
      mapStyle="mapbox://styles/medhisavarybouge/cki8vpz0l0iee1aoiw7r69bwr"
    >
      {router.pathname === "/pdrs/[slug]" &&
        data.map((location) => {
          const { id, latitude, longitude } = location;
          return (
            <Marker key={id} latitude={latitude} longitude={longitude}>
              {/* si on supprime le bouton l'image de s'affiche pas sur la map*/}
              <button></button>
              <Image src="/images/map-pin.svg" alt="" width={40} height={40} />
            </Marker>
          );
        })}
      {router.pathname === "/points-de-rencontre-sportive" &&
        data.map((pdrs) => {
          const { id, latitude, longitude } = pdrs;
          return (
            <Marker key={id} latitude={latitude} longitude={longitude}>
              <button
                className={style.markerBtn}
                onClick={(event) => {
                  event.preventDefault();
                  setSelectedPDRS(pdrs);
                }}
              >
                <Image
                  src="/images/map-pin.svg"
                  alt=""
                  width={40}
                  height={40}
                />
              </button>
            </Marker>
          );
        })}

      {selectedPDRS && (
        <Popup
          latitude={selectedPDRS.latitude}
          longitude={selectedPDRS.longitude}
          onClose={() => {
            setSelectedPDRS(null);
          }}
          closeButton={true}
          closeOnclick={false}
          captureClick={true}
        >
          <div
            className={style.selectedPDRS}
            onMouseDown={() => {
              router.push(`/pdrs/${selectedPDRS.slug}`);
            }}
          >
            <Image
              src={imageUrl}
              alt={selectedPDRS.micro_activity_name}
              width={100}
              height={100}
            />

            <div className={style.selectedPDRSdetails}>
              <div className={style.selectedPDRStitle}>
                <h4>
                  {selectedPDRS.installation_name}
                </h4>
                <span>
                  {selectedPDRS.name)}
                </span>
                <span>
                  {selectedPDRS.micro_activity_name)}       
                </span>
              </div>
              <div className={style.selectedPDRSdate}>
                {selectedPDRS.start_date &&
                  selectedPDRS.start_date +
                    " - " +
                    selectedPDRS.duration +
                    " - " +
                    selectedPDRS.access_type}
              </div>
              <div className={style.selectedPDRSbadge}>
                {badge[selectedPDRS.category]}
              </div>
            </div>
          </div>
        </Popup>
      )}
    </ReactMapGL>
  );

I had to put a onMouseDown on my popup div so that the redirection works.
With an onclick it doesn't work. The popup just closed.
The problem is for the responsive.
I think the onClick is the same as touching the phone screen but the onMouseDown does not do anything on phone so the users can't be redirect to the page.
How can I make the popup redirection work with a onClick ?

1 Answers

You don't need to add onMouseDown. onClick is sufficient for desktop + touch devices.

One thing to note is that bind onClick to layer not to Map directly. Example

Updated code below with React(You can update in Next accordingly), where on clicking popup "hi" will print in console.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <!-- Load Babel -->
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

    <link rel="stylesheet" type="text/css" href="https://api.mapbox.com/mapbox-assembly/mbx/v0.18.0/assembly.min.css">
    <script src="https://api.mapbox.com/mapbox-assembly/mbx/v0.18.0/assembly.js"></script>

    <link rel="stylesheet" type="text/css" href="https://api.mapbox.com/mapbox-gl-js/v0.39.1/mapbox-gl.css">
    <script src="https://api.mapbox.com/mapbox-gl-js/v0.39.1/mapbox-gl.js"></script>

</head>

<body>
    <div id="app"></div>
    <script type="text/babel">
        /* See https://github.com/mapbox/mapbox-react-examples/ for full example */

        //Your Mapbox access token from here
        mapboxgl.accessToken = '';

        class Tooltip extends React.Component {
            handleClick(e) {
                console.log("hi");
            }
            render() {
                const { features } = this.props;

                const renderFeature = (feature, i) => {
                    return (
                        <div key={i}>
                            <strong className='mr3'>{feature.layer['source-layer']}:</strong>
                            <span className='color-gray-light'>{feature.layer.id}</span>
                        </div>
                    )
                };

                return (
                    <div onClick={(e) => this.handleClick(e)} className="flex-parent-inline flex-parent--center-cross flex-parent--column absolute bottom">
                        <div className="flex-child px12 py12 bg-gray-dark color-white shadow-darken10 round txt-s w240 clip txt-truncate">
                            {features.map(renderFeature)}
                        </div>
                        <span className="flex-child color-gray-dark triangle triangle--d"></span>
                    </div>
                );
            }
        }

        class Application extends React.Component {
            tooltipContainer;

            setTooltip(features) {
                if (features.length) {
                    ReactDOM.render(
                        React.createElement(
                            Tooltip, {
                            features
                        }
                        ),
                        this.tooltipContainer
                    );
                } else {
                    ReactDOM.unmountComponentAtNode(this.tooltipContainer)
                }
            }

            componentDidMount() {

                // Container to put React generated content in.
                this.tooltipContainer = document.createElement('div');

                const map = new mapboxgl.Map({
                    container: this.mapContainer,
                    style: 'mapbox://styles/mapbox/streets-v9',
                    center: [-79.38, 43.65],
                    zoom: 12.5,
                    dragRotate: false,
                    scrollZoom: true,
                    doubleClickZoom: false,
                    touchZoomRotate: false,
                    renderWorldCopies: false
                });

                map.getCanvas().style.cursor = 'default';

                const tooltip = new mapboxgl.Marker(this.tooltipContainer, {
                    offset: [-120, 0]
                }).setLngLat([0, 0]).addTo(map);

                map.on('click', "place-neighbourhood", (e) => {
                    const features = map.queryRenderedFeatures(e.point);
                    tooltip.setLngLat(e.lngLat);
                    /* map.getCanvas().style.cursor = features.length ? 'pointer' : ''; */
                    this.setTooltip(features);
                });

                // Change the cursor to a pointer when the mouse is over the places layer.
                map.on('mouseenter', "place-neighbourhood", function () {
                    map.getCanvas().style.cursor = 'pointer';
                });

                // Change it back to a pointer when it leaves.
                map.on('mouseleave', "place-neighbourhood", function () {
                    map.getCanvas().style.cursor = 'default';
                });
            }

            render() {
                return (
                    <div ref={el => this.mapContainer = el} className="absolute top right left bottom" />
                );
            }
        }

        ReactDOM.render(<Application />, document.getElementById('app'));
    </script>
</body>

</html>

Checkout https://visgl.github.io/react-map-gl/examples/controls example at react-map-gl package where in file. Update below code:

import * as React from 'react';

function CityInfo(props) {
  const { info } = props;
  const displayName = `${info.city}, ${info.state}`;

  const handleClick = function (e) {
    console.log("hi");
  }

  return (
    <div onClick={(e) => handleClick(e)}>
      <div>
        {displayName} |{' '}
        <a
          target="_new"
          href={`http://en.wikipedia.org/w/index.php?title=Special:Search&search=${displayName}`}
        >
          Wikipedia
        </a>
      </div>
      <img width={240} src={info.image} />
    </div>
  );
}

export default React.memo(CityInfo);

Screenshot: enter image description here

Thanks!

Related