Skip to content

Commit

Permalink
Merge pull request #877 from sharetribe/reusable-search-map
Browse files Browse the repository at this point in the history
Reusable map container
  • Loading branch information
Gnito authored Aug 1, 2018
2 parents a032ad2 + 53bd0b3 commit 31f4dda
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 29 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ way to update this template, but currently, we follow a pattern:
---

## Upcoming version
* [change] Reusable SearchMap.
[#877](https://github.com/sharetribe/flex-template-web/pull/877)
* [change] Use seeded random for client side coordinate obfuscation
[#874](https://github.com/sharetribe/flex-template-web/pull/874)

Expand Down
90 changes: 90 additions & 0 deletions src/components/SearchMap/ReusableMapContainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { node, string } from 'prop-types';
import css from './SearchMap.css';

class ReusableMapContainer extends React.Component {
constructor(props) {
super(props);

if (typeof window !== 'undefined') {
window.reusableSearchMapElement =
window.reusableSearchMapElement || document.createElement('div');

if (!props.className) {
console.warn('ReusableMapContainer should get className prop which defines its layout');
}
// If no className is given, we use some defaults, which makes it easier to debug loading.
const mapLayoutClassName = props.className || css.defaultMapLayout;

this.el = window.reusableSearchMapElement;
this.el.id = 'search-map';
this.el.classList.add(mapLayoutClassName);
}

this.mountNode = null;
this.renderSearchMap = this.renderSearchMap.bind(this);
}

componentDidMount() {
this.renderSearchMap();
}

componentDidUpdate() {
this.renderSearchMap();
}

componentWillUnmount() {
this.el.classList.add(css.reusableMapHidden);
this.mountNode.removeChild(this.el);
document.body.appendChild(this.el);
}

renderSearchMap() {
const targetDomNode = document.getElementById(this.el.id);

// Check if we have already added map somewhere on the DOM
if (!targetDomNode) {
if (this.mountNode && !this.mountNode.firstChild) {
// If mountable, but not yet mounted, append rendering context inside SPA rendering tree.
this.mountNode.appendChild(this.el);
} else if (!this.mountNode) {
// if no mountNode is found, append this outside SPA rendering tree (to document body)
document.body.appendChild(this.el);
}
} else {
this.el.classList.remove(css.reusableMapHidden);

if (this.mountNode && !this.mountNode.firstChild) {
// Move the map to correct location if we have rendered the map before
// (but it's not yet moved to correct location of rendering tree).
document.body.removeChild(this.el);
this.mountNode.appendChild(this.el);
}
}

ReactDOM.render(this.props.children, this.el);
}

render() {
return (
<div
className={css.reusableMap}
ref={node => {
this.mountNode = node;
}}
/>
);
}
}

ReusableMapContainer.defaultProps = {
className: string,
};

ReusableMapContainer.propTypes = {
children: node.isRequired,
className: string,
};

export default ReusableMapContainer;
31 changes: 31 additions & 0 deletions src/components/SearchMap/SearchMap.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,34 @@
width: 100%;
height: 100%;
}

.reusableMap {
width: 100%;
height: 100%;
}

.defaultMapLayout {
position: fixed;
top: 0;
right: 0;
width: 50vw;
height: 100vh;
}

/**
* When reusable map is attached right to the body it's hidden.
* Specificity rule is added to overwrite positioning coming from props.className
*/
body > .reusableMapHidden {
position: absolute;
top: -1000px;
left: -1000px;
visibility: hidden;
opacity: 0;

@media (--viewportMedium) {
top: -1000px;
left: -1000px;
right: auto;
}
}
62 changes: 34 additions & 28 deletions src/components/SearchMap/SearchMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { googleBoundsToSDKBounds } from '../../util/googleMaps';
import { SearchMapInfoCard, SearchMapPriceLabel, SearchMapGroupLabel } from '../../components';
import config from '../../config';

import ReusableMapContainer from './ReusableMapContainer';
import css from './SearchMap.css';

const LABEL_HANDLE = 'SearchMapLabel';
Expand Down Expand Up @@ -240,6 +241,7 @@ export class SearchMapComponent extends Component {
const {
className,
rootClassName,
reuseableContainerClassName,
center,
isOpenOnModal,
listings: originalListings,
Expand All @@ -263,28 +265,30 @@ export class SearchMapComponent extends Component {
// container element listens clicks so that opened SearchMapInfoCard can be closed
/* eslint-disable jsx-a11y/no-static-element-interactions */
return isMapsLibLoaded ? (
<MapWithGoogleMap
containerElement={<div className={classes} onClick={this.onMapClicked} />}
mapElement={<div className={mapClasses} />}
center={center}
isOpenOnModal={isOpenOnModal}
listings={listings}
activeListingId={activeListingId}
infoCardOpen={this.state.infoCardOpen}
onListingClicked={this.onListingClicked}
onMapLoad={this.onMapLoadHandler}
onIdle={() => {
if (this.googleMap) {
onIdle(this.googleMap);
}
}}
onCloseAsModal={() => {
if (onCloseAsModal) {
onCloseAsModal();
}
}}
zoom={zoom}
/>
<ReusableMapContainer className={reuseableContainerClassName}>
<MapWithGoogleMap
containerElement={<div className={classes} onClick={this.onMapClicked} />}
mapElement={<div className={mapClasses} />}
center={center}
isOpenOnModal={isOpenOnModal}
listings={listings}
activeListingId={activeListingId}
infoCardOpen={this.state.infoCardOpen}
onListingClicked={this.onListingClicked}
onMapLoad={this.onMapLoadHandler}
onIdle={() => {
if (this.googleMap) {
onIdle(this.googleMap);
}
}}
onCloseAsModal={() => {
if (onCloseAsModal) {
onCloseAsModal();
}
}}
zoom={zoom}
/>
</ReusableMapContainer>
) : (
<div className={classes} />
);
Expand All @@ -293,31 +297,33 @@ export class SearchMapComponent extends Component {
}

SearchMapComponent.defaultProps = {
className: null,
rootClassName: null,
mapRootClassName: null,
reuseableContainerClassName: null,
bounds: null,
center: new sdkTypes.LatLng(0, 0),
activeListingId: null,
className: null,
isOpenOnModal: false,
listings: [],
mapRootClassName: null,
onCloseAsModal: null,
rootClassName: null,
useLocationSearchBounds: true,
zoom: 11,
coordinatesConfig: config.coordinates,
};

SearchMapComponent.propTypes = {
className: string,
rootClassName: string,
mapRootClassName: string,
reuseableContainerClassName: string,
bounds: propTypes.latlngBounds,
center: propTypes.latlng,
activeListingId: propTypes.uuid,
className: string,
isOpenOnModal: bool,
listings: arrayOf(propTypes.listing),
mapRootClassName: string,
onCloseAsModal: func,
onIdle: func.isRequired,
rootClassName: string,
useLocationSearchBounds: bool, // eslint-disable-line react/no-unused-prop-types
zoom: number,
coordinatesConfig: shape({
Expand Down
5 changes: 5 additions & 0 deletions src/containers/SearchPage/SearchPage.css
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@
}
}

.mapWrapper {
width: 100%;
height: 100%;
}

.map {
width: 100vw;
height: 100vh;
Expand Down
3 changes: 2 additions & 1 deletion src/containers/SearchPage/SearchPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,10 @@ export class SearchPageComponent extends Component {
showAsModalMaxWidth={MODAL_BREAKPOINT}
onManageDisableScrolling={onManageDisableScrolling}
>
<div className={css.map}>
<div className={css.mapWrapper}>
{shouldShowSearchMap ? (
<SearchMap
reuseableContainerClassName={css.map}
activeListingId={activeListingId}
bounds={bounds}
center={origin}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ exports[`SearchPageComponent matches snapshot 1`] = `
mapRootClassName={null}
onCloseAsModal={[Function]}
onIdle={[Function]}
reuseableContainerClassName={null}
rootClassName={null}
useLocationSearchBounds={true}
zoom={11}
Expand Down

0 comments on commit 31f4dda

Please sign in to comment.