Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add domRendered callback to InfoWindow to rebind React Router, etc #202

Open
blainegarrett opened this issue Apr 22, 2018 · 6 comments
Open

Comments

@blainegarrett
Copy link
Contributor

Use case: The contents of my InfoWindow are Material-UI components that contain Buttons that utilize React Router and ultimately show a dialog. Currently, the InfoWindow component renders the children as a string and set this to the content of the infoWindow.setContent(renderedString). As such all event bindings are lost.

After experimenting, it seems infoWindow.setContent() can also take a dom element. If InfoWindow.renderChildren() could conditionally pass the rendered dom to a callback to further process it, we'd have an edge to rebind clickhandlers, etc.

I have gotten this to work on an older fork of google-maps-react. I have only tested it with React Router 3 and component. I assume react's sythentic events need to rebound for more general use, but I didn't need this for my use case. I'll try to branch latest master and make a PR adding this functionality.

@lepirlouit
Copy link
Contributor

which is the status of this ?

@alejo4373
Copy link

What can I do to help? This is affecting me as I need to put a <Link/> inside the InfoWindow or at least a onClick function to redirect users once they click on it. Also I ended up here by investigating the error message You should not use <Link> outside a <Router> given when I put a <Link/> inside the InfoWindow. I haven't figured a workaround yet.

@blainegarrett
Copy link
Contributor Author

blainegarrett commented Oct 13, 2018

Unfortunately, I never had time to make a PR and I've since moved on from the project and I don't know that my solution is the best. Somewhere I started investigating the use of "Portals" but had to move on to other things.

That said, I ended up with something like:

// MyMapComponnent
  showPopup() {
    // This is the clickhandler bound in the renderDomCallback for the infoWindow
    // TODO: Investigate Portal?
    let slug = this.state.selectedPlace.slug;
    this.props.router.push({
      pathname: `/galleries/${slug}`,
      state: {
        modal: true,
        returnTo: '/gallery/guide?'
      }
    });
  }
  renderDomCallback(domElement) {
    // This is passed down to the InfoWindow
    // TODO: Investigate Portal
    let a = domElement.querySelector("a#galleryPopupLink"); // id of Material-UI <Button /> inside of VenueRenderer component
    if (a) {
      a.addEventListener('click', this.showPopup.bind(this));
    }
    return domElement;
  }

  render () { 
  ...

  return (
    <Map ...>
    ...
    <InfoWindow
          marker={this.state.activeMarker}
          visible={this.state.showingInfoWindow}
          ...
          renderDomCallback={this.renderDomCallback.bind(this)}>
            <MapInfoWindowContent>
              <VenueRenderer resource={ this.state.selectedPlace } />
            </MapInfoWindowContent>
        </InfoWindow>
    </Map>
   );

Wrap MyMapComponent in withRouter() HOC from react-router. I'm leveraging several Material-UI bits, but mine looks like:
export default compose(withWidth(), withStyles(styles, { withTheme: true }))(withRouter(APIEnabledMap));
where ApiEnabledMap is

let APIMap = GoogleApiWrapper({
  apiKey: GOOGLE_MAPS_API_KEY,
  libraries: ['places','visualization'],
})(MyMapComponent);```

The change to google-maps-react
Inside my forked google-maps-react/components/InfoWindow component class:

renderChildren() {
    const {children} = this.props;

    var domWrapper = document.createElement('DIV');
    domWrapper.innerHTML = ReactDOMServer.renderToString(children);

    if (this.props.renderDomCallback) {
      domWrapper = this.props.renderDomCallback(domWrapper);
    }

    return domWrapper;
  }

Here is that same method in current master sans callback: https://github.com/fullstackreact/google-maps-react/blob/master/src/components/InfoWindow.js#L96

Basically, once the dom is rendered inside the infowindow, I'm calling the provided callback I passed in via props. From here, I just find the target element and bind click handlers. Not ideal, but works.

As far as Material-UI goes, the component just acts as a ship to wrap the new dom inside of the MUI theme provider for styling. If you do not need it, you can remove it.
Otherwise it looks like this:

export default function MapInfoWindowContent({children}) {
return (<ThemeProvider>{children}</ThemeProvider>);
}

That said, there might be a way to address this problem using "Portals" but I never dug that far into them. Either/or, I think you still would need a callback to wrap the dom.

More information on portals: https://reactjs.org/docs/portals.html

@miliu99
Copy link

miliu99 commented Oct 22, 2018

Any chance this problem gets resolved? I really need to use Link in InfoWindow. Thanks!

@eur2
Copy link

eur2 commented Jun 6, 2019

Hey @blainegarrett, any chance to see the code above on a repo? I'm struggling to make the InfoWindow popup/modal available with a Route. It seems you have succeed?
Many thanks in advance

@titivermeesch
Copy link

Is there still an activity here? I really need this to work but I can't figure it out...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants