Gist to serve as template for future TDD D3 blocks
Last active
May 17, 2016 15:43
-
-
Save Golodhros/dfe7c0c8be07a461e6ba to your computer and use it in GitHub Desktop.
TDD D3 Template
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var graphs = graphs || {}; | |
graphs.dataManager = function module() { | |
var exports = {}, | |
dispatch = d3.dispatch('dataReady', 'dataLoading', 'dataError'), | |
data; | |
d3.rebind(exports, dispatch, 'on'); | |
exports.loadJsonData = function(_file, _cleaningFn) { | |
var loadJson = d3.json(_file); | |
loadJson.on('progress', function(){ | |
dispatch.dataLoading(d3.event.loaded); | |
}); | |
loadJson.get(function (_err, _response){ | |
if(!_err){ | |
_response.data.forEach(function(d){ | |
_cleaningFn(d); | |
}); | |
data = _response.data; | |
dispatch.dataReady(_response.data); | |
}else{ | |
dispatch.dataError(_err.statusText); | |
} | |
}); | |
}; | |
exports.loadTsvData = function(_file, _cleaningFn) { | |
var loadTsv = d3.tsv(_file); | |
loadTsv.on('progress', function() { | |
dispatch.dataLoading(d3.event.loaded); | |
}); | |
loadTsv.get(function (_err, _response) { | |
if(!_err){ | |
_response.forEach(function(d){ | |
_cleaningFn(d); | |
}); | |
data = _response; | |
dispatch.dataReady(_response); | |
}else{ | |
dispatch.dataError(_err.statusText); | |
} | |
}); | |
}; | |
// If we need more types of data geoJSON, csv, etc. we will need | |
// to create methods for them | |
exports.getCleanedData = function(){ | |
return data; | |
}; | |
return exports; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<link type="text/css" rel="stylesheet" href="style.css"/> | |
</head> | |
<body> | |
<h2 class="block-title">TDD Template</h2> | |
<div class="graph"></div> | |
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script src="dataManager.js"></script> | |
<script src="src.js"></script> | |
<script type="text/javascript"> | |
// Code that instantiates the graph and uses the data manager to load the data | |
var app = { | |
// D3 Reusable API Chart | |
graph: { | |
dataManager: null, | |
config: { | |
margin : { | |
top : 20, | |
bottom: 30, | |
right : 20, | |
left : 40 | |
}, | |
aspectWidth: 13, | |
aspectHeight: 4, | |
animation: 'linear', | |
dataURL: 'data.tsv' | |
}, | |
init: function(ele){ | |
this.$el = ele; | |
this.requestNewData(); | |
this.addEvents(); | |
}, | |
addEvents: function(){ | |
//Callback triggered by browser | |
window.onresize = $.proxy(this.drawGraph, this); | |
}, | |
calculateRatioHeight: function(width) { | |
var config = this.config; | |
return Math.ceil((width * config.aspectHeight) / config.aspectWidth); | |
}, | |
dataCleaningFunction: function(d){ | |
d.frequency = +d.frequency; | |
d.letter = d.letter; | |
}, | |
drawGraph: function(){ | |
var config = this.config, | |
width = this.$el.width(), | |
height = this.calculateRatioHeight(width); | |
this.resetGraph(); | |
this.chart = graphs.chart() | |
.width(width).height(height).margin(config.margin); | |
this.container = d3.select(this.$el[0]) | |
.datum(this.data) | |
.call(this.chart); | |
}, | |
handleReceivedData: function(result){ | |
this.data = result; | |
this.drawGraph(); | |
}, | |
requestNewData: function(el){ | |
this.dataManager = graphs.dataManager(); | |
this.dataManager.on('dataError', function(errorMsg){ | |
console.log('error:', errorMsg); | |
}); | |
this.dataManager.on('dataReady', $.proxy(this.handleReceivedData, this)); | |
this.dataManager.loadTsvData(this.config.dataURL, this.dataCleaningFunction); | |
}, | |
resetGraph: function(){ | |
this.$el.find('svg').remove(); | |
} | |
} | |
}; | |
$(function(){ | |
app.graph.init($('.graph')); | |
}); | |
</script> | |
</body> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var graphs = graphs || {}; | |
graphs.chart = function module(){ | |
var margin = {top: 20, right: 20, bottom: 30, left: 40}, | |
width = 960, | |
height = 500, | |
data, | |
chartW, chartH, | |
xScale, yScale, | |
xAxis, yAxis; | |
var svg; | |
function buildContainerGroups(){ | |
var container = svg.append("g").classed("container-group", true); | |
container.append("g").classed("chart-group", true); | |
container.append("g").classed("x-axis-group", true); | |
container.append("g").classed("y-axis-group", true); | |
} | |
function buildScales(){ | |
xScale = d3.scale.ordinal() | |
.domain(data.map(function(d) { return d.letter; })) | |
.rangeRoundBands([0, chartW], 0.1); | |
yScale = d3.scale.linear() | |
.domain([0, d3.max(data, function(d) { return d.frequency; })]) | |
.range([chartH, 0]); | |
} | |
function buildAxis(){ | |
xAxis = d3.svg.axis() | |
.scale(xScale) | |
.orient("bottom"); | |
yAxis = d3.svg.axis() | |
.scale(yScale) | |
.orient("left") | |
.ticks(10, "%"); | |
} | |
function drawAxis(){ | |
svg.select('.x-axis-group') | |
.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + chartH + ")") | |
.call(xAxis); | |
svg.select(".y-axis-group") | |
.append("g") | |
.attr("class", "y axis") | |
.call(yAxis) | |
.append("text") | |
.attr("transform", "rotate(-90)") | |
.attr("y", 6) | |
.attr("dy", ".71em") | |
.style("text-anchor", "end") | |
.text("Frequency"); | |
} | |
function drawBars(){ | |
// Setup the enter, exit and update of the actual bars in the chart. | |
// Select the bars, and bind the data to the .bar elements. | |
var bars = svg.select('.chart-group').selectAll(".bar") | |
.data(data); | |
// If there aren't any bars create them | |
bars.enter().append('rect') | |
.attr("class", "bar") | |
.attr("x", function(d) { return xScale(d.letter); }) | |
.attr("width", xScale.rangeBand()) | |
.attr("y", function(d) { return yScale(d.frequency); }) | |
.attr("height", function(d) { return chartH - yScale(d.frequency); }); | |
} | |
function exports(_selection){ | |
_selection.each(function(_data){ | |
chartW = width - margin.left - margin.right; | |
chartH = height - margin.top - margin.bottom; | |
data = _data; | |
buildScales(); | |
buildAxis(); | |
if (!svg) { | |
svg = d3.select(this) | |
.append('svg') | |
.classed('bar-chart', true); | |
} | |
svg.attr({ | |
width: width + margin.left + margin.right, | |
height: height + margin.top + margin.bottom | |
}); | |
buildContainerGroups(); | |
drawBars(); | |
drawAxis(); | |
}); | |
} | |
exports.margin = function(_x) { | |
if (!arguments.length) return margin; | |
margin = _x; | |
return this; | |
}; | |
exports.width = function(_x) { | |
if (!arguments.length) return width; | |
width = _x; | |
return this; | |
}; | |
exports.height = function(_x) { | |
if (!arguments.length) return height; | |
height = _x; | |
return this; | |
}; | |
return exports; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Simple tests for the bar chart | |
describe('Reusable barChart Test Suite', function() { | |
var barChart, dataset, containerFixture, f; | |
beforeEach(function() { | |
dataset = [ | |
{ | |
letter: 'A', | |
frequency: .08167 | |
}, | |
{ | |
letter: 'B', | |
frequency: .01492 | |
}, | |
{ | |
letter: 'C', | |
frequency: .02782 | |
}, | |
{ | |
letter: 'D', | |
frequency: .04253 | |
}, | |
{ | |
letter: 'E', | |
frequency: .12702 | |
}, | |
{ | |
letter: 'F', | |
frequency: .02288 | |
}, | |
{ | |
letter: 'G', | |
frequency: .02015 | |
}, | |
{ | |
letter: 'H', | |
frequency: .06094 | |
}, | |
{ | |
letter: 'I', | |
frequency: .06966 | |
}, | |
{ | |
letter: 'J', | |
frequency: .00153 | |
}, | |
{ | |
letter: 'K', | |
frequency: .00772 | |
}, | |
{ | |
letter: 'L', | |
frequency: .04025 | |
}, | |
{ | |
letter: 'M', | |
frequency: .02406 | |
}, | |
{ | |
letter: 'N', | |
frequency: .06749 | |
}, | |
{ | |
letter: 'O', | |
frequency: .07507 | |
}, | |
{ | |
letter: 'P', | |
frequency: .01929 | |
}, | |
{ | |
letter: 'Q', | |
frequency: .00095 | |
}, | |
{ | |
letter: 'R', | |
frequency: .05987 | |
}, | |
{ | |
letter: 'S', | |
frequency: .06327 | |
}, | |
{ | |
letter: 'T', | |
frequency: .09056 | |
}, | |
{ | |
letter: 'U', | |
frequency: .02758 | |
}, | |
{ | |
letter: 'V', | |
frequency: .00978 | |
}, | |
{ | |
letter: 'W', | |
frequency: .02360 | |
}, | |
{ | |
letter: 'X', | |
frequency: .00150 | |
}, | |
{ | |
letter: 'Y', | |
frequency: .01974 | |
}, | |
{ | |
letter: 'Z', | |
frequency: .00074 | |
} | |
]; | |
barChart = graphs.barChart(); | |
$('body').append($('<div class="test-container"></div>')); | |
containerFixture = d3.select('.test-container'); | |
containerFixture.datum(dataset).call(barChart); | |
}); | |
afterEach(function() { | |
containerFixture.remove(); | |
}); | |
it('should render a chart with minimal requirements', function() { | |
expect(containerFixture.select('.chart')).toBeDefined(1); | |
}); | |
it('should render container, axis and chart groups', function() { | |
expect(containerFixture.select('g.container-group')[0][0]).not.toBeNull(); | |
expect(containerFixture.select('g.chart-group')[0][0]).not.toBeNull(); | |
expect(containerFixture.select('g.x-axis-group')[0][0]).not.toBeNull(); | |
expect(containerFixture.select('g.y-axis-group')[0][0]).not.toBeNull(); | |
}); | |
it('should render an X and Y axis', function() { | |
expect(containerFixture.select('.x.axis')[0][0]).not.toBeNull(); | |
expect(containerFixture.select('.y.axis')[0][0]).not.toBeNull(); | |
}); | |
it('should render a bar for each data entry', function() { | |
var numBars = dataset.length; | |
expect(containerFixture.selectAll('.bar')[0].length).toEqual(numBars); | |
}); | |
it('should provide margin getter and setter', function() { | |
var defaultMargin = barChart.margin(), | |
testMargin = {top: 4, right: 4, bottom: 4, left: 4}, | |
newMargin; | |
barChart.margin(testMargin); | |
newMargin = barChart.margin(); | |
expect(defaultMargin).not.toBe(testMargin); | |
expect(newMargin).toBe(testMargin); | |
}); | |
it('should provide width getter and setter', function() { | |
var defaultWidth = barChart.width(), | |
testWidth = 200, | |
newWidth; | |
barChart.width(testWidth); | |
newWidth = barChart.width(); | |
expect(defaultWidth).not.toBe(testWidth); | |
expect(newWidth).toBe(testWidth); | |
}); | |
it('should provide height getter and setter', function() { | |
var defaultHeight = barChart.height(), | |
testHeight = 200, | |
newHeight; | |
barChart.height(testHeight); | |
newHeight = barChart.height(); | |
expect(defaultHeight).not.toBe(testHeight); | |
expect(newHeight).toBe(testHeight); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@import url("//fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,700italic,400,300,700"); | |
body { | |
font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Helvetica, Arial, sans-serif; | |
} | |
.block-title { | |
color: #222; | |
font-size: 44px; | |
font-style: normal; | |
font-weight: 300; | |
text-rendering: optimizelegibility; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE HTML> | |
<html lang="en-US"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Jasmine Spec Runner</title> | |
<link rel="stylesheet" type="text/css" href="http://cdnjs.cloudflare.com/ajax/libs/jasmine/2.2.0/jasmine.css"> | |
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script> | |
<script src="http://cdnjs.cloudflare.com/ajax/libs/jasmine/2.2.0/jasmine.js"></script> | |
<script src="http://cdnjs.cloudflare.com/ajax/libs/jasmine/2.2.0/jasmine-html.js"></script> | |
<script src="http://cdnjs.cloudflare.com/ajax/libs/jasmine/2.2.0/boot.js"></script> | |
<!-- Favicon --> | |
<link rel="shortcut icon" type="image/png" href="http://cdnjs.cloudflare.com/ajax/libs/jasmine/2.0.0/jasmine_favicon.png" /> | |
<!-- End Favicon --> | |
<!-- source files... --> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script src="dataManager.js"></script> | |
<script src="src.js"></script> | |
<!-- spec files... --> | |
<script src="src.spec.js"></script> | |
</head> | |
<body> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment