Categories
React D3

Adding Graphics to a React App with D3 — Line Graph

D3 lets us add graphics to a front-end web app easily.

Vue is a popular front end web framework.

They work great together. In this article, we’ll look at how to add graphics to a Vue app with D3.

Line Graph

We can add a line graph into our React app with D3.

To do this, we write:

public/data.csv

year,population
2006,20
2008,25
2010,38
2012,41
2014,53
2016,26
2017,42

App.js

import React, { useEffect } from "react";
import * as d3 from "d3";

const createLineChart = async () => {
  const margin = { top: 20, right: 20, bottom: 30, left: 50 },
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

  const x = d3.scaleTime().range([0, width]);
  const y = d3.scaleLinear().range([height, 0]);

  const valueline = d3
    .line()
    .x(function (d) {
      return x(d.year);
    })
    .y(function (d) {
      return y(d.population);
    });

  const svg = d3
    .select("body")
    .append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", `translate(${margin.left}, ${margin.top})`);

  const data = await d3.csv("/data.csv");

  data.forEach(function (d) {
    d.population = +d.population;
  });

  x.domain(
    d3.extent(data, function (d) {
      return d.year;
    })
  );

  y.domain([
    0,
    d3.max(data, function (d) {
      return d.population;
    })
  ]);

  svg.append("path").data([data]).attr("class", "line").attr("d", valueline);

  svg
    .append("g")
    .attr("transform", `translate(0, ${height})`)
    .call(d3.axisBottom(x));

svg.append("g").call(d3.axisLeft(y));
};

export default function App() {
  useEffect(() => {
    createLineChart();
  }, []);

  return (
    <div className="App">
      <style>{`
        .line {
          fill: none;
          stroke: green;
          stroke-width: 5px;
        }
      `}</style>
    </div>
  );
}

We create the createLineChart function to create the line chart.

First, we write:

const margin = {
    top: 20,
    right: 20,
    bottom: 30,
    left: 50
  },
  width = 960 - margin.left - margin.right,
  height = 500 - margin.top - margin.bottom;

to set the margins, width, and height of the chart.

Then we add the x and y objects to let us add the min and max values for the lines:

const x = d3.scaleTime().range([0, width]);
const y = d3.scaleLinear().range([height, 0])

Then we set the data for the x and y axes:

const valueline = d3
  .line()
  .x(function(d) {
    return x(d.year);
  })
  .y(function(d) {
    return y(d.population);
  });

Next, we add the svg element into our component with:

const svg = d3
  .select("body")
  .append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform", `translate(${margin.left}, ${margin.top})`);

Then we read the data from the CSV with:

const data = await d3.csv("/data.csv");

Then we add the x and y domains with:

data.forEach(function(d) {
  d.population = +d.population;
});

x.domain(
  d3.extent(data, function(d) {
    return d.year;
  })
);

y.domain([
  0,
  d3.max(data, function(d) {
    return d.population;
  })
]);

to return the labels for the x and y axes.

To add the line, we write:

svg.append("path").data([data]).attr("class", "line").attr("d", valueline);

We add the x-axis with:

svg
  .append("g")
  .attr("transform", `translate(0, ${height})`)
  .call(d3.axisBottom(x));

And we add the y-axis with:

svg.append("g").call(d3.axisLeft(y));

Conclusion

We can add a line graph with D3 into our React app.

Categories
React D3

Adding Graphics to a React App with D3 — Pie Chart

D3 lets us add graphics to a front-end web app easily.

Vue is a popular front end web framework.

They work great together. In this article, we’ll look at how to add graphics to a Vue app with D3.

Pie Chart

We can add a pie chart into our React app with D3.

For instance, we can write:

public/populations.csv

states,percent
California,38.00
New York,18.00
Texas,20.0

src/App.js

import React, { useEffect } from "react";
import * as d3 from "d3";

const createPieChart = async () => {
  const svg = d3.select("svg"),
    width = svg.attr("width"),
    height = svg.attr("height"),
    radius = Math.min(width, height) / 2;

  const g = svg
    .append("g")
    .attr("transform", `translate(${width / 2}, ${height / 2})`);

  const color = d3.scaleOrdinal(["gray", "green", "brown"]);

  const pie = d3.pie().value(function (d) {
    return d.percent;
  });

  const path = d3
    .arc()
    .outerRadius(radius - 10)
    .innerRadius(0);

  const label = d3
    .arc()
    .outerRadius(radius)
    .innerRadius(radius - 80);

  const data = await d3.csv("/populations.csv");

  const arc = g
    .selectAll(".arc")
    .data(pie(data))
    .enter()
    .append("g")
    .attr("class", "arc");

  arc
    .append("path")
    .attr("d", path)
    .attr("fill", function (d) {
      return color(d.data.states);
    });

  arc
    .append("text")
    .attr("transform", function (d) {
      return `translate(${label.centroid(d)})`;
    })
    .text(function (d) {
      return d.data.states;
    });

svg
    .append("g")
    .attr("transform", `translate(${width / 2 - 120},20)`)
    .append("text")
    .text("Top population states in the US")
    .attr("class", "title");
};

export default function App() {
  useEffect(() => {
    createPieChart();
  }, []);

  return (
    <div className="App">
      <style>{`
        .arc text {
          font: 12px arial;
          text-anchor: middle;
        }

        .arc path {
          stroke: #fff;
        }

        .title {
          fill: green;
          font-weight: italic;
        }
      `}</style>
      <svg width="600" height="400"></svg>
    </div>
  );
}

We put the CSV in the public folder so that we can read static files in our React code.

The createPieChart function lets us get the svg element.

And we set the width , height and radius of the pie chart.

We create the group for the pie with:

const g = svg
   .append("g")
   .attr("transform", `translate(${width / 2}, ${height / 2})`);

Then we add the colors with:

const color = d3.scaleOrdinal(["gray", "green", "brown"]);

Next, we add the pies with:

const pie = d3.pie().value(function(d) {
  return d.percent;
});

Then add the arcs for the pie with:

const path = d3
  .arc()
  .outerRadius(radius - 10)
  .innerRadius(0);

The labels are added with:

const label = d3
  .arc()
  .outerRadius(radius)
  .innerRadius(radius - 80);

Then we read the population.csv file with:

const data = await d3.csv("/populations.csv");

We set the lengths of the arc with:

const arc = g
  .selectAll(".arc")
  .data(pie(data))
  .enter()
  .append("g")
  .attr("class", "arc");

And we set the pie colors with:

arc
  .append("path")
  .attr("d", path)
  .attr("fill", function(d) {
    return color(d.data.states);
  });

And we set the text labels for the pies with:

arc
  .append("text")
  .attr("transform", function(d) {
    return `translate(${label.centroid(d)})`;
  })
  .text(function(d) {
    return d.data.states;
  });

Finally, we add the title of the chart with:

svg
  .append("g")
  .attr("transform", `translate(${width / 2 - 120},20)`)
  .append("text")
  .text("Top population states in the US")
  .attr("class", "title");

In App , we add the styles for the arc so that we can set the font and title color to what we want.

Conclusion

We can add a pie chart easily into our React app.

Categories
React D3

Adding Graphics to a React App with D3 — Circle Chart

D3 lets us add graphics to a front-end web app easily.

Vue is a popular front end web framework.

They work great together. In this article, we’ll look at how to add graphics to a Vue app with D3.

Circle Chart

We can create a circle chart with D3 in our React app.

For example, we can write:

import React, { useEffect } from "react";
import * as d3 from "d3";

export default function App() {
  useEffect(() => {
    const width = 400;
    const height = 400;
    const data = [10, 28, 35];
    const colors = ["green", "lightblue", "yellow"];

    const svg = d3
      .select("body")
      .append("svg")
      .attr("width", width)
      .attr("height", height);

    const g = svg
      .selectAll("g")
      .data(data)
      .enter()
      .append("g")
      .attr("transform", function (d, i) {
        return "translate(0,0)";
      });

    g.append("circle")
      .attr("cx", function (d, i) {
        return i * 75 + 50;
      })
      .attr("cy", function (d, i) {
        return 75;
      })
      .attr("r", function (d) {
        return d * 1.5;
      })
      .attr("fill", function (d, i) {
        return colors[i];
      });

    g.append("text")
      .attr("x", function (d, i) {
        return i * 75 + 25;
      })
      .attr("y", 80)
      .attr("stroke", "teal")
      .attr("font-size", "10px")
      .attr("font-family", "sans-serif")
      .text((d) => {
        return d;
      });
  }, []);

  return (
    <div className="App">
      <div id="svgcontainer"></div>
    </div>
  );
}

We create the svg by selecting the body and then add the svg to it.

And we also set the width and height of the SVG.

This is done with:

const svg = d3
  .select("body")
  .append("svg")
  .attr("width", width)
  .attr("height", height);

Then we create the group with the data by writing:

const g = svg
  .selectAll("g")
  .data(data)
  .enter()
  .append("g")
  .attr("transform", function(d, i) {
    return "translate(0,0)";
  });

data has the data.

Next, we add the circles by writing:

g.append("circle")
  .attr("cx", function(d, i) {
    return i * 75 + 50;
  })
  .attr("cy", function(d, i) {
    return 75;
  })
  .attr("r", function(d) {
    return d * 1.5;
  })
  .attr("fill", function(d, i) {
    return colors[i];
  });

We add the cx and cy attributes by call attr .

r has the radius, and fill has the background color for each circle.

Then we add the text that goes with the circles by writing:

g.append("text")
  .attr("x", function(d, i) {
    return i * 75 + 25;
  })
  .attr("y", 80)
  .attr("stroke", "teal")
  .attr("font-size", "10px")
  .attr("font-family", "sans-serif")
  .text((d) => {
    return d;
  });

We call append with the 'text' argument.

And then we set the x and y attributes of the position of the text.

Then we set the color of the text with the stroke .

font-size has the font size and font-family has the font family.

The text method takes a callback that returns the text.

Conclusion

We can add a circle chart into our React app with D3 easily.

Categories
React D3

Adding Graphics to a React App with D3

D3 lets us add graphics to a front-end web app easily.

Vue is a popular front end web framework.

They work great together. In this article, we’ll look at how to add graphics to a Vue app with D3.

Getting Started

We install D3 by running npm i d3 .

Then we can use it in our Vue app.

Data Join

Now that we installed D3, we can use it in our app.

D3 lets us render an array of items into a list easily.

To do this, we write:

import React, { useEffect } from "react";
import * as d3 from "d3";

const arr = [10, 20, 30, 25, 15];

export default function App() {
  useEffect(() => {
    d3.select("#list")
      .selectAll("li")
      .data(arr)
      .text((d) => d);
  }, []);

  return (
    <div className="App">
      <ul id="list">
        {arr.map(() => (
          <li></li>
        ))}
      </ul>
    </div>
  );
}

We put our D3 code into the useEffect callback to get the list and then select all the li elements.

Then we put the numbers in the li elements.

SVG

We can use D3 to add SVGs into our components.

To do this, we write:

import React, { useEffect } from "react";
import * as d3 from "d3";

export default function App() {
  useEffect(() => {
    const width = 300;
    const height = 300;
    const svg = d3
      .select("#svgcontainer")
      .append("svg")
      .attr("width", width)
      .attr("height", height);
    svg
      .append("line")
      .attr("x1", 100)
      .attr("y1", 100)
      .attr("x2", 200)
      .attr("y2", 200)
      .style("stroke", "rgb(255,0,0)")
      .style("stroke-width", 2);
  }, []);

  return (
    <div className="App">
      <div id="svgcontainer"></div>
    </div>
  );
}

to add a line.

We have:

const svg = d3
  .select("#svgcontainer")
  .append("svg")
  .attr("width", width)
  .attr("height", height);

to add the svg into the div with ID svgcontainer .

And we set the width and the height of the svg with the attr methods.

Then we add the line by writing:

svg
  .append("line")
  .attr("x1", 100)
  .attr("y1", 100)
  .attr("x2", 200)
  .attr("y2", 200)
  .style("stroke", "rgb(255,0,0)")
  .style("stroke-width", 2);

We call append with 'line' to add the line.

Then we add the x1 and y1 attributes to add the coordinates of the start of the line.

And we add the x2 and y2 attributes to add the coordinates of the end of the line.

Also, we set the stroke color of the line.

And we set the stroke width to set the line thickness.

Rectangle

We can add a rectangle with the append method with 'rect' as the argument.

For instance, we can write:

import React, { useEffect } from "react";
import * as d3 from "d3";

export default function App() {
  useEffect(() => {
    const width = 300;
    const height = 300;
    const svg = d3
      .select("#svgcontainer")
      .append("svg")
      .attr("width", width)
      .attr("height", height);
    svg
      .append("rect")
      .attr("x", 20)
      .attr("y", 20)
      .attr("width", 200)
      .attr("height", 100)
      .attr("fill", "green");
  }, []);

  return (
    <div className="App">
      <div id="svgcontainer"></div>
    </div>
  );
}

We add the svg the same way as the previous example.

Then we add the rectangle with the append method.

The x and y attributes are the x and y coordinates of the top left corner of the circle.

The width and height attributes are the width and height of the rectangle.

And the fill is the background color of the rectangle.

Conclusion

We can add basic shapes to our React app with D3.

Categories
Vue 3 Testing

Testing Vue 3 Apps — Test with Real Router and Stub Components

With apps getting more complex than ever, it’s important to test them automatically. We can do this with unit tests, and then we don’t have to test everything by hand.

In this article, we’ll look at how to test Vue 3 apps by writing a simple app and testing it.

Testing With a Real Router

We can write tests that test our app with the real router.

For example, we can write:

src/router/index.js

import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import Edit from '../views/Edit.vue'
import Post from '../views/Post.vue'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/edit',
    name: 'edit',
    component: Edit
  },
  {
    path: '/posts/:id/edit',
    name: 'post',
    component: Post
  },
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
})

export default router

src/views/Edit.vue

<template>
  <button @click="redirect">Click to Edit</button>
</template>

<script>
export default {
  methods: {
    redirect() {
      this.$router.push(`/posts/1/edit`);
    },
  },
};
</script>

src/views/Post.vue

<template>
  <div>Post {{$route.params.id}}</div>
</template>

tests/unit/example.spec.js

import { flushPromises, mount } from '@vue/test-utils'
import App from '@/App'
import router from '@/router'

describe('component handles routing correctly', () => {
  it('allows authenticated user to edit a post', async () => {
    router.push('/edit')
    await router.isReady()
    const wrapper = mount(App, {
      global: {
        plugins: [router],
      }
    })
    await wrapper.find('button').trigger('click')
    await flushPromises()
    expect(wrapper.html()).toContain('Post 1')
  })
})

We have some routes mapped to some components.

Then in our test, we redirect to the edit route so the Edit component is loaded.

Next, we call router.isReady() to wait until the Vue Router is loaded.

Then we mount the App and pass in the router into the plugins property.

Then we trigger a click on the button in the Edit component.

And finally, we check what’s rendered after calling flushPromises to wait until the new route is loaded.

Stubbing a Single Child Component

We can stub child components in our app.

For example, we can write:

import { mount } from '@vue/test-utils'
import axios from 'axios';

const FetchDataFromApi = {
  name: 'FetchDataFromApi',
  template: `
    <div>{{ result }}</div>
  `,
  async mounted() {
    const res = await axios.get('https://yesno.wtf/api')
    this.result = res.data
  },
  data() {
    return {
      result: ''
    }
  }
}

const App = {
  components: {
    FetchDataFromApi
  },
  template: `
    <div>
      <h1>Welcome to Vue.js 3</h1>
      <fetch-data-from-api />
    </div>
  `
}

test('stubs component with custom template', () => {
  const wrapper = mount(App, {
    global: {
      stubs: {
        FetchDataFromApi: {
          template: '<span />'
        }
      }
    }
  })
  expect(wrapper.html()).toContain('Welcome to Vue.js 3')
})

We have the FetchDataFromApi component that we want to ignore in our test.

It’s used in App , but we don’t want to mount it in our test.

Therefore, we want to stub this component. To do this, we pass in a stubbed component into the global.stubs property.

Then we check the content of App in the last line of the test.

Conclusion

We can test our components with a real Vue Router with Vue 3 components.

Also, we can stub components and test only the parts we want to test.