Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Feature: optional axis min and max values #10

Merged
merged 4 commits into from

2 participants

@paularmstrong

Added options to xChart and setData

  • xMin
  • xMax
  • yMin
  • yMax

If null, uses the previous logic for min/max. If defined, will use that value regardless of input data. Does not affect ordinal scales.

Closes gh-5.

@jmullan

looks-ok

@jmullan jmullan closed this
@jmullan jmullan reopened this
@jmullan jmullan merged commit d434dd6 into from
@paularmstrong paularmstrong deleted the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
58 docs/docs.json
@@ -28,47 +28,67 @@
"desc": "Callback behavior for a user clicking a data point."
},
"axisPaddingTop": {
- "type": "int",
+ "type": "Number",
"default": 0,
"desc": "Amount of space between the top of the chart and the top value on the y-scale."
},
"axisPaddingRight": {
- "type": "int",
+ "type": "Number",
"default": 0,
"desc": "Amount of space between the right side of the chart and the highest value on the x-scale."
},
"axisPaddingBottom": {
- "type": "int",
+ "type": "Number",
"default": 5,
"desc": "Amount of space between the bottom of the chart and the lowest value on the y-scale."
},
"axisPaddingLeft": {
- "type": "int",
+ "type": "Number",
"default": 20,
"desc": "Amount of space between the left side of the chart and the lowest value on the x-scale."
},
+ "xMin": {
+ "type": "Number",
+ "default": "null",
+ "desc": "Minimum allowed value on the xScale. If <code>null</code>, uses the data's min value, logically padded for aesthetics. Does not affect <code>ordinal</code> scales. May be overrided using the <a href=\"#method-setData\">setData</a> method with the <a href=\"#data-xMin\">xMin</a> data format key."
+ },
+ "xMax": {
+ "type": "Number",
+ "default": "null",
+ "desc": "Maximum allowed value on the xScale. If <code>null</code>, uses the data's max value, logically padded for aesthetics. Does not affect <code>ordinal</code> scales. May be overrided using the <a href=\"#method-setData\">setData</a> method with the <a href=\"#data-xMax\">xMax</a> data format key."
+ },
+ "yMin": {
+ "type": "Number",
+ "default": "null",
+ "desc": "Minimum allowed value on the yScale. If <code>null</code>, uses the data's min value, logically padded for aesthetics. Does not affect <code>ordinal</code> scales. May be overrided using the <a href=\"#method-setData\">setData</a> method with the <a href=\"#data-yMin\">yMin</a> data format key."
+ },
+ "yMax": {
+ "type": "Number",
+ "default": "null",
+ "desc": "Maximum allowed value on the yScale. If <code>null</code>, uses the data's max value, logically padded for aesthetics. Does not affect <code>ordinal</code> scales. May be overrided using the <a href=\"#method-setData\">setData</a> method with the <a href=\"#data-yMax\">yMax</a> data format key."
+ },
"paddingTop": {
- "type": "int",
+ "type": "Number",
"default": 0,
"desc": "Amount of space from the top edge of the svg element to the beginning of the <var>axisPaddingTop</var>."
},
"paddingRight": {
- "type": "int",
+ "type": "Number",
"default": 0,
"desc": "Amount of space from the right edge of the svg element to the beginning of the <var>axisPaddingRight</var>."
},
"paddingBottom": {
- "type": "int",
+ "type": "Number",
"default": 20,
"desc": "Allows space for the x-axis scale. Controls the amount of space from the bottom edge of the svg element to the beginning of the <var>axisPaddingBottom</var>."
},
"paddingLeft": {
- "type": "int",
+ "type": "Number",
"default": 60,
"desc": "Allows space for the y-axis scale. Amount of space from the left edge of the svg element to the beginning of the <var>axisPaddingLeft</var>."
},
"tickHintX": {
- "type": "int",
+ "type": "Number",
"default": 10,
"desc": "The amount of ticks that you would <em>like</em> to have displayed on the x-axis. Note: this is merely a guide and your results will likely vary."
},
@@ -81,7 +101,7 @@
}
},
"tickHintY": {
- "type": "int",
+ "type": "Number",
"default": 10,
"desc": "The amount of ticks that you would <em>like</em> to have displayed on the y-axis. Note: this is merely a guide and your results will likely vary."
},
@@ -142,7 +162,7 @@
}
},
"timing": {
- "type": "int",
+ "type": "Number",
"default": 750,
"desc": "The amount of time, in milliseconds, to transition during draw/update."
},
@@ -199,6 +219,22 @@
"desc": "Scale type to use along the y-axis (vertical).",
"options": ["ordinal", "linear", "time", "exponential"]
},
+ "xMin": {
+ "type": "Number",
+ "desc": "Minimum allowed value on the xScale. If <code>null</code>, uses the data's min value, logically padded for aesthetics. Does not affect <code>ordinal</code> scales."
+ },
+ "xMax": {
+ "type": "Number",
+ "desc": "Maximum allowed value on the xScale. If <code>null</code>, uses the data's max value, logically padded for aesthetics. Does not affect <code>ordinal</code> scales."
+ },
+ "yMin": {
+ "type": "Number",
+ "desc": "Minimum allowed value on the yScale. If <code>null</code>, uses the data's min value, logically padded for aesthetics. Does not affect <code>ordinal</code> scales."
+ },
+ "yMax": {
+ "type": "Number",
+ "desc": "Maximum allowed value on the yScale. If <code>null</code>, uses the data's max value, logically padded for aesthetics. Does not affect <code>ordinal</code> scales."
+ },
"type": {
"optional": true,
"type": "string",
View
11 lib/chart.js
@@ -23,6 +23,12 @@ var emptyData = [[]],
tickHintY: 10,
tickFormatY: function (y) { return y; },
+ // Min/Max Axis Values
+ xMin: null,
+ xMax: null,
+ yMin: null,
+ yMax: null,
+
// Pre-format input data
dataFormatX: function (x) { return x; },
dataFormatY: function (y) { return y; },
@@ -221,6 +227,11 @@ _.defaults(xChart.prototype, {
break;
}
+ o.xMin = data.xMin || o.xMin;
+ o.xMax = data.xMax || o.xMax;
+ o.yMin = data.yMin || o.yMin;
+ o.yMax = data.yMax || o.yMax;
+
if (self._vis) {
self._destroy(self._vis, self._mainStorage);
}
View
87 lib/scales.js
@@ -14,27 +14,6 @@ function _getDomain(data, axis) {
.sort(d3.ascending);
}
-function _extendDomain(domain, axis) {
- var min = domain[0],
- max = domain[1],
- diff,
- e;
-
- if (min === max) {
- e = Math.max(Math.round(min / 10), 4);
- min -= e;
- max += e;
- }
-
- diff = max - min;
- min = (min) ? min - (diff / 10) : min;
- min = (domain[0] > 0) ? Math.max(min, 0) : min;
- max = (max) ? max + (diff / 10) : max;
- max = (domain[1] < 0) ? Math.min(max, 0) : max;
-
- return [min, max];
-}
-
function ordinal(data, axis, bounds, spacing) {
spacing = spacing || defaultSpacing;
var domain = _getDomain(data, axis);
@@ -44,10 +23,6 @@ function ordinal(data, axis, bounds, spacing) {
}
function linear(extents, bounds, axis) {
- if (axis === 'y') {
- extents = _extendDomain(extents, axis);
- }
-
return d3.scale.linear()
.domain(extents)
.nice()
@@ -55,10 +30,6 @@ function linear(extents, bounds, axis) {
}
function exponential(extents, bounds, axis) {
- if (axis === 'y') {
- extents = _extendDomain(extents, axis);
- }
-
return d3.scale.pow()
.exponent(0.65)
.domain(extents)
@@ -72,22 +43,64 @@ function time(extents, bounds) {
.range(bounds);
}
-function _getExtents(data, key) {
- var nData = _.chain(data)
- .pluck('data')
- .flatten()
- .value();
+function _extendDomain(domain, axis) {
+ var min = domain[0],
+ max = domain[1],
+ diff,
+ e;
+
+ if (min === max) {
+ e = Math.max(Math.round(min / 10), 4);
+ min -= e;
+ max += e;
+ }
- return {
+ diff = max - min;
+ min = (min) ? min - (diff / 10) : min;
+ min = (domain[0] > 0) ? Math.max(min, 0) : min;
+ max = (max) ? max + (diff / 10) : max;
+ max = (domain[1] < 0) ? Math.min(max, 0) : max;
+
+ return [min, max];
+}
+
+function _getExtents(options, data, xType, yType) {
+ var extents,
+ nData = _.chain(data)
+ .pluck('data')
+ .flatten()
+ .value();
+
+ extents = {
x: d3.extent(nData, function (d) { return d.x; }),
y: d3.extent(nData, function (d) { return d.y; })
};
+
+ _.each([xType, yType], function (type, i) {
+ var axis = (i) ? 'y' : 'x',
+ extended;
+ extents[axis] = d3.extent(nData, function (d) { return d[axis]; });
+ if (type === 'ordinal') {
+ return;
+ }
+
+ _.each([axis + 'Min', axis + 'Max'], function (minMax, i) {
+ if (type !== 'time') {
+ extended = _extendDomain(extents[axis]);
+ }
+ extents[axis][i] = (options.hasOwnProperty(minMax) &&
+ options[minMax] !== null) ? options[minMax]
+ : (type !== 'time') ? extended[i] : extents[axis][i];
+ });
+ });
+
+ return extents;
}
function xy(self, data, xType, yType) {
- var extents = _getExtents(data),
+ var o = self._options,
+ extents = _getExtents(o, data, xType, yType),
scales = {},
- o = self._options,
horiz = [o.axisPaddingLeft, self._width],
vert = [self._height, o.axisPaddingTop],
xScale,
View
18 test/chart.test.js
@@ -61,7 +61,11 @@
tickHintX: 10,
tickHintY: 10,
timing: 750,
- interpolation: 'monotone'
+ interpolation: 'monotone',
+ xMin: null,
+ xMax: null,
+ yMin: null,
+ yMax: null
});
});
@@ -252,6 +256,18 @@
expect(c._mainData[0].data[0]).to.be.eql({ x: '1taco', y: 10 });
expect(c._mainData[0].data[1]).to.be.eql({ x: '2taco', y: 20 });
});
+
+ it('allows setting x/y Min/Max onto options', function () {
+ var c = new xChart('bar', mData, container),
+ newData = _.extend({}, mData, {
+ xMin: 1,
+ xMax: 10,
+ yMin: 2,
+ yMax: 8
+ });
+ c.setData(newData);
+ expect(c._options.xMin).to.equal(newData.xMin);
+ });
});
describe('setType(type)', function () {
View
72 test/scales.test.js
@@ -44,17 +44,10 @@
describe('Linear', function () {
it('returns a linear scale', function () {
- var s = scales.linear([10, 50], [0, 100], 'y');
+ var s = scales.linear([0, 60], [0, 100], 'y');
_testLinear(s);
});
- it('extends the domain if only 1 value', function () {
- var s = scales.linear([10, 10], [0, 100], 'y');
- expect(s.domain()).to.be.eql([5, 15]);
- s = scales.linear([100, 100], [0, 100], 'y');
- expect(s.domain()).to.be.eql([88, 112], 'uses scaled padding');
- });
-
it('does not round values', function () {
var s = scales.linear([1.1, 2.1], [0, 100], 'x');
expect(s(1.4)).to.not.equal(s(1.1));
@@ -65,9 +58,9 @@
describe('Exponential', function () {
it('returns an exponential scale', function () {
var s = scales.exponential([0, 500], [0, 100], 'y');
- expect(s(10)).to.be.eql(7); // would be 2 on linear
- expect(s(100)).to.be.eql(31); // would be 20 on linear
- expect(s(400)).to.be.eql(77); // would be 80 on linear
+ expect(s(10)).to.be.eql(8); // would be 2 on linear
+ expect(s(100)).to.be.eql(35); // would be 20 on linear
+ expect(s(400)).to.be.eql(86); // would be 80 on linear
});
});
@@ -78,17 +71,62 @@
describe('xy', function () {
it('returns multiple types', function () {
var foo = {
- _options: {
- axisPaddingLeft: 0,
- axisPaddingRight: 0
+ _options: {
+ axisPaddingLeft: 0,
+ axisPaddingRight: 0
+ },
+ _width: 100,
+ _height: 100
},
- _width: 100,
- _height: 100
- },
s = scales.xy(foo, data, 'ordinal', 'linear');
+ window.a = s;
_testOrdinal(s.x);
_testLinear(s.y);
});
+
+ it('uses xMin, xMax, yMin, and yMax in options', function () {
+ var foo = {
+ _options: {
+ xMin: -1,
+ xMax: 20,
+ yMin: 10,
+ yMax: 40
+ },
+ _width: 100,
+ _height: 100
+ },
+ data = [
+ { data: [ { x: 1, y: 50 }, { x: 1, y: 20 } ] },
+ { data: [ { x: 2, y: 50 }, { x: 2, y: 10 } ] }
+ ],
+ sOrdinal,
+ sLinear,
+ sTime;
+
+ sOrdinal = scales.xy(foo, data, 'ordinal', 'ordinal');
+ expect(sOrdinal.x.domain()).to.eql([1, 2]);
+ expect(sOrdinal.y.domain()).to.eql([10, 20, 50]);
+
+ sLinear = scales.xy(foo, data, 'linear', 'linear');
+ expect(sLinear.x.domain()).to.eql([foo._options.xMin, foo._options.xMax]);
+ expect(sLinear.y.domain()).to.eql([foo._options.yMin, foo._options.yMax]);
+
+ foo._options = {
+ xMin: new Date(2012, 1, 1),
+ xMax: new Date(2012, 10, 1),
+ yMin: new Date(2012, 2, 1),
+ yMax: new Date(2012, 11, 1)
+ };
+ data = [
+ { data: [ { x: new Date(), y: new Date() },
+ { x: new Date(), y: new Date() } ] },
+ { data: [ { x: new Date(), y: new Date() },
+ { x: new Date(), y: new Date() } ] }
+ ];
+ sTime = scales.xy(foo, data, 'time', 'time');
+ expect(sTime.x.domain()).to.eql([foo._options.xMin, foo._options.xMax]);
+ expect(sTime.y.domain()).to.eql([foo._options.yMin, foo._options.yMax]);
+ });
});
}());
Something went wrong with that request. Please try again.