DEV Community

Tomasz Wegrzanowski
Tomasz Wegrzanowski

Posted on

Open Source Adventures: Episode 30: Using D3 and Parcel to visualize Russian Tank Losses

In the previous episode I used D3 to make a simple graph, without using any tooling. Let's do a more modern take.

Create a new app

There's a lot of different bundlers, and most of them require some painful configuration.

Let's try parcel this time, as it promises to just work out of the box.

$ npm init -y
$ npm install d3 parcel
Enter fullscreen mode Exit fullscreen mode

It doesn't quite do what it promises, but it's still a lot less configuration that webpack or rollup.

Parcel configuration for GitHub Pages

The first issue with Parcel is that it outputs everything with absolute path, so your app will only work if you host it on top level of a domain.

Which is not how GitHub Pages are setup, and is overall a terrible default. The default should be relative paths, so it can be served anywhere. To make it work, we need to passs --public-url . to parcel.

package.json

We need to change two things. Set source entry with our entrypoint. And tell Parcel that we want relative URLs, so it works with GitHub Pages.

{
  "name": "episode-30",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "parcel:build": "parcel build --public-url ."
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "d3": "^7.4.2",
    "parcel": "^2.4.1"
  },
  "source": "src/index.html"
}
Enter fullscreen mode Exit fullscreen mode

That's almost the end of our Parcel issues.

src/index.html

Here we can reduce two scripts to one. We need to add type="module" annotation to make it work.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="app.css">
  </head>
  <body>
    <h1>Russian Tank Losses</h1>
    <script src="app.js" type="module"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

src/app.css

Unchanged from previous version.

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
Enter fullscreen mode Exit fullscreen mode

src/app.js

And one more Parcel issue. By default it won't copy static assets like our .csv files to the build directory. There are many solutions to this - the way we'll be using it, is by importing url: of the asset we want.

Overall, only three lines changed from previous episode's version.

First we need to import d3 with import * as d3 from "d3". We could also just import specific functions like import {csv, scaleLinear, scaleTime, extent, select, axisBottom, axisLeft, line} from d3, but D3 API really wasn't created for it, and I wouldn't recommend it.

Second we need to inform Parcel that our csv needs bundling. csvUrl will become the Parcel-bundled URL, with appropriate hash. This isn't the only way to do assets, but it works well enough.

And then we need to use that csvUrl with let data = await d3.csv(csvUrl, parseRow).

Nothing else needed changing.

import * as d3 from "d3"
import csvUrl from 'url:./russia_losses_equipment.csv'

let parseRow = ({date,tank}) => ({date: new Date(date), tank: +tank})

let main = async () => {
  let data = await d3.csv(csvUrl, parseRow)
  data.unshift({date: new Date("2022-02-24"), tank: 0})

  let xScale = d3.scaleTime()
    .domain(d3.extent(data, d => d.date))
    .range([0, 600])

  let yScale = d3.scaleLinear()
    .domain(d3.extent(data, d => d.tank))
    .range([400, 0])

  let svg = d3.select("body")
    .append("svg")
      .attr("width", 800)
      .attr("height", 600)
    .append("g")
      .attr("transform", "translate(100, 100)")

  svg.append("g")
    .call(d3.axisLeft(yScale))

  svg.append("g")
    .attr("transform", "translate(0, 400)")
    .call(d3.axisBottom(xScale))

  svg.append("path")
    .datum(data)
    .attr("fill", "none")
    .attr("stroke", "red")
    .attr("stroke-width", 1.5)
    .attr("d", d3.line()
      .x(d => xScale(d.date))
      .y(d => yScale(d.tank)))
}

main()
Enter fullscreen mode Exit fullscreen mode

Should you use Parcel?

Parcel didn't quite do zero-config bundling, and there are some annoying things, like it's impossible to turn off hashing (--no-content-hash just replaces content hashes with static hashes), but that's still a big improvement over other JavaScript bundlers.

If you use a framework like Svelte or React, you already have a bundler setup, so you should probably just use that. But if you don't, Parcel might be the best low-config solution right now.

Story so far

I deployed this on GitHub Pages, you can see it here.

Coming next

In the next episode, we'll port this app to Svelte. And after that we'll try to figure out how long until Russia runs out of tanks.

Top comments (0)