From fcf6bd11c7bda89127b0dffcfc5575b26100fcff Mon Sep 17 00:00:00 2001 From: David Mai Date: Sun, 21 Feb 2016 23:54:00 -0800 Subject: [PATCH 1/7] world spec --- test/components/world.spec.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 test/components/world.spec.js diff --git a/test/components/world.spec.js b/test/components/world.spec.js new file mode 100644 index 000000000..1ed4bdb1a --- /dev/null +++ b/test/components/world.spec.js @@ -0,0 +1,22 @@ +import {expect} from 'chai' +import {shallow} from 'enzyme' +import React from 'react' + +import World from '../../app/scripts/components/world' + +describe('World', () => { + it('should have the correct defaults', () => { + const el = shallow() + expect(el.find('AutoSizer').length).to.equal(1) + + var inst = el.instance() + expect(inst.props.peersCount).to.equal(0) + expect(Object.keys(inst.props.locations).length) + .to.equal(0) + }) + + it('should set peersCount', () => { + const el = shallow() + expect(el.find('.counter').node.props.children).to.equal(1337) + }) +}) From a14fa0936aa18099273ac92b1c4e0fa5644ee6bb Mon Sep 17 00:00:00 2001 From: David Mai Date: Mon, 22 Feb 2016 17:20:53 -0800 Subject: [PATCH 2/7] move world-map to it's own component --- app/scripts/components/world-map.js | 87 +++++++++++++++++++++++++++++ app/scripts/components/world.js | 84 +--------------------------- test/components/map.spec.js | 15 +++++ test/components/world.spec.js | 2 +- 4 files changed, 106 insertions(+), 82 deletions(-) create mode 100644 app/scripts/components/world-map.js create mode 100644 test/components/map.spec.js diff --git a/app/scripts/components/world-map.js b/app/scripts/components/world-map.js new file mode 100644 index 000000000..e2417fd9c --- /dev/null +++ b/app/scripts/components/world-map.js @@ -0,0 +1,87 @@ +import React, {Component, PropTypes} from 'react' +import d3 from 'd3' +import topojson from 'topojson' +import {AutoSizer} from 'react-virtualized' +import ReactFauxDOM from 'react-faux-dom' + +import worldData from '../../data/world.json' + +export default class WorldMap extends Component { + static propTypes = { + coordinates: PropTypes.array.isRequired + }; + + _renderMap = ({width, height}) => { + const projection = d3.geo.equirectangular() + .scale(height / Math.PI) + .translate([width / 2, height / 2]) + .precision(0.1) + + const path = d3.geo.path() + .projection(projection) + + const graticule = d3.geo.graticule() + + const el = d3.select(ReactFauxDOM.createElement('svg')) + .attr('width', width) + .attr('height', height) + + // Fill Pattern + el.append('defs') + .append('pattern') + .attr('id', 'gridpattern') + .attr('x', 0) + .attr('y', 0) + .attr('width', 4) + .attr('height', 4) + .attr('patternUnits', 'userSpaceOnUse') + .append('circle') + .attr('cx', 0) + .attr('cy', 0) + .attr('r', 1) + .attr('style', 'stroke: none; fill: rgba(255, 255, 255, 0.7)') + + el.append('path') + .datum(graticule) + .attr('class', 'graticule') + .attr('d', path) + + el.insert('path', '.graticule') + .datum(topojson.feature(worldData, worldData.objects.land)) + .attr('d', path) + .attr('fill', 'url(#gridpattern)') + + el.insert('path', '.graticule') + .datum(topojson.mesh( + worldData, + worldData.objects.countries, + (a, b) => a !== b + )) + .attr('d', path) + .attr('fill', 'none') + .attr('stroke', 'none') + .attr('stroke-width', '0.5px') + + el.append('path') + .datum({ + type: 'MultiPoint', + coordinates: this.props.coordinates + }) + .attr('d', path.pointRadius((d) => 8)) + .attr('class', 'world-locations-base') + + el.append('path') + .datum({ + type: 'MultiPoint', + coordinates: this.props.coordinates + }) + .attr('d', path.pointRadius((d) => 2)) + .attr('class', 'world-locations-center') + + return el.node().toReact() + } + + render () { + return {this._renderMap} + } +} diff --git a/app/scripts/components/world.js b/app/scripts/components/world.js index 4db1e5981..a0c168529 100644 --- a/app/scripts/components/world.js +++ b/app/scripts/components/world.js @@ -1,9 +1,6 @@ import React, {Component, PropTypes} from 'react' -import d3 from 'd3' -import ReactFauxDOM from 'react-faux-dom' -import topojson from 'topojson' -import {AutoSizer} from 'react-virtualized' import {map, isEqual} from 'lodash' +import WorldMap from './world-map' import worldData from '../../data/world.json' @@ -18,93 +15,18 @@ export default class World extends Component { locations: {} }; - _renderMap = (locations) => { - return ({width, height}) => { - const projection = d3.geo.equirectangular() - .scale(height / Math.PI) - .translate([width / 2, height / 2]) - .precision(0.1) - - const path = d3.geo.path() - .projection(projection) - - const graticule = d3.geo.graticule() - - const el = d3.select(ReactFauxDOM.createElement('svg')) - .attr('width', width) - .attr('height', height) - - // Fill Pattern - el.append('defs') - .append('pattern') - .attr('id', 'gridpattern') - .attr('x', 0) - .attr('y', 0) - .attr('width', 4) - .attr('height', 4) - .attr('patternUnits', 'userSpaceOnUse') - .append('circle') - .attr('cx', 0) - .attr('cy', 0) - .attr('r', 1) - .attr('style', 'stroke: none; fill: rgba(255, 255, 255, 0.7)') - - el.append('path') - .datum(graticule) - .attr('class', 'graticule') - .attr('d', path) - - el.insert('path', '.graticule') - .datum(topojson.feature(worldData, worldData.objects.land)) - .attr('d', path) - .attr('fill', 'url(#gridpattern)') - - el.insert('path', '.graticule') - .datum(topojson.mesh( - worldData, - worldData.objects.countries, - (a, b) => a !== b - )) - .attr('d', path) - .attr('fill', 'none') - .attr('stroke', 'none') - .attr('stroke-width', '0.5px') - - el.append('path') - .datum({ - type: 'MultiPoint', - coordinates: locations - }) - .attr('d', path.pointRadius((d) => 8)) - .attr('class', 'world-locations-base') - - el.append('path') - .datum({ - type: 'MultiPoint', - coordinates: locations - }) - .attr('d', path.pointRadius((d) => 2)) - .attr('class', 'world-locations-center') - - return el.node().toReact() - } - }; - shouldComponentUpdate (nextProps) { return !isEqual(nextProps, this.props) } render () { - // Draw peer locations - const locations = map(this.props.locations, ({lon, lat}) => { + const coordinates = map(this.props.locations, ({lon, lat}) => { return [lon, lat] }) return (
- - {this._renderMap(locations)} - +
{this.props.peersCount}
Peers
diff --git a/test/components/map.spec.js b/test/components/map.spec.js new file mode 100644 index 000000000..275b571fd --- /dev/null +++ b/test/components/map.spec.js @@ -0,0 +1,15 @@ +import {expect} from 'chai' +import {render} from 'enzyme' +import sinon from 'sinon' +import React from 'react' + +import WorldMap from '../../app/scripts/components/world-map' + +describe('WorldMap', () => { + it('should render an svg', () => { + const c = [[12, 14]] + + const el = render() + expect(el.find('svg').length).to.equal(1) + }) +}) diff --git a/test/components/world.spec.js b/test/components/world.spec.js index 1ed4bdb1a..22f0a7ff6 100644 --- a/test/components/world.spec.js +++ b/test/components/world.spec.js @@ -7,7 +7,7 @@ import World from '../../app/scripts/components/world' describe('World', () => { it('should have the correct defaults', () => { const el = shallow() - expect(el.find('AutoSizer').length).to.equal(1) + expect(el.find('WorldMap').length).to.equal(1) var inst = el.instance() expect(inst.props.peersCount).to.equal(0) From 87cb734276ff57ec0e1d7805417be43135e86d4d Mon Sep 17 00:00:00 2001 From: David Mai Date: Mon, 22 Feb 2016 17:39:55 -0800 Subject: [PATCH 3/7] fix lint errors --- app/scripts/components/world.js | 4 +--- test/components/{map.spec.js => world-map.spec.js} | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) rename test/components/{map.spec.js => world-map.spec.js} (92%) diff --git a/app/scripts/components/world.js b/app/scripts/components/world.js index a0c168529..40423b6a1 100644 --- a/app/scripts/components/world.js +++ b/app/scripts/components/world.js @@ -2,8 +2,6 @@ import React, {Component, PropTypes} from 'react' import {map, isEqual} from 'lodash' import WorldMap from './world-map' -import worldData from '../../data/world.json' - export default class World extends Component { static propTypes = { peersCount: PropTypes.number, @@ -26,7 +24,7 @@ export default class World extends Component { return (
- +
{this.props.peersCount}
Peers
diff --git a/test/components/map.spec.js b/test/components/world-map.spec.js similarity index 92% rename from test/components/map.spec.js rename to test/components/world-map.spec.js index 275b571fd..3effc10ad 100644 --- a/test/components/map.spec.js +++ b/test/components/world-map.spec.js @@ -1,6 +1,5 @@ import {expect} from 'chai' import {render} from 'enzyme' -import sinon from 'sinon' import React from 'react' import WorldMap from '../../app/scripts/components/world-map' From af730f5fa910b8ce4455325b98743032e384fb18 Mon Sep 17 00:00:00 2001 From: David Mai Date: Tue, 23 Feb 2016 00:04:47 -0800 Subject: [PATCH 4/7] assert correct coordinates --- test/components/world-map.spec.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/test/components/world-map.spec.js b/test/components/world-map.spec.js index 3effc10ad..012d984e8 100644 --- a/test/components/world-map.spec.js +++ b/test/components/world-map.spec.js @@ -1,14 +1,24 @@ import {expect} from 'chai' -import {render} from 'enzyme' +import {shallow, render} from 'enzyme' import React from 'react' import WorldMap from '../../app/scripts/components/world-map' describe('WorldMap', () => { - it('should render an svg', () => { - const c = [[12, 14]] + const c = [[12, 14]] + it('should render an svg', () => { const el = render() expect(el.find('svg').length).to.equal(1) }) + + it('should have the correct coordiates', () => { + const el = shallow() + const p = el.instance().props + + c.forEach((coordinates, i) => { + expect(coordinates[0]).to.equal(p[i][0]) + expect(coordinates[1]).to.equal(p[i][1]) + }) + }) }) From a8807b7172470f9977dcb7d5ad464001b6ec78b0 Mon Sep 17 00:00:00 2001 From: David Mai Date: Tue, 23 Feb 2016 00:19:53 -0800 Subject: [PATCH 5/7] fix coordinate property reference --- test/components/world-map.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/components/world-map.spec.js b/test/components/world-map.spec.js index 012d984e8..20f08a502 100644 --- a/test/components/world-map.spec.js +++ b/test/components/world-map.spec.js @@ -17,8 +17,8 @@ describe('WorldMap', () => { const p = el.instance().props c.forEach((coordinates, i) => { - expect(coordinates[0]).to.equal(p[i][0]) - expect(coordinates[1]).to.equal(p[i][1]) + expect(coordinates[0]).to.equal(p.coordinates[i][0]) + expect(coordinates[1]).to.equal(p.coordinates[i][1]) }) }) }) From cbbf145d19f154ee80e78fb3a3450dc128643da1 Mon Sep 17 00:00:00 2001 From: David Mai Date: Tue, 23 Feb 2016 00:34:46 -0800 Subject: [PATCH 6/7] a semicolon is required after a class property --- app/scripts/components/world-map.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/components/world-map.js b/app/scripts/components/world-map.js index e2417fd9c..987d34007 100644 --- a/app/scripts/components/world-map.js +++ b/app/scripts/components/world-map.js @@ -83,5 +83,5 @@ export default class WorldMap extends Component { render () { return {this._renderMap} - } + }; } From d6d4fa5b59a1160bc2956d2306536e2be71c4fe1 Mon Sep 17 00:00:00 2001 From: David Mai Date: Tue, 23 Feb 2016 00:46:54 -0800 Subject: [PATCH 7/7] correct semicolon --- app/scripts/components/world-map.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/scripts/components/world-map.js b/app/scripts/components/world-map.js index 987d34007..93e4a3238 100644 --- a/app/scripts/components/world-map.js +++ b/app/scripts/components/world-map.js @@ -79,9 +79,9 @@ export default class WorldMap extends Component { .attr('class', 'world-locations-center') return el.node().toReact() - } + }; render () { return {this._renderMap} - }; + } }