A simple D3 plugin
D3 plugins are a integral part of the soon to be released D3 v4. Now, rather than having to include the all of D3, it is possible to pick and choose the bits of the framework that you want to use, and ignore the rest.
Moreover this lets you create charts that can be versioned and distributed through package control. Awesome!
Since the current javascript modus operandi is to modularize as much as possible, lets modularize the most familiar part of any D3 chart.
var svg = d3.select('body')
.append('svg')
.attr('width', width)
.attr('height', height);
This simple plugin is going to give us a very small script that still gives us a significant amount of D3 js power.
Project Setup
Mike Bostock gives a great introduction to D3 plugins in his Lets make a D3 Plugin article. Its highly recommended that you give that page a glance since it lays the foundation for the rest of this article.
Mike provides a zip file of a starting project but it has a few short comings that make it less than useful.
The changes are walked through below, or you can run over to the github repo if you are impatient.
Configure Rollup.js
The first change that should happen with the base zip file is getting rollup configured so that it can include files from npm. This requires using rollup-plugin-node-resolve
and attaching it as a plugin to the build process.
- Run
npm i rollup-plugin-node-resolve --save
to install the package. - Create a rollup.config.js like the one d3-svg uses.
- Add the
-c rollup.config.js
option to the rollup command in the package.json file.
Package.json script changes
The great thing about the base zip file is that so much of the build/test/publish process is just taken care of for you. That being said, I've modified the scripts slightly to better fit my workflow and use case. See the d3-svg package.json for details, but the additional functionality is:
npm run build
to be able to trigger arbitrary builds of the project.npm version
will add commit a build of the project at that version.
Authoring the project
As you can imagine implementing this is trivial:
export {d3_svg as create};
import * as d3_selection from 'd3-selection';
function d3_svg(elem, opts) {
var body = d3_selection.select(elem);
var svg = body.append('svg');
if (opts && opts.width) {
svg.attr('width', opts.width);
}
if (opts && opts.height) {
svg.attr('height', opts.height);
}
return svg;
}
Exporting the right thing
Take note that the D3 build system provided by Mike is going to namespace everything against your package name and that your package cannot be a single function. The function above is going to be callable as d3_svg.create()
. The build system is going to also create a property d3_svg.version
that is pulled from the version
key of your package.json.
Write a test
Yes, write a damn test. The base zip file already has all of the testing framework set up. Just write one simple test that checks the most basic bit of functionality for your module. See d3-svg's tests if you need some inspiration.
Getting this out of the way now makes it much easier to create tests in the future and allows you to ask pull requesters to write tests for bugs that they find. Tests keep your code healthy and working properly as modules get more complex and grow.
Publish and profit!
Now that everything is pulled together there is only one last thing to do.
npm version patch
npm publish
There you go - a D3js plugin that is versioned and distributed through npm. Now, with relative ease, you can make masterpieces like this:
var svg = d3_svg.create('#example', {width: 710, height: 100});
svg.append('text')
.text('Magic!')
.attr('x', 710/2)
.attr('y', 50)
.attr('text-anchor', 'middle');
svg.selectAll('circle')
.data([100, 130, 160, 190, 610]).enter()
.append('circle')
.attr('cx', function(d) {return d;})
.attr('cy', 50).attr('r', 10)
.style('fill', 'steelblue');