Skip to content

Upgrading to 1.0.0

jtlan edited this page Jun 12, 2015 · 99 revisions

How To Use This Guide

This Guide consists of a few sections to help you get started

  1. Upgrade steps - step by step walk through of the changes you need to make
  2. Detailed information on the changes - explanations and motivations for changes, as well as any additional details about usage or best practices

Upgrade Steps

1. Rename All Modules

The following modules have been renamed:

  • Component -> Components
  • Plot -> Plots
  • Drawer -> Drawers
  • Scale -> Scales
  • Animator -> Animators
  • Axis -> Axes
  • Dispatcher -> Dispatchers
  • Interaction -> Interactions
  • _Util -> Utils

2. Rename All Abstract Classes

Abstract classes have been renamed as follows:

  • Plottable.Components.AbstractComponent -> Plottable.Component
  • Plottable.Plots.AbstractPlot -> Plottable.Plot
  • Plottable.Plots.AbstractXYPlot -> Plottable.XYPlot
  • Plottable.Drawers.AbstractDrawer -> Plottable.Drawer
  • Plottable.Scales.AbstractScale -> Plottable.Scale
  • Plottable.Axes.AbstractAxis -> Plottable.Axis
  • Plottable.Scales.AbstractQuantitative -> Plottable.QuantitativeScale
  • Plottable.Components.AbstractComponentContainer -> Plottable.ComponentContainer
  • Plottable.Dispatchers.AbstractDispatcher -> Plottable.Dispatcher
  • Plottable.Interactions.AbstractInteraction -> Plottable.Interaction

3. Convert project() --> attr()

The way display properties are set on Plots has changed: see "Removal of project()" for a more detailed explanation as to why. Rename all invocations of project() to attr(). With a global find-replace:

  • FIND: project(
  • REPLACE: attr(

4. Unpack all key-strings into Accessors.

Previously, a string passed in as the second argument to project() would be used as a key into the data (unless it began with "#"), so project("fill", "color") was the same as project("fill", function(d) { return d.color; }). This functionality has been removed.

With a global find-replace (with regular expressions):

  • FIND: \.attr\("([^"]+)",\s*("[^"]+")
  • REPLACE: .attr("$1", function(d) { return d[$2]; }

5. Convert XYPlot project() calls to property-setter calls

project() calls that did not directly set DOM attributes have been spun off into their own methods:

  • project("x", ...) --> x(...)
  • project("y", ...) --> y(...)
  • project("y0", ...) --> y0(...)
  • project("size", ...) --> size(...)
  • project("symbol", ...) --> symbol(...)
  • project("x2", ...) --> x2(...)
  • project("y2", ...) --> y2(...)

With a global find-replace (with regular expressions):

  • FIND: \.attr\("(x|y|y0|size|symbol|x1|y1|x2|y2)",\s*
  • REPLACE: .$1(

6. Convert Plots.Pie project() calls to property-setter calls

  • project("inner-radius", ...) --> innerRadius(...)
  • project("outer-radius", ...) --> outerRadius(...)
  • project("value", ...) --> sectorValue(...)

With a global find-replace (with regular expressions):

  • FIND: \.attr\("(inner|outer)-radius",\s*
  • REPLACE: .$1Radius(
  • FIND: \.attr\("value",\s*
  • REPLACE: .sectorValue(

7. Fix Changes to Types

  • _SpaceRequest -> SpaceRequest. The signature of this type has also changed:
type SpaceRequest = {
  minWidth: number;
  minHeight: number;
}
  • SelectionArea type has been removed. SelectionArea was no longer needed after DragBoxLayer was added in v0.54.0, but the type was mistakenly left in.
  • _Accessor --> Accessor and now takes a generic. The signature has also changed:
export interface Accessor<T> {
  (datum: any, index: number, dataset: Dataset): T;
}
  • The Extent type has been renamed to Range:
export type Range = {
  min: number;
  max: number;
}
  • _Projector has been renamed to Projector.
  • The PlotData type has been removed. The Entity type has been created to represent a visual entity inside a Component:
export interface Entity<C extends Component> {
  datum: any;
  position: Point;
  selection: d3.Selection<any>;
  component: C;
}

Methods that previously returned PlotData now use the PlotEntity type:

interface PlotEntity extends Entity<Plot> {
  dataset: Dataset;
  index: number;
  component: Plot;
}

Unlike PlotData, an PlotEntity corresponds to exactly one datum and its visual representation on the screen.

  • The type StringAccessor has been removed, as there are no Plottable API points that use it.
  • The type Animators.Plot has been renamed to Animator.

8. Remove Scales from Plot constructors.

Since the Scales are supplied through the property setters for a Plot, specifying them in the constructor is redundant. With global find-replace (with regular expressions):

  • FIND: (\.Plots\.[^\(]*\()[^,\)]*(, )?[^\),]*(, )*
  • REPLACE: $1

9. Rename Methods (by class)

Component

  • _anchor() -> anchor()
  • _computeLayout() -> computeLayout(). The signature of this method has also been changed to:
computeLayout(origin?: Point, availableWidth?: number, availableHeight?: number);
  • _doRender() -> renderImmediately()
  • hitBox() removed. See "Interaction changes" for details.
  • _isFixedWidth() -> fixedWidth()
  • _isFixedHeight() -> fixedHeight()
  • registerInteraction() removed. To attach an Interaction to a Component:
// previously: component.registerInteraction(interaction);
interaction.attachTo(component);

See "Interaction changes" for details.

  • remove() -> destroy()
  • _render() -> render()
  • _requestedSpace() -> requestedSpace()
  • xAlign() / yAlign() -> xAlignment() / yAlignment()
  • classed() has been split into three methods:
hasClass(cssClass: string): boolean;
addClass(cssClass: string): Component;
removeClass(cssClass: string): Component;

Group

  • .addComponent(Component, boolean) -> append(Component)
  • Added has(Component).
  • removeComponent(component) -> remove(component)

Dispatchers

  • onEvent(key, callback) -> onEvent(callback)
  • onEvent(key, null) -> offEvent(callback) to remove callback See "Registering for events" for details.

Dispatchers.Mouse

  • getLastMousePosition() --> lastMousePosition()

Drawer

  • Constructor takes in a Dataset instead of a string key
  • _getPixelPoint() has been moved to Plot.
  • setClass() endpoint has been removed.
  • _getRenderArea() and setup(Selection) have been combined into renderArea(), which functions as a getter-setter.
  • _getSelector() --> selector()
  • _getSelection() --> selectionForIndex()
  • added totalTime(), which returns the total time that would be spent drawing (in milliseconds).
  • draw() now returns the calling Drawer, instead of the total time that would be spent drawing.

Drawers.Area

  • No longer extends Drawer.Line.

Drawers.Element

  • This class has been removed and its functionality folded into the parent Drawer class.

Drawers.Rect

Has been renamed to Drawers.Rectangle. The label-drawing functionality has been moved to Plots.Bar. Consequently:

  • drawText() has been removed
  • removeLabels() has been removed
  • _getIfLabelsTooWide() has been removed

Legend

  • scale() --> colorScale()
  • sortFunction() -> comparator()
  • symbolFactoryAccessor() -> symbol()
  • entitiesAt() replaces getEntry() and returns Entity<Legend>[] (see the section on type changes for details on Entity).

Plots

Datasets are now manipulated by direct references rather than string keys. See "Working With Datasets" for details.

  • datasetOrder() removed. Use datasets() to set the order of the Datasets.
  • addDataset() and removeDataset() now only accept Datasets:
public addDataset(dataset: Dataset);
public removeDataset(dataset: Dataset);
  • getAllSelections() --> selections().
  • getAllPlotData() --> entities():
public entities(datasets = this.datasets()): Plots.Entity[];
  • getClosestPlotData() --> entityNearest():
public entityNearest(queryPoint: Point): Plots.Entity;
  • selections() and entities() now take in Dataset[].
  • selections() no longer takes a boolean "exclude" parameter. To get Selections for a certain subset of Datasets:
var datasetsToExclude = [...];
var datasetExclusionFilter = function(dataset) { return datasetsToExclude.indexOf(dataset) === -1; }
var unexcludedDatasets = plot.datasets().filter(datasetExclusionFilter);
plot.selections(unexcludedDatasets);
  • generateProjectors() has been removed -- see "Working with Datasets" for how to use attr() and property setters instead.
  • _Accessors now take in the Dataset instead of the metadata from that Dataset. See "Changes to Types"
  • project() has been removed in favor of property setters; see "Removal of project()".
  • Attributes set using attr() will now be applied directly to the DOM. See "Removal of project()" for more details.
  • animate() --> animated(), and now also functions as a getter.

XYPlot

The methods automaticallyAdjustXScaleOverVisiblePoints() and automaticallyAdjustYScaleOverVisiblePoints() have been combined into a single method, autorangeMode(). The autorange functionality is now set as follows:

xyPlot.autorangeMode("x"); // to adjust the x scale based on the y domain
xyPlot.autorangeMode("y"); // to adjust the y scale based on the x domain
xyPlot.autorangeMode("none"); // to disable autorange functionality.

StackedPlot

StackedPlot was transformed into a set of static utilities called Plottable.Utils.Stacking.

Plots.Bar

  • The orientation is now set in the constructor as follows:
new Plots.Bar(Plots.Bar.ORIENTATION_VERTICAL);
new Plots.Bar(Plots.Bar.ORIENTATION_HORIZONTAL);
  • The orientation can now be retrieved with orientation().
  • The barAlignment() endpoint has been removed. The original goal for that API point was to allow for histogram-like visualizations, but having a proper Histogram class would be a better way of supporting that goal.
  • barLabelFormatter() -> labelFormatter()
  • barLabelsEnabled() -> labelsEnabled()
  • baseline() -> baselineValue() *baselineValue() now operates on X|Y, instead of number:
baselineValue(): X | Y;
baselineValue(value: X | Y): Bar<X, Y>;
  • getBars has been split into
public entitiesAt(p: Point): Entity[];

and

public entitiesIn(bounds: Bounds): Entity[];
public entitiesIn(xRange: Range, yRange: Range): Entity[];

Plots.Grid --> Plots.Rectangle

  • Plots.Grid's functionality has been rolled into Plots.Rectangle.
  • The constructor no longer takes in a Scales.InterpolatedColor. Instead, the Scales.InterpolatedColor to be used should be set using attr():
var interpolatedColorScale = new Plottable.Scales.InterpolatedColor();
gridPlot.attr("fill", function(d) { return d.value; }, interpolatedColorScale);
  • The x()/x2() properties use the same Scale, as do the y()/y2() properties. As a result, the x2() and y2() property setters do not take in a Scale.

Domainers

Domainers have been removed, and their functionality moved to QuantitativeScale. See "Replacing Domainers" for details.

Scales

Scales now work with IncludedValuesProviders. An IncludedValuesProvider is a function that returns an array of domain values:

interface IncludedValuesProvider<D> {
    (scale: Scale<D, any>): D[];
}

The returned values will be included in the domain of the Scale when it autoDomain()s. IncludedValuesProviders are added and removed from a Scale with the following methods on Scale:

addIncludedValuesProvider(provider: Scales.IncludedValuesProvider<D>): Scale<D, R>;
removeIncludedValuesProvider(provider: Scales.IncludedValuesProvider<D>): Scale<D, R>;
  • copy() removed. If this method is important to your use-case, please contact us or file an issue and we'll help you out.

QuantitativeScale

  • clamp() removed.
  • interpolate() removed.
  • rangeRound() removed.
  • numTicks() has been removed because it didn't actually deliver on its intended functionality: numTicks(count) did not guarantee exactly count ticks would be drawn. In many cases, setting an exact number of ticks would also lead to non-round numbers. We're working on building out a better API point that better conveys the intended functionality.

If any of the above methods are important to your use-case, please contact us or file an issue and we'll help you out. *getDefaultTicks() --> defaultTicks()

  • padProportion(number) has been added. This method can be used to set the how much padding occurs on a QuantitativeScale when autoDomain()-ing. The method is passed an argument signifying how much larger the padded domain should be, relative to the size of the original domain. For example, padProportion(0.5) will cause the padded domain to be about 50% larger than the unpadded domain. padProportion(0) disables padding. QuantitativeScales have a padProportion() of 0.05 by default.

  • QuantitativeScales now work with PaddingExceptionsProviders:

export interface PaddingExceptionsProvider<D> {
  (scale: QuantitativeScale<D>): D[];
}

A PaddingExceptionsProvider is a function that returns an array of domain values. If any of those values are either end of the domain computed when autoDomain()-ing, that end of the domain will not be padded. PaddingExceptionsProvider can be added and removed from the QuantitativeScale with the following methods:

addPaddingExceptionsProvider(provider: Scales.PaddingExceptionsProvider<D>): QuantitativeScale<D>;
removePaddingExceptionsProvider(provider: Scales.PaddingExceptionsProvider<D>): QuantitativeScale<D>;

Scales.Log

Scales.Log has been deprecated for a while, and has now been removed. ModifiedLog is recommended as a replacement due to its better behavior when autoDomain()ing, as well as its better support for negative numbers.

Scales.ModifiedLog

  • The showIntermediateTicks() endpoint has been removed. To exercise more control over which ticks are displayed, assign a custom TickGenerator to the Scale.

Table

  • addComponent(row, col, component) -> add(component, row, col)
  • _removeComponent(component) -> remove(component)
  • colWeight() -> columnWeight()
  • padding() has been split into rowPadding() and columnPadding()

Labels

  • orientation(orientation: string) is now angle(angle: number). Supported angles are -90, 0 (horizontal), and 90.
Plottable.Label(displayText = "", angle = 0);

Axis

  • gutter() -> margin()
  • orient() -> orientation()
  • No longer accepts a Formatter in the constructor. Use formatter() to set the Formatter after construction.

Axes.Numeric

  • showEndTickLabel() removed; The method didn't actually do anything.

RenderController

  • .setRenderPolicy() no longer takes a RenderPolicy. The following stringy enums are available:
export module Policy {
    export var IMMEDIATE = "immediate";
    export var ANIMATION_FRAME = "animationframe";
    export var TIMEOUT = "timeout";
}

Animator

  • getTiming() --> totalTime()

Animators.Base --> Animators.Easing

A number of renames for clarity:

  • delay() -> startDelay()
  • duration() -> stepDuration()
  • easing() -> easingMode()
  • maxIterativeDelay -> stepDelay()
  • maxTotalDuration() now defaults to Infinity. The maximum total duration of animations on Plots remains at the old default of 600ms.
  • static fields DEFAULT_START_DELAY_MILLISECONDS, DEFAULT_STEP_DURATION_MILLISECONDS, DEFAULT_ITERATIVE_DELAY_MILLISECONDS, DEFAULT_MAX_TOTAL_DURATION_MILLISECONDS, and DEFAULT_EASING have been removed. The default values can be queried by creating a new Animators.Easing and calling the appropriate getters.

Animators.Rect / Animators.MovingRect

  • Animators.Rect and Animators.MovingRect have been removed. Their logic was very specific towards the use case in Plots.Bar, which now uses a properly configured Animators.Base instead. If you are interested in the animation logic used in those animators, please contact us.

Utils

Plottable.Utils.Methods has been split into multiple modules. As a result, the module hierarchy (and sometimes the name of the method itself) have been changed changed:

Utils.Methods.inRange()           -> Utils.Math.inRange();
Utils.Methods.clamp()             -> Utils.Math.clamp();
Utils.Methods.max()               -> Utils.Math.max();
Utils.Methods.min()               -> Utils.Math.min();
Utils.Methods.isNaN()             -> Utils.Math.isNaN();
Utils.Methods.isValidNumber()     -> Utils.Math.isValidNumber();
Utils.Methods.range()             -> Utils.Math.range();
Utils.Methods.distanceSquared()   -> Utils.Math.distanceSquared();

Utils.Methods.addArrays()         -> Utils.Array.add();
Utils.Methods.uniq()              -> Utils.Array.uniq();
Utils.Methods.createFilledArray() -> Utils.Array.createFilledArray();
Utils.Methods.flatten()           -> Utils.Array.flatten();
Utils.Methods.arrayEq()           -> DELETED

Utils.Methods.copyMap()           -> DELETED
Utils.Methods.populateMap()       -> DELETED

Utils.Methods.warn()              -> Utils.Window.warn();
Utils.Methods.setTimeout()        -> Utils.Window.setTimeout();
Utils.Methods.objEq()             -> DELETED
Utils.Methods.isIE()              -> DELETED
Utils.Methods.parseRange()        -> DELETED

Utils.Methods.colorTest()         -> Utils.Color.colorTest();
Utils.Methods.lightenColor()      -> Utils.Color.lightenColor();

Utils.Methods.intersectsBBox()    -> Utils.DOM.intersectsBBox();

Utils.DOM

  • getBBox() -> elementBBox()
  • getElementWidth() -> elementWidth()
  • getElementHeight() -> elementHeight()
  • getBoundingSVG() -> boundingSVG()
  • getUniqueClipPathId() -> generateUniqueClipPathId()
  • getSVGPixelWidth() -> REMOVED
  • getParsedStyleValue() -> REMOVED
  • isSelectionRemovedFromSVG() -> REMOVED If you were using the removed methods, please contact us.

10. Rename Component-adding methods on Group and Table

The semantics of adding Components to Groups and Tables has been improved. To add a Component to a Group:

// previously: group.addComponent(component);
group.append(component);

To add a Component to a Table:

// previously: table.addComponent(rowIndex, columnIndex, component);
table.add(component, rowIndex, columnIndex);

To convert to the new API:

  • FIND: \.addComponent\((\d+),\s*(\d+),\s*([^)]+)\)
  • REPLACE: \.add($3, $1, $2)

11. Remove uses of above() and below()

above() and below() had some odd behavior -- combining two non-Group Components would create a Group out of nowhere, but if the caller or target was a Group the non-Group Component would be added to the existing Group. If both caller and target were Groups one would be appended to the other, but it wasn't clear which way that would be. Consequently, we have removed these API points. A Group must be explicitly created to group multiple Components:

// previously: component1.above(component2);
var group = new Plottable.Components.Group([component2, component1]); 
// previously: component1.below(component2);
var group = new Plottable.Components.Group([component1, component2]); 
// previously: group.below(component);
group.append(component);

12. No automagic Groups in Tables.

Continuing with the theme of not creating Groups automatically, adding a Component to an occupied cell in a Table will now throw an error. To put multiple Components in the same cell of a Table, add the Components to a Group, then place the Group in that cell.

13. Remove callbacks using direct reference

Previously, a callback was removed like this:

clickInteraction.onClick(null);

Now, to remove a callback:

clickInteraction.offClick(callback); // === operator is used to decide which callback to remove

where callback is the callback that was originally passed in to the registration call (onClick() in this case).

Detailed Explanations

Removal of project()

Motivation

project() previously performed a lot of magic in the background. For example, calling .project("fill", () => "red"), would set the "fill" attribute in the DOM to "red", but calling .project("x", ...) set a variety of DOM attributes depending on what kind of Plot was used. Furthermore, if the second argument to project() was a string, it would automatically be used as a key into the data (unless the string began with "#"...), meaning that .project("fill", "red") didn't do what might be expected.

We have decided to remedy this by splitting the functionality of project() into attr() and "property setters".

attr()

attr() previously existed as an alias for project(). Now, it directly sets DOM attributes to the results of an Accessor and a Scale:

attr(attr: string, attrValue: number | string | Accessor<number> | Accessor<string>): Plot;
attr<A>(attr: string, attrValue: A | Accessor<A>, scale: Scale<A, number | string>): Plot;

Example invocations:

  • plot.attr("stroke-width", 2);
  • plot.attr("fill", "pink");
  • plot.attr("fill", function(d) { return d.value >= 0 ? "green" : "red"; });
  • plot.attr("stroke", function(d) { return d.type; }, colorScale);

The Scale must be passed as the third argument for it to autoDomain() over the data passing through it.

attr() can also be invoked at as a getter that returns the AccessorScaleBinding for a particular attribute:

interface AccessorScaleBinding {
  accessor: Accessor;
  scale?: Scale;
}

Property Setters

Property setters are methods for setting Plot properties that are not actual DOM attributes. These have a similar signature to attr(), taking in an Accessor and a Scale. For example:

x(x: number | Accessor<number>): XYPlot<X, Y>;
x(x: X | Accessor<X>, xScale: Scale<X, number>): XYPlot<X, Y>;

x() is used to set the "x" property on an XYPlot: the previous invocation would have been something like project("x", function(d) { return d.x; }, xScale). Now, instead:

plot.x(function(d) { return d.x; }, xScale);

Invoking a property setter with no arguments will return an AccessorScaleBinding for that property.

Here is the list of property setters:

  • All XYPlots: x() and y()
  • Plots.Area: y0()
  • Plots.Grid: x2() and y2()
  • Plots.Pie: innerRadius(), outerRadius(), and sectorValue()
  • Plots.Scatter: size() and symbol()

Working with Datasets

Since keys are no longer used to register Datasets, operations involving Datasets are now done by using the Datasets directly. For example, to loop over all Datasets in a Plot:

var datasets = plot.datasets(); // Dataset[]
datasets.forEach((dataset) => {
  ...
});

Reversing the order of Datasets in a Plot:

plot.datasets(plot.datasets().reverse());

Methods that previously accepted keys associated with Datasets now take an array of Datasets. For example, to get the Selections associated with a particular Dataset:

var selectionsOfInterest = plot.getAllSelections([datasetOfInterest]);

Previously generateProjectors() was used to get the projectors (scaled Accessors) associated with particular Dataset keys. Since the property-setting methods and attr() now function as getters, they can be used to retrieve the AccessorScaleBinding instead:

var yBinding = plot.y();
var yAccessor = yBinding.accessor;
var yScale = yBinding.scale;
plot.datasets().forEach((dataset) => {
  var yValues = dataset.data().map((datum, index) => yAccessor(datum, index, dataset));
  var scaledYValues = yValues.map((value) => yScale.scale(value));
  // do something with the y-values
  ...
});

If a Scale was not originally set with attr() or the property setter, no Scale will be present in the AccessorScaleBinding:

plot.attr("fill", (datum) => datum.color);
...
...
var fillBinding = plot.attr("fill");
var fillAccessor = fillBinding.accessor;
var fillScale = fillBinding.scale; // will be undefined

Interaction changes

Previously, Components used a transparent hitbox to detect events; Interactions would attach event listeners to the hitbox. However, this mechanism created a problem because the hitboxes of Components higher up in a Group would block the hitboxes of Components below them.

All of Plottable's Interactions have been changed to use a different event-detecting mechanism that does not require a hitbox, so the hitbox and its associated retrieval call hitBox() have been removed.

The semantics for attaching Interactions has been changed to make it clearer that an Interaction can only be attached to one Component at a time:

// previously: component.registerInteraction(interaction);
interaction.attachTo(component);

Interactions can now also be detached from Components:

interaction.detachFrom(component);

Replacing Domainers

Domainers were previously used when autoDomain()-ing a QuantitativeScale. However, this functionality more properly belongs on the QuantitativeScale itself, so it has been moved there:

padProportion(): number;
padProportion(padProportion: number): QuantitativeScale<D>;
addPaddingExceptionsProvider(provider: Scales.PaddingExceptionsProvider<D>): QuantitativeScale<D>;
removePaddingExceptionsProvider(provider: Scales.PaddingExceptionsProvider<D>): QuantitativeScale<D>;
addIncludedValuesProvider(provider: Scales.IncludedValuesProvider<D>): Scale<D, R>;
removeIncludedValuesProvider(provider: Scales.IncludedValuesProvider<D>): Scale<D, R>;

See here and here for details on how to use those methods.

domainMin() and domainMax()

If you were previously extending Domainer to control the min and max values on the QuantitativeScale's domain, we have added two methods for setting only one end of the domain on QuantitativeScale:

  • domainMin() sets the lower end of the domain.
  • domainMax() sets the upper end of the domain.

If only one is set, the other end of the scale will behave as though it was autoDomain()-ed.

Calling both domainMin(min) and domainMax(max) is equivalent to calling domain([min, max]). Setting both can reverse the domain on QuantitativeScale`s that support reversal.

Calling autoDomain() will clear both set values, the same way calling autoDomain() overrides domain().

Example fiddle: http://jsfiddle.net/fv2a2jej/

Bugfixes

If you were previously using a Scales.Color inside an Accessor:

plot.project("fill", function(d) { return colorScale.scale(d.type); });

The Scale's domain would have expanded to cover all the data, which was an error. Scales must be passed as the third argument to attr() (or the second argument to the property setters) for the Scale to autoDomain() over all the data. Instead, do this:

plot.attr("fill", function(d) { return d.type; }, colorScale);