Files
Nicholas Carrigan (he/him) b36cdbafd1 chore: rename "part" to "step" (#43934)
* chore: rename part to step

* chore: update metas

* chore: more renaming

* chore: update tooling

* chore: update frontmatter

* chore(tools): title testing
2021-10-21 18:07:52 +01:00

14 KiB

id, title, challengeType, dashedName
id title challengeType dashedName
5d8a4cfbe6b6180ed9a1ca71 Step 145 0 step-145

--description--

Change the fill of the tumblr-circles and instagram-circles to use a "d function" that returns their respective color variables when d.year equals year, leave it white when they don't. This is similar to how you set the fill of the Twitter circles.

Then, all of the circles will get filled in for the currently displayed year.

--hints--

test-text

const circles = Object.values($('.dashboard svg circle'));
assert(
  circles.filter(
    (el) =>
      el.getAttribute &&
      (el.getAttribute('fill') === '#7cd9d1' ||
        el.getAttribute('fill') === '#f6dd71' ||
        el.getAttribute('fill') === '#fd9b98')
  ).length === 3
);

--seed--

--before-user-code--

<!DOCTYPE html>
<html>
  <head>
    <title>D3 Dashboard</title>
    <style>
      body {
        background-color: #ccc;
        padding: 100px 10px;
      }

      .dashboard {
        width: 980px;
        height: 500px;
        background-color: white;
        box-shadow: 5px 5px 5px 5px #888;
        margin: auto;
        display: flex;
        align-items: center;
      }
    </style>
  </head>

  <body>
    <div class="dashboard"></div>
  </body>
</html>

--seed-contents--

<script>
  const data = [ 
    { year: 2012, followers: { twitter: 2594, tumblr:  401, instagram:   83 }},
    { year: 2013, followers: { twitter: 3049, tumblr:  440, instagram:  192 }},
    { year: 2014, followers: { twitter: 3511, tumblr:  415, instagram:  511 }},
    { year: 2015, followers: { twitter: 3619, tumblr:  492, instagram: 1014 }},
    { year: 2016, followers: { twitter: 4046, tumblr:  543, instagram: 2066 }},
    { year: 2017, followers: { twitter: 3991, tumblr:  701, instagram: 3032 }},
    { year: 2018, followers: { twitter: 3512, tumblr: 1522, instagram: 4512 }},
    { year: 2019, followers: { twitter: 3274, tumblr: 1989, instagram: 4715 }},
    { year: 2020, followers: { twitter: 2845, tumblr: 2040, instagram: 4801 }}
  ];
</script>
<script>
  function drawDashboard(year) {
    d3.select('.dashboard').html('');
    const index = data.findIndex(d => d.year === year);

    const svgMargin = 70,
      svgWidth = 700,
      svgHeight = 500,
      twitterColor = '#7cd9d1',
      tumblrColor = '#f6dd71',
      instagramColor = '#fd9b98';

    const lineGraph = d3.select('.dashboard')
      .append('svg')
      .attr('width', svgWidth)
      .attr('height', svgHeight);

    const yScale = d3.scaleLinear()
      .domain([0, 5000])
      .range([svgHeight - svgMargin, svgMargin]);

    const xScale = d3.scaleLinear()
      .domain([2012, 2020])
      .range([svgMargin, svgWidth - svgMargin]);

    const yAxis = d3.axisLeft(yScale)
      .ticks(6, '~s');

    const xAxis = d3.axisBottom(xScale)
      .tickFormat(d3.format(''))
      .tickPadding(10);

    lineGraph.append('g')
      .call(yAxis)
      .attr('transform', `translate(${svgMargin}, 0)`)
      .style('font', '10px verdana');

    lineGraph.append('g')
      .call(xAxis)
      .attr('transform', `translate(0, ${svgHeight - svgMargin})`)
      .selectAll('text')
      .style('transform', 'translate(-12px, 0) rotate(-50deg)')
      .style('text-anchor', 'end')
      .style('cursor', 'pointer')
      .style('font', d => d === year ? 'bold 10px verdana' : '10px verdana')
      .on('mouseover', d => drawDashboard(d));

    const twitterLine = d3.line()
      .x(d => xScale(d.year))
      .y(d => yScale(d.followers.twitter));

    lineGraph.append('path')
      .attr('d', twitterLine(data))
      .attr('stroke', twitterColor)
      .attr('stroke-width', 3)
      .attr('fill', 'transparent');

    const tumblrLine = d3.line()
      .x(d => xScale(d.year))
      .y(d => yScale(d.followers.tumblr));

    lineGraph.append('path')
      .attr('d', tumblrLine(data))
      .attr('stroke', tumblrColor)
      .attr('stroke-width', 3)
      .attr('fill', 'transparent');

    const instagramLine = d3.line()
      .x(d => xScale(d.year))
      .y(d => yScale(d.followers.instagram));

    lineGraph.append('path')
      .attr('d', instagramLine(data))
      .attr('stroke', instagramColor)
      .attr('stroke-width', 3)
      .attr('fill', 'transparent');
      
    lineGraph.selectAll('twitter-circles')
      .data(data)
      .enter()
      .append('circle')
      .attr('cx', d => xScale(d.year))
      .attr('cy', d => yScale(d.followers.twitter))
      .attr('r', 6)
      .attr('fill', d => d.year === year ? twitterColor : 'white')
      .attr('stroke', twitterColor)
      .style('cursor', 'pointer')
      .on('mouseover', d => drawDashboard(d.year));

    lineGraph.selectAll('tumblr-circles')
      .data(data)
      .enter()
      .append('circle')
      .attr('cx', d => xScale(d.year))
      .attr('cy', d => yScale(d.followers.tumblr))
      .attr('r', 6)
      .attr('fill', 'white')


      .attr('stroke', tumblrColor)
      .style('cursor', 'pointer')
      .on('mouseover', d => drawDashboard(d.year));

    lineGraph.selectAll('instagram-circles')
      .data(data)
      .enter()
      .append('circle')
      .attr('cx', d => xScale(d.year))
      .attr('cy', d => yScale(d.followers.instagram))
      .attr('r', 6)
      .attr('fill', 'white')


      .attr('stroke', instagramColor)
      .style('cursor', 'pointer')
      .on('mouseover', d => drawDashboard(d.year));

    const rightDashboard = d3.select('.dashboard')
      .append('div');

    const pieGraph = rightDashboard.append('svg')
      .attr('width', 200)
      .attr('height', 200)
      .style('position', 'relative')
      .style('left', '20px');

    const pieArc = d3.arc()
      .outerRadius(100)
      .innerRadius(0);

    const pieColors = d3.scaleOrdinal()  
      .domain(data[index].followers)
      .range([twitterColor, tumblrColor, instagramColor]);

    const pie = d3.pie()
      .value(d => d.value);
      
    const pieGraphData = pieGraph.selectAll('pieSlices')
      .data(pie(d3.entries(data[index].followers)))
      .enter()
      .append('g')
      .attr('transform', 'translate(100, 100)');

    pieGraphData.append('path')
      .attr('d', pieArc)
      .attr('fill', d => pieColors(d.data.key))
      .attr('stroke', 'white')
      .attr('stroke-width', 2);

    pieGraphData.append('text')
      .text(d => {
        const values = d3.values(data[index].followers);
        const sum = d3.sum(values);
        const percent = d.data.value/sum;
        return `${ Math.round(percent*100) }%`;
      })
      .attr('transform', d => `translate(${pieArc.centroid(d)})`)
      .style('text-anchor', 'middle')
      .style('font', '10px verdana');

    const legend = rightDashboard.append('table')
      .attr('width', 200)
      .attr('height', 120)
      .style('font', '12px verdana')
      .style('position', 'relative')
      .style('top', '30px');

    const legendTitle = legend.append('thead')
      .append('tr')
      .append('th')
      .text('2020 followers')
      .attr('colspan', 3)
      .style('position', 'relative')
      .style('left', '20px');

    const legendRows = legend.append('tbody')
      .selectAll('tr')
      .data(d3.entries(data[index].followers))
      .enter()
      .append('tr');

    legendRows.append('td')  
      .text(d => d.key)
      .attr('align', 'right');

    legendRows.append('td')
      .attr('align', 'center')
      .append('div')
      .style('width', '16px')
      .style('height', '16px')
      .style('background-color', d => pieColors(d.key))

    legendRows.append('td')
      .text(d => d.value)
      .attr('align', 'left');
  }

  drawDashboard(2020);
</script>

--solutions--

<script>
  const data = [ 
    { year: 2012, followers: { twitter: 2594, tumblr:  401, instagram:   83 }},
    { year: 2013, followers: { twitter: 3049, tumblr:  440, instagram:  192 }},
    { year: 2014, followers: { twitter: 3511, tumblr:  415, instagram:  511 }},
    { year: 2015, followers: { twitter: 3619, tumblr:  492, instagram: 1014 }},
    { year: 2016, followers: { twitter: 4046, tumblr:  543, instagram: 2066 }},
    { year: 2017, followers: { twitter: 3991, tumblr:  701, instagram: 3032 }},
    { year: 2018, followers: { twitter: 3512, tumblr: 1522, instagram: 4512 }},
    { year: 2019, followers: { twitter: 3274, tumblr: 1989, instagram: 4715 }},
    { year: 2020, followers: { twitter: 2845, tumblr: 2040, instagram: 4801 }}
  ];
</script>
<script>
  function drawDashboard(year) {
    d3.select('.dashboard').html('');
    const index = data.findIndex(d => d.year === year);

    const svgMargin = 70,
      svgWidth = 700,
      svgHeight = 500,
      twitterColor = '#7cd9d1',
      tumblrColor = '#f6dd71',
      instagramColor = '#fd9b98';

    const lineGraph = d3.select('.dashboard')
      .append('svg')
      .attr('width', svgWidth)
      .attr('height', svgHeight);

    const yScale = d3.scaleLinear()
      .domain([0, 5000])
      .range([svgHeight - svgMargin, svgMargin]);

    const xScale = d3.scaleLinear()
      .domain([2012, 2020])
      .range([svgMargin, svgWidth - svgMargin]);

    const yAxis = d3.axisLeft(yScale)
      .ticks(6, '~s');

    const xAxis = d3.axisBottom(xScale)
      .tickFormat(d3.format(''))
      .tickPadding(10);

    lineGraph.append('g')
      .call(yAxis)
      .attr('transform', `translate(${svgMargin}, 0)`)
      .style('font', '10px verdana');

    lineGraph.append('g')
      .call(xAxis)
      .attr('transform', `translate(0, ${svgHeight - svgMargin})`)
      .selectAll('text')
      .style('transform', 'translate(-12px, 0) rotate(-50deg)')
      .style('text-anchor', 'end')
      .style('cursor', 'pointer')
      .style('font', d => d === year ? 'bold 10px verdana' : '10px verdana')
      .on('mouseover', d => drawDashboard(d));

    const twitterLine = d3.line()
      .x(d => xScale(d.year))
      .y(d => yScale(d.followers.twitter));

    lineGraph.append('path')
      .attr('d', twitterLine(data))
      .attr('stroke', twitterColor)
      .attr('stroke-width', 3)
      .attr('fill', 'transparent');

    const tumblrLine = d3.line()
      .x(d => xScale(d.year))
      .y(d => yScale(d.followers.tumblr));

    lineGraph.append('path')
      .attr('d', tumblrLine(data))
      .attr('stroke', tumblrColor)
      .attr('stroke-width', 3)
      .attr('fill', 'transparent');

    const instagramLine = d3.line()
      .x(d => xScale(d.year))
      .y(d => yScale(d.followers.instagram));

    lineGraph.append('path')
      .attr('d', instagramLine(data))
      .attr('stroke', instagramColor)
      .attr('stroke-width', 3)
      .attr('fill', 'transparent');
      
    lineGraph.selectAll('twitter-circles')
      .data(data)
      .enter()
      .append('circle')
      .attr('cx', d => xScale(d.year))
      .attr('cy', d => yScale(d.followers.twitter))
      .attr('r', 6)
      .attr('fill', d => d.year === year ? twitterColor : 'white')
      .attr('stroke', twitterColor)
      .style('cursor', 'pointer')
      .on('mouseover', d => drawDashboard(d.year));

    lineGraph.selectAll('tumblr-circles')
      .data(data)
      .enter()
      .append('circle')
      .attr('cx', d => xScale(d.year))
      .attr('cy', d => yScale(d.followers.tumblr))
      .attr('r', 6)
      .attr('fill', d => d.year === year ? tumblrColor : 'white')
      .attr('stroke', tumblrColor)
      .style('cursor', 'pointer')
      .on('mouseover', d => drawDashboard(d.year));

    lineGraph.selectAll('instagram-circles')
      .data(data)
      .enter()
      .append('circle')
      .attr('cx', d => xScale(d.year))
      .attr('cy', d => yScale(d.followers.instagram))
      .attr('r', 6)
      .attr('fill', d => d.year === year ? instagramColor : 'white')
      .attr('stroke', instagramColor)
      .style('cursor', 'pointer')
      .on('mouseover', d => drawDashboard(d.year));

    const rightDashboard = d3.select('.dashboard')
      .append('div');

    const pieGraph = rightDashboard.append('svg')
      .attr('width', 200)
      .attr('height', 200)
      .style('position', 'relative')
      .style('left', '20px');

    const pieArc = d3.arc()
      .outerRadius(100)
      .innerRadius(0);

    const pieColors = d3.scaleOrdinal()  
      .domain(data[index].followers)
      .range([twitterColor, tumblrColor, instagramColor]);

    const pie = d3.pie()
      .value(d => d.value);
      
    const pieGraphData = pieGraph.selectAll('pieSlices')
      .data(pie(d3.entries(data[index].followers)))
      .enter()
      .append('g')
      .attr('transform', 'translate(100, 100)');

    pieGraphData.append('path')
      .attr('d', pieArc)
      .attr('fill', d => pieColors(d.data.key))
      .attr('stroke', 'white')
      .attr('stroke-width', 2);

    pieGraphData.append('text')
      .text(d => {
        const values = d3.values(data[index].followers);
        const sum = d3.sum(values);
        const percent = d.data.value/sum;
        return `${ Math.round(percent*100) }%`;
      })
      .attr('transform', d => `translate(${pieArc.centroid(d)})`)
      .style('text-anchor', 'middle')
      .style('font', '10px verdana');

    const legend = rightDashboard.append('table')
      .attr('width', 200)
      .attr('height', 120)
      .style('font', '12px verdana')
      .style('position', 'relative')
      .style('top', '30px');

    const legendTitle = legend.append('thead')
      .append('tr')
      .append('th')
      .text('2020 followers')


      .attr('colspan', 3)
      .style('position', 'relative')
      .style('left', '20px');

    const legendRows = legend.append('tbody')
      .selectAll('tr')
      .data(d3.entries(data[index].followers))
      .enter()
      .append('tr');

    legendRows.append('td')  
      .text(d => d.key)
      .attr('align', 'right');

    legendRows.append('td')
      .attr('align', 'center')
      .append('div')
      .style('width', '16px')
      .style('height', '16px')
      .style('background-color', d => pieColors(d.key))

    legendRows.append('td')
      .text(d => d.value)
      .attr('align', 'left');
  }

  drawDashboard(2020);
</script>