D3.js Dynamic Donut Chart

Pull some data and draw a donut and a table

by k_sav

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>

<div class="c-donut  js-donut" data-donut-data='[{"fund":"AUSTRALIAN sHaRes","rate":17,"range":"10-20"},{"fund":"international shares","rate":22,"range":"20-30"},{"fund":"direct property","rate":8,"range":"5-10"},{"fund":"infrastructure","rate":6,"range":"2-10"},{"fund":"private equity","rate":1.5,"range":"1-5"},{"fund":"credit","rate":12,"range":"10-15"},{"fund":"fixed interest","rate":22,"range":"15-25"},{"fund":"cash","rate":11.5,"range":"10-15"},{"fund":"other assets","rate":0,"range":"0-2"}]'>
  
  <h4 class="c-donut-header">Investment breakdown</h4>
  <div class="row">
    <div class="col-md-4  offset-md-1  js-donut-holder">
    </div>
    <div class="col-md-6  offset-md-1  js-legend-holder"></div>
  </div>
  
  
</div>

CSS

html {
  font-size: 62.5%;
}

body {
  margin: 0;
  padding: 0;
  width: 100%;
  font-size: 1.6rem;
}

svg {
  width: 100%;
}

.c-tooltip {
  width: 9rem;
  position: absolute;
  padding: 1rem;
  font: 12px sans-serif;
  border: 1px solid #616161;
  border-radius: .8rem;
  pointer-events: none;
  z-index: 10;
  background: white;
  line-height: 1.4;
  margin-top: -2.3rem;
  margin-left: 2rem;
}

.c-tooltip--right {
  margin-left: -13rem;
}

.c-tooltip-color {
  display: inline-block;
  vertical-align: baseline;
  width: .4rem;
  height: 1.2rem;
  margin-right: .3rem;
}

.tip {
  width: 0;
  height: 0;
  position: absolute;
  left: -2rem;
  top: 1.1rem;
  border: solid 1rem;
  border-color: transparent #616161 transparent transparent;
}

.c-tooltip--right .tip {
  left: auto;
  right: -2rem;
  border-color: transparent transparent transparent #616161;
}

.tip:after {
  content: ' ';
  width: 0;
  height: 0;
  position: absolute;
  left: -9px;
  top: -10px;
  border: solid 10px;
  border-color: transparent #fff transparent transparent;
}

.c-tooltip--right .tip:after {
  left: auto;
  right: -.9rem;
  border-color: transparent transparent transparent #fff;
}

.c-tooltip p {
  margin: 0;
}

.c-donut__legend-color {
  display: inline-block;
  width: 2rem;
  height: 2rem;
  margin-right: 1rem;
  border-radius: 50%;
}

.c-donut__legend-item:hover {
  cursor: default;
}

.c-donut__legend-item--highlight {
  background: #eee;
}

JavaScript

const CLASSES = {
  COMPONENT: 'js-donut',
  legend: 'c-donut__legend',
  donutGroup: 'c-donut__group',
  donutArc: 'c-donut__arc',
  legendItem: 'c-donut__legend-item',
  legendItemHighlight: 'c-donut__legend-item--highlight',
  legendColor: 'c-donut__legend-color',
  tooltip: 'c-tooltip',
  tooltipTip: 'tip',
  tooltipRight: 'c-tooltip--right',
  tooltipColor: 'c-tooltip-color',
}

const SELECTORS = {
  COMPONENT: `.${CLASSES.COMPONENT}`,
  donutArc: `.${CLASSES.arc}`,
  legendHolder: '.js-legend-holder',
  donutHolder: '.js-donut-holder',
}

const SETTINGS = {
  margin: {
    top: 0,
    right: 0,
    bottom: 0,
    left: 0,
  },
  breakpoints: {
    mobile: 320,
    tablet: 740,
  },
  colors: {
    bg: '#ffffff',
    funds: {
      'australian-shares': '#ef7f00',
      'international-shares': '#d0d0ce',
      'cash': '#bc032a',
      'direct-property': '#00a4e7',
      'infrastructure': '#f36205',
      'private-equity': '#c9520d',
      'credit': '#e1edfa',
      'fixed-interest': '#c0dbf3',
      'other-assets': '#f14432',
    },
  },
  arc: {
    outerRadius: 0.99,
    innerRadius: 0.65,
  },
}

function getWindowWidth() {
  const width = document.getElementsByClassName(CLASSES.COMPONENT)[0].clientWidth
  return width
}

function getRadius() {
  return getWindowWidth() / 2
}

function isMobileVersion() {
  if (getWindowWidth() < SETTINGS.breakpoints.tablet) {
    //console.log('is mobile')
    return true
  }
  //console.log('is not mobile')
  return false
}


// Calculate arcs
const arc = d3.svg.arc()
  .outerRadius(getRadius() * SETTINGS.arc.outerRadius)
  .innerRadius(getRadius() * SETTINGS.arc.innerRadius)

// Calculate donut
const cookDonut = d3.layout.pie()
  .sort(null)
  .value(function(d) {
    return d.rate;
  })



// Define our main component div
const component = d3.select(SELECTORS.COMPONENT)

// Define SVG element, append to col1
const svg = d3.select(SELECTORS.donutHolder).append('svg')
  .attr('preserveAspectRatio', 'xMinYMin')

// Define...