Skip to content

Commit

Permalink
Merge pull request #8 from pieterv/frag-component
Browse files Browse the repository at this point in the history
Added Frag component
  • Loading branch information
pieterv committed Sep 15, 2014
2 parents 8d60b38 + a2b6cbe commit e0e0c6d
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 11 deletions.
21 changes: 21 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ API
- [`Circle`](#circle)
- [`Rectangle`](#rectangle)
- [`OverlayView`](#overlayview)
- [`Frag`](#frag)
- [PropTypes](#proptypes)

# Components
Expand Down Expand Up @@ -161,6 +162,26 @@ Uses the GoogleMaps OverlayView to render arbitrary React DOM elements into the

[GoogleMaps OverlayView](https://developers.google.com/maps/documentation/javascript/reference#OverlayView) docs.

## Frag

```js
var MarkerCollection = React.createClass({
render: function() {
return (
<Frag map={this.props.map}>
<Marker {...props} />
<Marker {...props} />
<Marker {...props} />
</Frag>
);
}
});
```

Frag is a helper component for creating reusable React GoogleMap components. This component has no functionality or output, it just allows you to return multiple components from a custom components `render` function.

The only prop required is the `map` prop (the GoogleMap instance), this is automatically passed down to all direct children of the `Map` component but will need to be manually set if you use `Frag` inside a custom component. The `Frag` component will then pass the `map` prop down to all of its direct children.

# PropTypes

```js
Expand Down
101 changes: 101 additions & 0 deletions examples/frags/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/** @jsx React.DOM */
"use strict";

// This example creates a reusable react component made
// up of ReactGoogleMap components. You can click the map
// to add a new instance or drag the component to move
// it around.

var React = require('react/addons');
var ReactGoogleMaps = require('../../');
var GoogleMapsAPI = window.google.maps;
var Map = ReactGoogleMaps.Map;
var Circle = ReactGoogleMaps.Circle;
var Polyline = ReactGoogleMaps.Polyline;
var Frag = ReactGoogleMaps.Frag;
var LatLng = GoogleMapsAPI.LatLng;

var DraggableFace = React.createClass({
render: function() {
var centerLat = this.props.center.lat();
var centerLng = this.props.center.lng();
return (
<Frag
map={this.props.map}>
<Circle
draggable
onDrag={this.props.onDrag}
center={new LatLng(centerLat + 0.05, centerLng + 0.1)}
radius={3000} />

<Circle
draggable
onDrag={this.props.onDrag}
center={new LatLng(centerLat + 0.05, centerLng - 0.1)}
radius={3000} />

<Polyline
draggable
onDrag={this.props.onDrag}
path={[
new LatLng(centerLat - 0.05, centerLng + 0.1),
new LatLng(centerLat - 0.05, centerLng - 0.1)
]} />
</Frag>
);
}
});

var GoogleMapFrags = React.createClass({
getInitialState: function() {
return {
center: new LatLng(41.879535, -87.624333),
zoom: 8,
faces: [
new LatLng(42.48374, -87.01171)
]
};
},

render: function() {
return (
<Map
initialZoom={this.state.zoom}
initialCenter={this.state.center}
width={700}
height={700}
onClick={this.handleFaceCreate}>
{this.state.faces.map(this.renderFace)}
</Map>
);
},

renderFace: function(position, i) {
return (
<DraggableFace
center={position}
onDrag={this.handleFaceDrag.bind(null, i)}
key={i} />
);
},

handleFaceDrag: function(i, e) {
var faces = React.addons
.update(this.state.faces, {$splice: [[i, 1, e.latLng]]});

this.setState({
faces: faces
});
},

handleFaceCreate: function(e) {
var faces = React.addons
.update(this.state.faces, {$push: [e.latLng]});

this.setState({
faces: faces
});
}
});

React.renderComponent(<GoogleMapFrags />, document.getElementById('example'));
8 changes: 8 additions & 0 deletions examples/frags/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!doctype html public "restroom">
<title>Frags</title>
<link href="../global.css" rel="stylesheet" />
<body>
<h1>Frags</h1>
<div id="example"></div>
<script src="http://maps.googleapis.com/maps/api/js"></script>
<script src="../build/frags.js"></script>
1 change: 1 addition & 0 deletions src/ReactMapComponents.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ var ReactMapComponents = mapObject({
Rectangle: null,
Polygon: null
// OverlayView: Note: Injected, see `ReactOverlayView`.
// Frag: Note: Injected, see `ReactFrag`.
}, createMapComponentClass);

var injection = {
Expand Down
4 changes: 3 additions & 1 deletion src/ui/ReactDefaultInjection.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ var MapEvent = require('./MapEvent');
var MapOptionConfig = require('./MapOptionConfig');
var ReactMap = require('./components/ReactMap');
var ReactOverlayView = require('./components/ReactOverlayView');
var ReactFrag = require('./components/ReactFrag');
var SimpleEventPlugin = require('../eventPlugins/SimpleEventPlugin');
var MouseEventPlugin = require('../eventPlugins/MouseEventPlugin');
var SideEffectEventPlugin = require('../eventPlugins/SideEffectEventPlugin');
Expand All @@ -26,7 +27,8 @@ function inject() {

ReactInjection.MapComponents.injectComponentClasses({
Map: ReactMap,
OverlayView: ReactOverlayView
OverlayView: ReactOverlayView,
Frag: ReactFrag
});

ReactInjection.MapOption.injectMapOptionConfig(MapOptionConfig);
Expand Down
30 changes: 30 additions & 0 deletions src/ui/components/ReactFrag.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/** @jsx React.DOM */
"use strict";

var React = require('react');
var cloneWithProps = require('react/lib/cloneWithProps');
var MapPropTypes = require('../MapPropTypes');

function injectMapInto(child) {
return React.isValidComponent(child) ?
cloneWithProps(child, {map: this.props.map}) : child;
}

var ReactFrag = React.createClass({
propTypes: {
map: MapPropTypes.Map.isRequired,
},

render: function() {
// Inject the `mapProps` into all children that are
// valid components.
var children = React.Children
.map(this.props.children, injectMapInto, this);

return (
<span>{children}</span>
);
}
});

module.exports = ReactFrag;
23 changes: 13 additions & 10 deletions src/ui/components/ReactMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ var keyMirror = require('react/lib/keyMirror');
var ReactMapComponents = require('../../ReactMapComponents');
var MapPropTypes = require('../MapPropTypes');
var PropTypeUtils = require('../../utils/PropTypeUtils');
var ReactFrag = require('./ReactFrag');

var GoogleMapsMap = ReactMapComponents.Map;

Expand Down Expand Up @@ -51,20 +52,22 @@ var ReactMap = React.createClass({

var map;
if (this.state.mapLifeCycleState !== MapLifeCycle.CREATING_HOLDER) {
map = this.transferPropsTo(<GoogleMapsMap ref="map" mapDiv={this.refs.mapHolder.getDOMNode()} width={null} height={null} />);
map = this.transferPropsTo(
<GoogleMapsMap
ref="map"
mapDiv={this.refs.mapHolder.getDOMNode()}
width={null}
height={null} />
);
}

// Check if there is an instance of a Google Map first
// Loop through each child adding the `this.__node` object
// to their props, this will allow the children to be injected
// into this map instance.
var children;
if (!this.state.mapLifeCycleState) {
var mapProps = {map: this.refs.map.__node};
children = React.Children
.map(this.props.children, function(child) {
return React.isValidComponent(child) ? cloneWithProps(child, mapProps) : child;
});
children = (
<ReactFrag map={this.refs.map.__node}>
{this.props.children}
</ReactFrag>
);
}

return (
Expand Down
36 changes: 36 additions & 0 deletions src/ui/components/__tests__/ReactFrag-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/** @jsx React.DOM */

jest.dontMock('../ReactFrag');

describe('ReactFrag', function() {
it('should pass all props to all valid children', function() {
var React = require('react');
var ReactTestUtils = require('react/lib/ReactTestUtils');
var ReactFrag = require('../ReactFrag');

var didMount = jest.genMockFn();
var Test = React.createClass({
render: function() { return null; },
componentDidMount: function() {
didMount(this.props);
}
});

var map = {};
var testProp = {};
ReactTestUtils.renderIntoDocument(
<ReactFrag map={map} testProp={testProp}>
<Test />
{null}
{false}
<ReactFrag>
<Test />
</ReactFrag>
</ReactFrag>
);

expect(didMount.mock.calls.length).toBe(2);
expect(didMount.mock.calls[0][0]).toEqual({map: map});
expect(didMount.mock.calls[1][0]).toEqual({map: map});
});
});

0 comments on commit e0e0c6d

Please sign in to comment.