Exercise 1: Build a component chart

👉 exercise1/js/Chart/index.js

Add some defaults

defaultData = [60, 40, 20]
defaultProps = {
  stroke: '#ccc',
  strokeWidth: '2px',
  fill: 'steelblue',
}

Getter-setter methods

selection(selector) {
  if (!selector) return this._selection;

  this._selection = d3.select(selector);
  return this;
}
data(arr) {
  if (!arr) return this._data || this.defaultData;

  this._data = arr;
  return this;
}
props(obj) {
  if (!obj) return this._props || this.defaultProps;

  this._props = merge(this._props || this.defaultProps, obj);
  return this;
}

Draw method

draw() {
    const data = this.data();
    const props = this.props();

    console.log('My chart DATA', data);
    console.log('My chart PROPS', props);
}

👉 exercise1/js/Container/index.js

Import chart:

import Chart from '../Chart';

Attach new instance to component:

chart = new Chart()

Change the render method:

render() {
  return (
    <div id='chart' />
  );
}

Call chart in lifecycle method:

componentDidMount() {
  this.chart
    .selection('#chart')
    .draw();
}

👀 Check your console!

Add some additional calls:

setTimeout(() => {
  this.chart
    .data([20, 34, 48, 60])
    .props({ fill: '#F86F00' })
    .draw();
}, 1000);

setTimeout(() => {
  this.chart
    .data([30, 50, 30])
    .props({ fill: '#007BC7' })
    .draw();
}, 2000);

👉 exercise1/js/Chart/index.js

Swap out your draw function for a real chart:

draw() {
  const data = this.data();
  const props = this.props();

  const node = this.selection().node();
  const { width, height } = node.getBoundingClientRect();
  const t = d3.transition()
    .duration(750);

  const g = this.selection()
      .appendSelect('svg')
      .attr('width', width)
      .attr('height', height)
      .appendSelect('g')
      .attr('transform', 'translate(' + (width / 2 - 62) + ', 60)');

  const circles = g.selectAll('circle')
      .data(data, (d, i) => i);

  circles
    .style('fill', props.fill)
    .style('stroke', props.stroke);

  circles.enter().append('circle')
    .style('fill', props.fill)
    .style('stroke', props.stroke)
    .style('stroke-width', props.strokeWidth)
    .attr('cy', '0')
    .attr('cx', (d, i) =>
      data.slice(0, i).reduce((a, b) => a + b, 0) + (d / 2)
    )
    .merge(circles)
    .transition(t)
    .attr('cx', (d, i) =>
      data.slice(0, i).reduce((a, b) => a + b, 0) + (d / 2)
    )
    .attr('r', d => d / 2);

  circles.exit()
    .transition(t)
    .attr('r', 0)
    .remove();
}