Making of a d3 plugin

David Nowinsky @davinov - Toucan Toco

Illustrated by d3 sparklines

Everything is a module

Although the word plugin suggests extending core functionality, this pattern is used internally by D3 to organize the code into modules. Thus, you can use this pattern to replace default behavior or to pick a subset of features for a custom build. Another way of thinking about it is that everything in D3 is a plugin, including core features like colors, scales, and selections.”
  • d3.scale

    
    d3.scale.linear()
    .domain([-1, 0, 1])
    .range(["red", "white", "green"]);
    								
  • d3.svg.axis

    
    d3.svg.axis()
    .scale(xScale)
    .orient("bottom");
    							
  • d3.select
  • d3.layouts

When to do a module?

Oh f**k I need to do that on this too. Already done that three times!

Reusability

Factorisation

A stupid plugin


d3.stupid = {};
					

d3.stupid.sayHello = function() {
	console.log("hello");
}
					

Litterature

Sparkline theory and practice - Edward Tufte

Design

Different types of plugins

    Computations

  • Scales
  • Layouts

    SVG Components

  • Shapes
  • Axis

In components...

“You’ll find your components much easier to reuse and reason about if you divide them into two categories. I call them Container and Presentational components but I also heard Fat and Skinny, Smart and Dumb, Stateful and Pure, Screens and Components, etc.These all are not exactly the same, but the core idea is similar.

(In React's world)

    Compuationals

  • Scales
  • Layouts

    Presentationals

  • Shapes
  • Axis

Container means it manages a state

Sparklines' component type?

See the Pen d3 sparklines by David Nowinsky (@davinov) on CodePen.

Presentation

with a tiny state ;)

I/O s

What do we need (for presentational components)?

A d3 selection


d3.stupid.writeHello = function(d3Selection) {
	d3Selection
	.text('Hello!');
};
						

d3.toucan.sparklines = function(d3Selection) {
	d3Selection
	.enter()
	.append('path');

	d3Selection
	.exit()
	.remove();

	d3Selection
	.select('path')
	.attr('d', function(d) {
		...
	});
};
						

Customisability

Towards reusable charts

“I’d like to propose a convention for encapsulating reusable charts in D3.”

function chart() {
	// generate chart here
}
				
From...

d3.stupid.writeHello = function(d3Selection) {
	d3Selection
	.text('Hello!');
};
				
To...

d3.stupid.writeHello = function(options) {

	helloWriter = function(d3Selection) {
		d3Selection
		.text('Hello!');

		// Use options here!
	};

	return helloWriter;
};
				
  • Private options
  • Default values
  • Reconfiguration

d3.stupid.writeHello = function(options) {

	helloWriter = function(d3Selection) {
		d3Selection
		.text('Hello!');
	};

	return helloWriter;
};
				
  • dateSelector
  • valueSelector
  • width
  • height
  • transitionDuration
  • unit
  • dateFormat: optional formatting of dates in tooltip
  • valueFormat: optional formatting of values in tooltip
  • forceLexicalOrder: optional, default true, set to false to force lexical reordering of ordinal dates
  • commonScatter: false by default, use the same scatter for all sparklines (y axis)
  • selectionTimeout: default 2000, time before tooltip disappears, 0 to disable
  • tooltipYOffset: default 0, offset of the tooltip

Composabilty

Breaking into smaller(st) components?

Little big details

A while ago...

d3.select('.sparkline') VS d3.selectAll('.sparkline')

It shall be named "sparklineS"

Best pratices

Have fun extending d3!

@davinov - Toucan Toco
toucantoco.com/sparklines