Is there an example for dc.js to react conversion? Interested in line chart,table and timeSlider (bar chart with brush).
Any help would be appreciated.
Thanks!
Is there an example for dc.js to react conversion? Interested in line chart,table and timeSlider (bar chart with brush).
Any help would be appreciated.
Thanks!
This is the begining of converting DC.js into React. The versions of the used library are as followed (package.json):
"dependencies": {
"babel-polyfill": "^6.3.14",
"bootstrap": "^3.3.6",
"crossfilter": "^1.3.12",
"d3": "^4.2.8",
"d3-request": "^1.0.2",
"d3-scale": "^1.0.3",
"d3-time-format": "^2.0.2",
"dc": "^2.0.0-beta.32",
"history": "^1.17.0",
"isomorphic-fetch": "^2.2.0",
"keymirror": "^0.1.0",
"react": "^0.14.8",
"react-addons-perf": "^0.14.0",
"react-bootstrap": "^0.29.5",
"react-bootstrap-table": "^2.3.7",
"react-d3-core": "^1.3.9",
"react-dom": "^0.14.8",
"react-redux": "^4.2.1",
"react-router": "^2.0.0",
"react-router-bootstrap": "^0.19.3",
"react-router-redux": "^4.0.0-rc.1",
"redux": "^3.2.1",
"redux-form": "^5.3.1",
"redux-logger": "^2.4.0",
"redux-thunk": "^1.0.3"
},
"devDependencies": {
"babel-core": "^6.17.0",
"babel-loader": "^6.2.5",
"babel-preset-es2015": "^6.16.0",
"babel-preset-react": "^6.3.13",
"babel-preset-react-hmre": "^1.1.1",
"babel-preset-stage-0": "^6.16.0",
"css-loader": "^0.12.1",
"expect": "^1.6.0",
"express": "^4.13.3",
"express-history-api-fallback": "^2.0.0",
"file-loader": "^0.8.1",
"node-libs-browser": "^0.5.2",
"style-loader": "^0.13.1",
"url-loader": "^0.5.7",
"webpack": "^1.13.2",
"webpack-dev-middleware": "^1.2.0",
"webpack-hot-middleware": "^2.9.1"
}
Notice that the current d3 version is 4.2.8 which implied changes in the dc.js code. Line.js is a component. In the index.html what's important is the style tag.
//////Line.js
import React, { PropTypes } from 'react';
import ReactDOM from "react-dom";
import * as d3 from 'd3';
import dc from "dc";
import * as crossfilter from 'crossfilter';
import {Jumbotron } from 'react-bootstrap';
import functionDCHelper from './functionDCHelper';
import {scaleTime, scaleLinear} from 'd3-scale';
import { axisLeft, axisBottom } from 'd3-axis';
import TableChart from "../components/TableChart.js";
class LineChart extends React.Component {
componentDidMount() {
var bitrateLineChart = dc.compositeChart(this.refs.lineChart);
var { min15, minDate, maxDate, bitrateWeekMinIntervalGroupMove, maxbit } = functionDCHelper.generateValues(this.props.data);
bitrateLineChart
.xUnits(min15.range)
.x(d3.scaleTime().domain([new Date(minDate), new Date(maxDate)]))
.yAxisPadding('5%')
.elasticY(true)
.width(990)
.height(200)
.transitionDuration(500)
.margins({ top: 30, right: 50, bottom: 25, left: 50, padding: 1 })
.mouseZoomable(true)
.brushOn(false)
.renderHorizontalGridLines(true)
.legend(dc.legend().x(800).y(10).itemHeight(13).gap(5))
//Render max bitrate horizontal line copied from bar-extra-line.html
.yAxisLabel("Total Bitrate per 15 minutes")
.on('renderlet', function (chart) {
chart.svg().selectAll('.chart-body').attr('clip-path', null)
var left_y = 10, right_y = 70; // use real statistics here!
var extra_data = [{ x: chart.x().range()[0], y: chart.y()(left_y) }, { x: chart.x().range()[1], y: chart.y()(right_y) }];
var line = d3.line()
.x(function (d) { return d.x; })
.y(function (d) { return maxbit; })
.curve(d3.curveLinear); //.interpolate('linear');
var chartBody = chart.select('g.chart-body');
var path = chartBody.selectAll('path.extra').data([extra_data]);
path.enter().append('path').attr({
class: 'extra',
stroke: 'red',
id: 'extra-line',
});
path.attr('d', line);
// Label the max line
var text = chartBody.selectAll('text.extra-label').data([0]);
text.enter().append('text')
.attr('text-anchor', 'middle')
.append('textPath').attr({
class: 'extra-label',
'xlink:href': '#extra-line',
startOffset: '50%'
})
.text('Total Bitrate Max Value');
})
// Title can be called by any stack layer.
.title(function (d) {
var value = d.value.total ? d.value.total : d.value;
if (isNaN(value)) {
value = 0;
}
return functionDCHelper.dateFormat(d.key) + ' \n Total Bit:' + functionDCHelper.numberFormat(value)
})
//Creating dynamic Y axis with min max ticks' values depending on min max of data - copied from http://jsfiddle.net/gordonwoodhull/7anae5c5/1/
.compose([
functionDCHelper.nonzero_min(dc.lineChart(bitrateLineChart)
.dimension(min15)
.colors('green')
.group(bitrateWeekMinIntervalGroupMove, 'Bitrate Total')
.valueAccessor(function (d) {
return d.value.total;
})
// .dashStyle([2,2])
.interpolate('d3.curveStepAfter')
.renderArea(false)
.brushOn(false)
.renderDataPoints(false)
.clipPadding(10)),
])
bitrateLineChart.render();
}
render() {
return(
<div ref="lineChart">
</div>
);
}
}
export default LineChart;
//////functionDCHelper.js
import crossfilter from 'crossfilter';
import * as d3 from 'd3';
import dc from 'dc';
var minDate,min15,bitrateWeekMinIntervalGroupMove,maxDate,minIntervalWeekBitrateGroup,dateDimension,dateFormat,numberFormat,maxbit;
function nonzero_min(chart) {
dc.override(chart, 'yAxisMin', function () {
var min = d3.min(chart.data(), function (layer) {
return d3.min(layer.values, function (p) {
return p.y + p.y0;
});
});
return dc.utils.subtract(min, chart.yAxisPadding());
});
return chart;
}
// 15 Min Interval - copied from https://github.com/mbostock/d3/blob/master/src/time/interval.js
var d3_date = Date;
function d3_time_interval(local, step, number) {
function round(date) {
var d0 = local(date), d1 = offset(d0, 1);
return date - d0 < d1 - date ? d0 : d1;
}
function ceil(date) {
step(date = local(new d3_date(date - 1)), 1);
return date;
}
function offset(date, k) {
step(date = new d3_date(+date), k);
return date;
}
function range(t0, t1, dt) {
var time = ceil(t0), times = [];
if (dt > 1) {
while (time < t1) {
if (!(number(time) % dt)) times.push(new Date(+time));
step(time, 1);
}
} else {
while (time < t1) times.push(new Date(+time)), step(time, 1);
}
return times;
}
function range_utc(t0, t1, dt) {
try {
d3_date = d3_date_utc;
var utc = new d3_date_utc();
utc._ = t0;
return range(utc, t1, dt);
} finally {
d3_date = Date;
}
}
local.floor = local;
local.round = round;
local.ceil = ceil;
local.offset = offset;
local.range = range;
var utc = local.utc = d3_time_interval_utc(local);
utc.floor = utc;
utc.round = d3_time_interval_utc(round);
utc.ceil = d3_time_interval_utc(ceil);
utc.offset = d3_time_interval_utc(offset);
utc.range = range_utc;
return local;
}
function d3_time_interval_utc(method) {
return function (date, k) {
try {
d3_date = d3_date_utc;
var utc = new d3_date_utc();
utc._ = date;
return method(utc, k)._;
} finally {
d3_date = Date;
}
};
}
// generalization of d3.time.minute copied from- https://github.com/mbostock/d3/blob/master/src/time/minute.js
function n_minutes_interval(nmins) {
var denom = 6e4 * nmins;
return d3_time_interval(function (date) {
return new d3_date(Math.floor(date / denom) * denom);
}, function (date, offset) {
date.setTime(date.getTime() + Math.floor(offset) * denom); // DST breaks setMinutes
}, function (date) {
return date.getMinutes();
});
}
min15 = n_minutes_interval(15);
dateFormat = d3.timeFormat('%Y/%m/%d/%H:%M');
numberFormat = d3.format('d');
//### Crossfilter Dimensions
function generateValues(data) {
data.forEach(function (d) {
d.bitdate = new Date(d.DATETIME); //d.DATETIME = dateFormat.parse(d.DATETIME);
// d.month = d3.time.month(d.bitdate);
// d.week = d3.time.week(d.bitdate);
d.BITRATE = +d.BITRATE.match(/\d+/); //d.BITRATE = +d.BITRATE;
});
var crossFilteredData = crossfilter(data);
var all = crossFilteredData.groupAll();
// Dimension by full date
dateDimension = crossFilteredData.dimension(function (d) {
return d.bitdate;
});
maxbit = d3.max(data, function (d) { return +d["BITRATE"]; }); //alert(maxbit);
//Group bitrate per week, 15 minInterval - maintain running tallies
bitrateWeekMinIntervalGroupMove = dateDimension.group(min15).reduce(
/* callback for when data is added to the current filter results */
function (p, v) {
++p.count;
p.BITRATE = +v.BITRATE;
p.total += +v.BITRATE;
p.avg = p.count ? Math.round(p.total / p.count) : 0;
return p;
},
/* callback for when data is removed from the current filter results */
function (p, v) {
--p.count;
p.BITRATE = +v.BITRATE;
p.total -= +v.BITRATE;
p.avg = p.count ? Math.round(p.total / p.count) : 0;
return p;
},
/* initialize p */
function () {
return {
count: 0,
bitrate: 0,
total: 0,
avg: 0
};
}
);
try {
minDate = dateDimension.bottom(1)[0].DATETIME;
} catch(err) {
minDate = new Date("2016-06-14 0:00");
}
try {
maxDate = dateDimension.top(1)[0].DATETIME;
} catch(err) {
maxDate = new Date("2016-06-18 23:55");
}
return {
min15, minDate, maxDate, bitrateWeekMinIntervalGroupMove,minIntervalWeekBitrateGroup,dateDimension,maxbit
};
}
export default {
generateValues,
nonzero_min,
dateFormat,
numberFormat
};
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
path.line {
fill: none;
stroke: green;
stroke-width: 1.5px;
}
.y.axis line,
.y.axis path {
fill: none;
stroke: black;
}
.x.axis line,
.x.axis path {
fill: none;
stroke: black;
}
</style>
<title>Line Chart DC.js,React</title>
</head>
<body>
<div id="App"></div>
<script src="/bundle.js"></script>
</body>
</html>
The result:
Line Chart dc.js in React