clientjs/packages/dashboard-components/src/ui/RowChart.jsx
changeset 0 5f4fcbc80b37
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/clientjs/packages/dashboard-components/src/ui/RowChart.jsx	Fri Sep 14 17:57:34 2018 +0200
@@ -0,0 +1,122 @@
+/* eslint func-names: "off" */
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import * as d3 from 'd3';
+import { withRouter } from 'react-router-dom';
+
+import './RowChart.scss';
+
+
+/**
+ * TODO: As a first implementation we have chosen to delagate the barchart DOM drawing to D3.
+ * This may not be the optimum solution. This may have to be rewrittten to use
+ * React for elementt creation and D3 as the visualisation kernel.
+ */
+class RowChart extends Component {
+  static propTypes = {
+    data: PropTypes.PropTypes.arrayOf(PropTypes.object).isRequired,
+    tagPrefix: PropTypes.string.isRequired,
+    history: PropTypes.object.isRequired,
+  }
+
+  componentDidMount() {
+    this.drawChart();
+  }
+
+  componentDidUpdate() {
+    this.drawChart();
+  }
+
+  drawChart() {
+    const { node } = this;
+    const { data, tagPrefix } = this.props;
+
+    const width = 800;
+    const barHeight = 30;
+    const height = barHeight * data.length;
+    const valueMargin = 4;
+    const yAxisHMargin = 10;
+    const yAxisVMargin = 10;
+    const barVMargin = 10;
+
+    const y = d3.scaleBand()
+      .rangeRound([0, height])
+      .padding(0.1)
+      .domain(data.map(d => d.tag));
+
+    const yAxis = d3.axisLeft(y);
+
+    d3.select(node).selectAll('svg').remove();
+    const chart = d3.select(node)
+      .append('svg')
+      .attr('width', '100%');
+
+    const barsContainer = chart.append('g')
+      .attr('class', 'bars-container');
+
+    const yAxisContainer = chart.append('g')
+      .attr('class', 'y-axis axis')
+      .call(yAxis);
+
+    let yAxisWidth = 0;
+    chart.selectAll('.y-axis text')
+      .each(function () { yAxisWidth = Math.max(yAxisWidth, this.getBBox().width); });
+
+    const x = d3.scaleLinear()
+      .range([0, width - yAxisWidth - yAxisHMargin])
+      .domain([0, d3.max(data, d => d.count)]);
+
+    const xAxis = d3.axisBottom(x);
+
+    const xAxisContainer = chart.append('g')
+      .attr('class', 'y-axis axis')
+      .attr('transform', `translate(${[yAxisWidth + 2 * yAxisHMargin, height + yAxisVMargin]})`)
+      .call(xAxis);
+
+    const bar = barsContainer.selectAll('.bars-container')
+      .data(data)
+      .enter().append('g')
+      .attr('transform', (d, i) => `translate(0,${i * barHeight})`)
+      .attr('class', 'graph-bar');
+
+    bar.append('rect')
+      .attr('width', d => x(d.count))
+      .attr('height', barHeight - barVMargin)
+      .attr('fill', d => d.color)
+      .attr('stroke', d => d.color)
+      .attr('transform', 'translate(0,5)')
+      .on('click', (d) => {
+        const { history } = this.props;
+        history.push(`/annotations/${tagPrefix}${d.tag}`);
+      });
+
+    bar.append('text')
+      .attr('y', barHeight / 2)
+      .attr('dy', '.35em')
+      .attr('dx', -valueMargin)
+      .attr('text-anchor', 'end')
+      .text(d => d.count)
+      .attr('x', function (d) {
+        const xWidth = this.getBBox().width;
+        return Math.max(xWidth + valueMargin, x(d.count));
+      });
+
+    yAxisContainer.attr('transform', `translate(${yAxisWidth + yAxisHMargin},0)`);
+    barsContainer.attr('transform', `translate(${yAxisWidth + 2 * yAxisHMargin},0)`);
+
+    const totalHeight = height + xAxisContainer.node().getBBox().height + yAxisVMargin;
+    const totalWidth = width + 2 * yAxisHMargin;
+
+    chart.attr('viewBox', [0, 0, totalWidth, totalHeight].join(' '));
+  }
+
+  render() {
+    return (
+      <div className="row chart-container">
+        <div className="col col-12" ref={(elt) => { this.node = elt; }} />
+      </div>
+    );
+  }
+}
+
+export default withRouter(RowChart);