Merge pull request #2680 from stormpython/fix/#1962
Add Negative Axes to Line, Bar, and Area Charts/Fix Pie Issues with 0 values
This commit is contained in:
commit
1d83784ba3
|
@ -237,5 +237,15 @@ define(function (require) {
|
|||
};
|
||||
inherits(errors.NoResults, KbnError);
|
||||
|
||||
/**
|
||||
* error thrown when no results are returned from an elasticsearch query
|
||||
*/
|
||||
errors.PieContainsAllZeros = function PieContainsAllZeros() {
|
||||
KbnError.call(this,
|
||||
'No results displayed because all values equal 0',
|
||||
errors.PieContainsAllZeros);
|
||||
};
|
||||
inherits(errors.PieContainsAllZeros, KbnError);
|
||||
|
||||
return errors;
|
||||
});
|
||||
|
|
|
@ -21,6 +21,7 @@ define(function (require) {
|
|||
return new Data(data, attr);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
var offset;
|
||||
|
||||
if (attr.mode === 'stacked') {
|
||||
|
@ -53,15 +54,141 @@ define(function (require) {
|
|||
this._normalizeOrdered();
|
||||
|
||||
this._attr = _.defaults(attr || {}, {
|
||||
|
||||
// d3 stack function
|
||||
stack: d3.layout.stack()
|
||||
.x(function (d) { return d.x; })
|
||||
.y(function (d) { return d.y; })
|
||||
.y(function (d) {
|
||||
if (offset === 'expand') {
|
||||
return Math.abs(d.y);
|
||||
}
|
||||
return d.y;
|
||||
})
|
||||
.offset(offset || 'zero')
|
||||
});
|
||||
|
||||
if (attr.mode === 'stacked' && attr.type === 'histogram') {
|
||||
this._attr.stack.out(function (d, y0, y) {
|
||||
return self._stackNegAndPosVals(d, y0, y);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true for positive numbers
|
||||
*/
|
||||
Data.prototype._isPositive = function (num) {
|
||||
return num >= 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true for negative numbers
|
||||
*/
|
||||
Data.prototype._isNegative = function (num) {
|
||||
return num < 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds two input values
|
||||
*/
|
||||
Data.prototype._addVals = function (a, b) {
|
||||
return a + b;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the results of the addition of numbers in a filtered array.
|
||||
*/
|
||||
Data.prototype._sumYs = function (arr, callback) {
|
||||
var filteredArray = arr.filter(callback);
|
||||
|
||||
return (filteredArray.length) ? filteredArray.reduce(this._addVals) : 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculates the d.y0 value for stacked data in D3.
|
||||
*/
|
||||
Data.prototype._calcYZero = function (y, arr) {
|
||||
if (y >= 0) return this._sumYs(arr, this._isPositive);
|
||||
return this._sumYs(arr, this._isNegative);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
Data.prototype._getCounts = function (i, j) {
|
||||
var data = this.chartData();
|
||||
var dataLengths = {};
|
||||
|
||||
dataLengths.charts = data.length;
|
||||
dataLengths.stacks = data[i].series.length;
|
||||
dataLengths.values = data[i].series[j].values.length;
|
||||
|
||||
return dataLengths;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
Data.prototype._createCache = function () {
|
||||
var cache = {
|
||||
index: {
|
||||
chart: 0,
|
||||
stack: 0,
|
||||
value: 0
|
||||
},
|
||||
yValsArr: []
|
||||
};
|
||||
|
||||
cache.count = this._getCounts(cache.index.chart, cache.index.stack);
|
||||
|
||||
return cache;
|
||||
};
|
||||
|
||||
/**
|
||||
* Stacking function passed to the D3 Stack Layout `.out` API.
|
||||
* See: https://github.com/mbostock/d3/wiki/Stack-Layout
|
||||
* It is responsible for calculating the correct d.y0 value for
|
||||
* mixed datasets containing both positive and negative values.
|
||||
*/
|
||||
Data.prototype._stackNegAndPosVals = function (d, y0, y) {
|
||||
var data = this.chartData();
|
||||
|
||||
// Storing counters and data characteristics needed to stack values properly
|
||||
if (!this._cache) {
|
||||
this._cache = this._createCache();
|
||||
}
|
||||
|
||||
d.y0 = this._calcYZero(y, this._cache.yValsArr);
|
||||
++this._cache.index.stack;
|
||||
|
||||
|
||||
// last stack, or last value, reset the stack count and y value array
|
||||
var lastStack = (this._cache.index.stack >= this._cache.count.stacks);
|
||||
if (lastStack) {
|
||||
this._cache.index.stack = 0;
|
||||
++this._cache.index.value;
|
||||
this._cache.yValsArr = [];
|
||||
// still building the stack collection, push v value to array
|
||||
} else if (y !== 0) {
|
||||
this._cache.yValsArr.push(y);
|
||||
}
|
||||
|
||||
// last value, prepare for the next chart, if one exists
|
||||
var lastValue = (this._cache.index.value >= this._cache.count.values);
|
||||
if (lastValue) {
|
||||
this._cache.index.value = 0;
|
||||
++this._cache.index.chart;
|
||||
|
||||
// no more charts, reset the queue and finish
|
||||
if (this._cache.index.chart >= this._cache.count.charts) {
|
||||
this._cache = this._createCache();
|
||||
return;
|
||||
}
|
||||
|
||||
// get stack and value count for next chart
|
||||
this._cache.count.stacks = data[this._cache.index.chart].series.length; // number of stack layers
|
||||
this._cache.count.values = data[this._cache.index.chart].series[this._cache.index.stack].values.length; // number of values
|
||||
}
|
||||
};
|
||||
|
||||
Data.prototype.getDataType = function () {
|
||||
var data = this.getVisData();
|
||||
var type;
|
||||
|
@ -160,15 +287,12 @@ define(function (require) {
|
|||
* @returns {Array} Value objects
|
||||
*/
|
||||
Data.prototype.flatten = function () {
|
||||
var data = this.chartData();
|
||||
var series = _.chain(data).pluck('series').pluck().value();
|
||||
var values = [];
|
||||
|
||||
series.forEach(function (d) {
|
||||
values.push(_.chain(d).flatten().pluck('values').value());
|
||||
});
|
||||
|
||||
return values;
|
||||
return _(this.chartData())
|
||||
.pluck('series')
|
||||
.flatten()
|
||||
.pluck('values')
|
||||
.flatten()
|
||||
.value();
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -176,16 +300,66 @@ define(function (require) {
|
|||
* TODO: need to make this more generic
|
||||
*
|
||||
* @method shouldBeStacked
|
||||
* @param series {Array} Array of data objects
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Data.prototype.shouldBeStacked = function (series) {
|
||||
Data.prototype.shouldBeStacked = function () {
|
||||
var isHistogram = (this._attr.type === 'histogram');
|
||||
var isArea = (this._attr.type === 'area');
|
||||
var isOverlapping = (this._attr.mode === 'overlap');
|
||||
var grouped = (this._attr.mode === 'grouped');
|
||||
|
||||
// Series should be an array
|
||||
return (isHistogram || isArea && !isOverlapping && series.length > 1);
|
||||
var stackedHisto = isHistogram && !grouped;
|
||||
var stackedArea = isArea && !isOverlapping;
|
||||
|
||||
return stackedHisto || stackedArea;
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates that the Y axis min value defined by user input
|
||||
* is a number.
|
||||
*
|
||||
* @param val {Number} Y axis min value
|
||||
* @returns {Number} Y axis min value
|
||||
*/
|
||||
Data.prototype.validateUserDefinedYMin = function (val) {
|
||||
if (!_.isNumber(val)) {
|
||||
throw new Error('validateUserDefinedYMin expects a number');
|
||||
}
|
||||
return val;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculates the min y value from this.dataArray
|
||||
* for each object in the dataArray.
|
||||
*
|
||||
* @method getYMinValue
|
||||
* @returns {Number} Min y axis value
|
||||
*/
|
||||
Data.prototype.getYMinValue = function () {
|
||||
var self = this;
|
||||
var arr = [];
|
||||
|
||||
if (this._attr.mode === 'percentage' || this._attr.mode === 'wiggle' ||
|
||||
this._attr.mode === 'silhouette') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var flat = this.flatten();
|
||||
// if there is only one data point and its less than zero,
|
||||
// return 0 as the yMax value.
|
||||
if (!flat.length || flat.length === 1 && flat[0].y > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var min = Infinity;
|
||||
|
||||
// for each object in the dataArray,
|
||||
// push the calculated y value to the initialized array (arr)
|
||||
_.each(this.chartData(), function (chart) {
|
||||
min = Math.min(min, self._getYExtent(chart, 'min'));
|
||||
});
|
||||
|
||||
return min;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -200,22 +374,27 @@ define(function (require) {
|
|||
Data.prototype.getYMaxValue = function () {
|
||||
var self = this;
|
||||
var arr = [];
|
||||
var grouped = (self._attr.mode === 'grouped');
|
||||
|
||||
if (self._attr.mode === 'percentage') {
|
||||
return 1;
|
||||
}
|
||||
|
||||
var flat = this.flatten();
|
||||
// if there is only one data point and its less than zero,
|
||||
// return 0 as the yMax value.
|
||||
if (!flat.length || flat.length === 1 && flat[0].y < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var max = -Infinity;
|
||||
|
||||
// for each object in the dataArray,
|
||||
// push the calculated y value to the initialized array (arr)
|
||||
_.forEach(this.flatten(), function (series) {
|
||||
if (self.shouldBeStacked(series) && !grouped) {
|
||||
return arr.push(self._getYMax(series, self._getYStack));
|
||||
}
|
||||
return arr.push(self._getYMax(series, self._getY));
|
||||
_.each(this.chartData(), function (chart) {
|
||||
max = Math.max(max, self._getYExtent(chart, 'max'));
|
||||
});
|
||||
|
||||
return _.max(arr);
|
||||
return max;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -235,10 +414,21 @@ define(function (require) {
|
|||
* Returns the max Y axis value for a `series` array based on
|
||||
* a specified callback function (calculation).
|
||||
*/
|
||||
Data.prototype._getYMax = function (series, calculation) {
|
||||
return d3.max(this.stackData(series), function (data) {
|
||||
return d3.max(data, calculation);
|
||||
});
|
||||
Data.prototype._getYExtent = function (chart, extent) {
|
||||
var calculation = this._getY;
|
||||
|
||||
if (this.shouldBeStacked()) {
|
||||
this.stackData(_.pluck(chart.series, 'values'));
|
||||
calculation = this._getYStack;
|
||||
}
|
||||
|
||||
var points = chart.series
|
||||
.reduce(function (points, series) {
|
||||
return points.concat(series.values);
|
||||
}, [])
|
||||
.map(calculation);
|
||||
|
||||
return d3[extent](points);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -37,6 +37,7 @@ define(function (require) {
|
|||
}),
|
||||
yAxis: new YAxis({
|
||||
el : vis.el,
|
||||
yMin : data.getYMinValue(),
|
||||
yMax : data.getYMaxValue(),
|
||||
_attr: vis._attr
|
||||
})
|
||||
|
|
|
@ -15,8 +15,9 @@ define(function (require) {
|
|||
*/
|
||||
function YAxis(args) {
|
||||
this.el = args.el;
|
||||
this.yMin = args.yMin;
|
||||
this.yMax = args.yMax;
|
||||
this._attr = _.defaults(args._attr || {}, {});
|
||||
this._attr = args._attr || {};
|
||||
}
|
||||
|
||||
_(YAxis.prototype).extend(ErrorHandler.prototype);
|
||||
|
@ -39,9 +40,37 @@ define(function (require) {
|
|||
* @returns {D3.Scale.QuantitiveScale|*} D3 yScale function
|
||||
*/
|
||||
YAxis.prototype.getYScale = function (height) {
|
||||
|
||||
// yMin and yMax can never be equal for the axis
|
||||
// to render. Defaults yMin to 0 if yMin === yMax
|
||||
// and yMin is greater than or equal to zero, else
|
||||
// defaults yMax to zero.
|
||||
if (this.yMin === this.yMax) {
|
||||
if (this.yMin > 0) {
|
||||
this.yMin = 0;
|
||||
} else if (this.yMin === 0) {
|
||||
this.yMin = -1;
|
||||
this.yMax = 1;
|
||||
} else {
|
||||
this.yMax = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this._attr.defaultYExtents) {
|
||||
// if yMin and yMax are both positive, then yMin should be zero
|
||||
if (this.yMin > 0 && this.yMax > 0) {
|
||||
this.yMin = 0;
|
||||
}
|
||||
|
||||
// if yMin and yMax are both negative, then yMax should be zero
|
||||
if (this.yMin < 0 && this.yMax < 0) {
|
||||
this.yMax = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// save reference to y scale
|
||||
this.yScale = d3.scale.linear()
|
||||
.domain([0, this.yMax])
|
||||
.domain([this.yMin, this.yMax])
|
||||
.range([height, 0])
|
||||
.nice(this.tickScale(height));
|
||||
|
||||
|
@ -76,7 +105,7 @@ define(function (require) {
|
|||
|
||||
if (isPercentage) {
|
||||
tickFormat = d3.format('%');
|
||||
} else if (this.yMax <= 100 && !isPercentage) {
|
||||
} else if (this.yMax <= 100 && this.yMin >= -100 && !isPercentage) {
|
||||
tickFormat = d3.format('n');
|
||||
} else {
|
||||
tickFormat = this.formatAxisLabel;
|
||||
|
|
|
@ -25,7 +25,7 @@ define(function (require) {
|
|||
Vis.Super.apply(this, arguments);
|
||||
this.el = $el.get ? $el.get(0) : $el;
|
||||
this.ChartClass = chartTypes[config.type];
|
||||
this._attr = _.defaults(config || {}, {});
|
||||
this._attr = _.defaults({}, config || {}, {});
|
||||
this.eventTypes = {
|
||||
enabled: []
|
||||
};
|
||||
|
@ -80,6 +80,7 @@ define(function (require) {
|
|||
// Because we have to wait for the DOM element to initialize, we do not
|
||||
// want to throw an error when the DOM `el` is zero
|
||||
if (error instanceof errors.ContainerTooSmall ||
|
||||
error instanceof errors.PieContainsAllZeros ||
|
||||
error instanceof errors.NotEnoughData ||
|
||||
error instanceof errors.NoResults) {
|
||||
this.handler.error(error.message);
|
||||
|
|
|
@ -58,7 +58,6 @@ define(function (require) {
|
|||
var color = this.handler.data.getColorFunc();
|
||||
var xScale = this.handler.xAxis.xScale;
|
||||
var yScale = this.handler.yAxis.yScale;
|
||||
var height = yScale.range()[0];
|
||||
var defaultOpacity = this._attr.defaultOpacity;
|
||||
|
||||
var area = d3.svg.area()
|
||||
|
@ -70,14 +69,16 @@ define(function (require) {
|
|||
})
|
||||
.y0(function (d) {
|
||||
if (isOverlapping) {
|
||||
return height;
|
||||
return yScale(0);
|
||||
}
|
||||
|
||||
return yScale(d.y0);
|
||||
})
|
||||
.y1(function (d) {
|
||||
if (isOverlapping) {
|
||||
return yScale(d.y);
|
||||
}
|
||||
|
||||
return yScale(d.y0 + d.y);
|
||||
});
|
||||
|
||||
|
@ -268,6 +269,8 @@ define(function (require) {
|
|||
var margin = this._attr.margin;
|
||||
var elWidth = this._attr.width = $elem.width();
|
||||
var elHeight = this._attr.height = $elem.height();
|
||||
var yMin = this.handler.yAxis.yMin;
|
||||
var yScale = this.handler.yAxis.yScale;
|
||||
var minWidth = 20;
|
||||
var minHeight = 20;
|
||||
var div;
|
||||
|
@ -308,6 +311,19 @@ define(function (require) {
|
|||
// add path
|
||||
path = self.addPath(svg, layers);
|
||||
|
||||
if (yMin < 0 && self._attr.mode !== 'wiggle' && self._attr.mode !== 'silhouette') {
|
||||
|
||||
// Draw line at yScale 0 value
|
||||
svg.append('line')
|
||||
.attr('class', 'zero-line')
|
||||
.attr('x1', 0)
|
||||
.attr('y1', yScale(0))
|
||||
.attr('x2', width)
|
||||
.attr('y2', yScale(0))
|
||||
.style('stroke', '#ddd')
|
||||
.style('stroke-width', 1);
|
||||
}
|
||||
|
||||
// add circles
|
||||
circles = self.addCircles(svg, layers);
|
||||
|
||||
|
@ -316,6 +332,7 @@ define(function (require) {
|
|||
|
||||
// chart base line
|
||||
var line = svg.append('line')
|
||||
.attr('class', 'base-line')
|
||||
.attr('x1', 0)
|
||||
.attr('y1', height)
|
||||
.attr('x2', width)
|
||||
|
|
|
@ -113,6 +113,9 @@ define(function (require) {
|
|||
var data = this.chartData;
|
||||
var xScale = this.handler.xAxis.xScale;
|
||||
var yScale = this.handler.yAxis.yScale;
|
||||
var height = yScale.range()[0];
|
||||
var yMin = this.handler.yAxis.yScale.domain()[0];
|
||||
var self = this;
|
||||
|
||||
var barWidth;
|
||||
if (data.ordered && data.ordered.date) {
|
||||
|
@ -132,9 +135,23 @@ define(function (require) {
|
|||
return barWidth || xScale.rangeBand();
|
||||
})
|
||||
.attr('y', function (d) {
|
||||
if (d.y < 0) {
|
||||
return yScale(d.y0);
|
||||
}
|
||||
|
||||
return yScale(d.y0 + d.y);
|
||||
})
|
||||
.attr('height', function (d) {
|
||||
if (d.y < 0) {
|
||||
return Math.abs(yScale(d.y0 + d.y) - yScale(d.y0));
|
||||
}
|
||||
|
||||
// for split bars or for one series,
|
||||
// last series will have d.y0 = 0
|
||||
if (d.y0 === 0 && yMin > 0) {
|
||||
return yScale(yMin) - yScale(d.y);
|
||||
}
|
||||
|
||||
return yScale(d.y0) - yScale(d.y0 + d.y);
|
||||
});
|
||||
|
||||
|
@ -151,6 +168,7 @@ define(function (require) {
|
|||
ColumnChart.prototype.addGroupedBars = function (bars) {
|
||||
var xScale = this.handler.xAxis.xScale;
|
||||
var yScale = this.handler.yAxis.yScale;
|
||||
var yMin = this.handler.yAxis.yMin;
|
||||
var data = this.chartData;
|
||||
var n = data.series.length;
|
||||
var height = yScale.range()[0];
|
||||
|
@ -184,9 +202,22 @@ define(function (require) {
|
|||
return xScale.rangeBand() / n;
|
||||
})
|
||||
.attr('y', function (d) {
|
||||
if (d.y < 0) {
|
||||
return yScale(0);
|
||||
}
|
||||
|
||||
return yScale(d.y);
|
||||
})
|
||||
.attr('height', function (d) {
|
||||
if (d.y < 0) {
|
||||
return Math.abs(yScale(0) - yScale(d.y));
|
||||
}
|
||||
|
||||
// if there is a negative yMin value, use yScale(0) instead of height
|
||||
if (yMin < 0) {
|
||||
return yScale(0) - yScale(d.y);
|
||||
}
|
||||
|
||||
return height - yScale(d.y);
|
||||
});
|
||||
|
||||
|
@ -230,6 +261,8 @@ define(function (require) {
|
|||
var margin = this._attr.margin;
|
||||
var elWidth = this._attr.width = $elem.width();
|
||||
var elHeight = this._attr.height = $elem.height();
|
||||
var yMin = this.handler.yAxis.yMin;
|
||||
var yScale = this.handler.yAxis.yScale;
|
||||
var minWidth = 20;
|
||||
var minHeight = 20;
|
||||
var div;
|
||||
|
@ -265,6 +298,7 @@ define(function (require) {
|
|||
self.addBarEvents(bars, svg);
|
||||
|
||||
var line = svg.append('line')
|
||||
.attr('class', 'base-line')
|
||||
.attr('x1', 0)
|
||||
.attr('y1', height)
|
||||
.attr('x2', width)
|
||||
|
@ -272,6 +306,19 @@ define(function (require) {
|
|||
.style('stroke', '#ddd')
|
||||
.style('stroke-width', 1);
|
||||
|
||||
if (yMin < 0) {
|
||||
|
||||
// Draw line at yScale 0 value
|
||||
svg.append('line')
|
||||
.attr('class', 'zero-line')
|
||||
.attr('x1', 0)
|
||||
.attr('y1', yScale(0))
|
||||
.attr('x2', width)
|
||||
.attr('y2', yScale(0))
|
||||
.style('stroke', '#ddd')
|
||||
.style('stroke-width', 1);
|
||||
}
|
||||
|
||||
return svg;
|
||||
});
|
||||
};
|
||||
|
|
|
@ -96,7 +96,7 @@ define(function (require) {
|
|||
.enter()
|
||||
.append('circle')
|
||||
.attr('class', function circleClass(d) {
|
||||
return self.colorToClass(color(d.label));
|
||||
return 'circle ' + self.colorToClass(color(d.label));
|
||||
})
|
||||
.attr('fill', function (d) {
|
||||
return color(d.label);
|
||||
|
@ -217,8 +217,8 @@ define(function (require) {
|
|||
var margin = this._attr.margin;
|
||||
var elWidth = this._attr.width = $elem.width();
|
||||
var elHeight = this._attr.height = $elem.height();
|
||||
var xScale = this.handler.xAxis.xScale;
|
||||
var chartToSmallError = 'The height and/or width of this container is too small for this chart.';
|
||||
var yMin = this.handler.yAxis.yMin;
|
||||
var yScale = this.handler.yAxis.yScale;
|
||||
var minWidth = 20;
|
||||
var minHeight = 20;
|
||||
var startLineX = 0;
|
||||
|
@ -261,6 +261,19 @@ define(function (require) {
|
|||
.append('g')
|
||||
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
|
||||
|
||||
if (yMin < 0) {
|
||||
|
||||
// Draw line at yScale 0 value
|
||||
svg.append('line')
|
||||
.attr('class', 'zero-line')
|
||||
.attr('x1', 0)
|
||||
.attr('y1', yScale(0))
|
||||
.attr('x2', width)
|
||||
.attr('y2', yScale(0))
|
||||
.style('stroke', '#ddd')
|
||||
.style('stroke-width', 1);
|
||||
}
|
||||
|
||||
self.addClipPath(svg, width, height);
|
||||
lines = self.addLines(svg, data.series);
|
||||
circles = self.addCircles(svg, layers);
|
||||
|
@ -269,6 +282,7 @@ define(function (require) {
|
|||
|
||||
var line = svg
|
||||
.append('line')
|
||||
.attr('class', 'base-line')
|
||||
.attr('x1', startLineX)
|
||||
.attr('y1', height)
|
||||
.attr('x2', width)
|
||||
|
@ -276,6 +290,7 @@ define(function (require) {
|
|||
.style('stroke', '#ddd')
|
||||
.style('stroke-width', lineStrokeWidth);
|
||||
|
||||
|
||||
return svg;
|
||||
});
|
||||
};
|
||||
|
|
|
@ -24,8 +24,11 @@ define(function (require) {
|
|||
}
|
||||
PieChart.Super.apply(this, arguments);
|
||||
|
||||
// Check whether pie chart should be rendered.
|
||||
this._validatePieData();
|
||||
|
||||
this._attr = _.defaults(handler._attr || {}, {
|
||||
isDonut: handler._attr.isDonut || false,
|
||||
isDonut: handler._attr.isDonut || false
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -61,6 +64,7 @@ define(function (require) {
|
|||
var partition = d3.layout.partition()
|
||||
.sort(null)
|
||||
.value(function (d) {
|
||||
if (d.size === 0) return;
|
||||
return Math.abs(d.size);
|
||||
});
|
||||
var x = d3.scale.linear()
|
||||
|
@ -109,7 +113,6 @@ define(function (require) {
|
|||
.style('stroke', '#fff')
|
||||
.style('fill', function (d) {
|
||||
if (d.depth === 0) { return 'none'; }
|
||||
|
||||
return color(format(d, d.name));
|
||||
});
|
||||
|
||||
|
@ -120,6 +123,29 @@ define(function (require) {
|
|||
return path;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether all pie slices have zero values.
|
||||
* If so, an error is thrown.
|
||||
*/
|
||||
PieChart.prototype._validatePieData = function () {
|
||||
this.chartData.slices = (function withoutZeroSlices(slices) {
|
||||
if (!slices.children) return slices;
|
||||
|
||||
slices = _.clone(slices);
|
||||
slices.children = slices.children.reduce(function (children, child) {
|
||||
if (child.size !== 0) {
|
||||
children.push(withoutZeroSlices(child));
|
||||
}
|
||||
return children;
|
||||
}, []);
|
||||
return slices;
|
||||
}(this.chartData.slices));
|
||||
|
||||
if (this.chartData.slices.children.length === 0) {
|
||||
throw new errors.PieContainsAllZeros();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders d3 visualization
|
||||
*
|
||||
|
|
|
@ -11,4 +11,10 @@
|
|||
Show Legend
|
||||
</label>
|
||||
</div>
|
||||
<div class="vis-option-item">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="vis.params.defaultYExtents" ng-checked="vis.params.defaultYExtents">
|
||||
Scale Y-Axis to Data Bounds
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
|
@ -16,7 +16,8 @@ define(function (require) {
|
|||
shareYAxis: true,
|
||||
addTooltip: true,
|
||||
addLegend: true,
|
||||
mode: 'stacked'
|
||||
mode: 'stacked',
|
||||
defaultYExtents: false
|
||||
},
|
||||
modes: ['stacked', 'overlap', 'percentage', 'wiggle', 'silhouette'],
|
||||
editor: require('text!plugins/vis_types/vislib/editors/area.html')
|
||||
|
|
|
@ -5,5 +5,4 @@
|
|||
</label>
|
||||
<select class="form-control" ng-model="vis.params.mode" ng-options="mode for mode in vis.type.params.modes"></select>
|
||||
</div>
|
||||
|
||||
<vislib-basic-options></vislib-basic-options>
|
||||
|
|
|
@ -14,7 +14,8 @@ define(function (require) {
|
|||
shareYAxis: true,
|
||||
addTooltip: true,
|
||||
addLegend: true,
|
||||
mode: 'stacked'
|
||||
mode: 'stacked',
|
||||
defaultYExtents: false
|
||||
},
|
||||
modes: ['stacked', 'percentage', 'grouped'],
|
||||
editor: require('text!plugins/vis_types/vislib/editors/histogram.html')
|
||||
|
|
|
@ -14,6 +14,7 @@ define(function (require) {
|
|||
shareYAxis: true,
|
||||
addTooltip: true,
|
||||
addLegend: true,
|
||||
defaultYExtents: false
|
||||
},
|
||||
editor: require('text!plugins/vis_types/vislib/editors/basic.html')
|
||||
},
|
||||
|
|
|
@ -24,7 +24,6 @@ define(function (require) {
|
|||
Renderbot = Private(require('plugins/vis_types/_renderbot'));
|
||||
VislibRenderbot = Private(require('plugins/vis_types/vislib/_vislib_renderbot'));
|
||||
normalizeChartData = Private(require('components/agg_response/index'));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -85,7 +84,7 @@ define(function (require) {
|
|||
}
|
||||
}, mockVisType)
|
||||
};
|
||||
var $el = 'element';
|
||||
var $el = $('<div>testing</div>');
|
||||
var createVisSpy;
|
||||
var getParamsStub;
|
||||
var renderbot;
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
define(function (require) {
|
||||
var moment = require('moment');
|
||||
|
||||
return {
|
||||
'label': '',
|
||||
'xAxisLabel': '@timestamp per 30 sec',
|
||||
'ordered': {
|
||||
'date': true,
|
||||
'min': 1411761457636,
|
||||
'max': 1411762357636,
|
||||
'interval': 30000
|
||||
},
|
||||
'yAxisLabel': 'Count of documents',
|
||||
'series': [
|
||||
{
|
||||
'values': [
|
||||
{
|
||||
'x': 1411761450000,
|
||||
'y': -41
|
||||
},
|
||||
{
|
||||
'x': 1411761480000,
|
||||
'y': -18
|
||||
},
|
||||
{
|
||||
'x': 1411761510000,
|
||||
'y': -22
|
||||
},
|
||||
{
|
||||
'x': 1411761540000,
|
||||
'y': -17
|
||||
},
|
||||
{
|
||||
'x': 1411761570000,
|
||||
'y': -17
|
||||
},
|
||||
{
|
||||
'x': 1411761600000,
|
||||
'y': -21
|
||||
},
|
||||
{
|
||||
'x': 1411761630000,
|
||||
'y': -16
|
||||
},
|
||||
{
|
||||
'x': 1411761660000,
|
||||
'y': -17
|
||||
},
|
||||
{
|
||||
'x': 1411761690000,
|
||||
'y': -15
|
||||
},
|
||||
{
|
||||
'x': 1411761720000,
|
||||
'y': -19
|
||||
},
|
||||
{
|
||||
'x': 1411761750000,
|
||||
'y': -11
|
||||
},
|
||||
{
|
||||
'x': 1411761780000,
|
||||
'y': -13
|
||||
},
|
||||
{
|
||||
'x': 1411761810000,
|
||||
'y': -24
|
||||
},
|
||||
{
|
||||
'x': 1411761840000,
|
||||
'y': -20
|
||||
},
|
||||
{
|
||||
'x': 1411761870000,
|
||||
'y': -20
|
||||
},
|
||||
{
|
||||
'x': 1411761900000,
|
||||
'y': -21
|
||||
},
|
||||
{
|
||||
'x': 1411761930000,
|
||||
'y': -17
|
||||
},
|
||||
{
|
||||
'x': 1411761960000,
|
||||
'y': -20
|
||||
},
|
||||
{
|
||||
'x': 1411761990000,
|
||||
'y': -13
|
||||
},
|
||||
{
|
||||
'x': 1411762020000,
|
||||
'y': -14
|
||||
},
|
||||
{
|
||||
'x': 1411762050000,
|
||||
'y': -25
|
||||
},
|
||||
{
|
||||
'x': 1411762080000,
|
||||
'y': -17
|
||||
},
|
||||
{
|
||||
'x': 1411762110000,
|
||||
'y': -14
|
||||
},
|
||||
{
|
||||
'x': 1411762140000,
|
||||
'y': -22
|
||||
},
|
||||
{
|
||||
'x': 1411762170000,
|
||||
'y': -14
|
||||
},
|
||||
{
|
||||
'x': 1411762200000,
|
||||
'y': -19
|
||||
},
|
||||
{
|
||||
'x': 1411762230000,
|
||||
'y': -22
|
||||
},
|
||||
{
|
||||
'x': 1411762260000,
|
||||
'y': -17
|
||||
},
|
||||
{
|
||||
'x': 1411762290000,
|
||||
'y': -8
|
||||
},
|
||||
{
|
||||
'x': 1411762320000,
|
||||
'y': -15
|
||||
},
|
||||
{
|
||||
'x': 1411762350000,
|
||||
'y': -4
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
'hits': 533,
|
||||
'xAxisFormatter': function (thing) {
|
||||
return moment(thing);
|
||||
},
|
||||
'tooltipFormatter': function (d) {
|
||||
return d;
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,152 @@
|
|||
define(function (require) {
|
||||
var moment = require('moment');
|
||||
|
||||
return {
|
||||
'label': '',
|
||||
'xAxisLabel': '@timestamp per 30 sec',
|
||||
'ordered': {
|
||||
'date': true,
|
||||
'min': 1411761457636,
|
||||
'max': 1411762357636,
|
||||
'interval': 30000
|
||||
},
|
||||
'yAxisLabel': 'Count of documents',
|
||||
'series': [
|
||||
{
|
||||
'values': [
|
||||
{
|
||||
'x': 1411761450000,
|
||||
'y': 41
|
||||
},
|
||||
{
|
||||
'x': 1411761480000,
|
||||
'y': 18
|
||||
},
|
||||
{
|
||||
'x': 1411761510000,
|
||||
'y': -22
|
||||
},
|
||||
{
|
||||
'x': 1411761540000,
|
||||
'y': -17
|
||||
},
|
||||
{
|
||||
'x': 1411761570000,
|
||||
'y': -17
|
||||
},
|
||||
{
|
||||
'x': 1411761600000,
|
||||
'y': -21
|
||||
},
|
||||
{
|
||||
'x': 1411761630000,
|
||||
'y': -16
|
||||
},
|
||||
{
|
||||
'x': 1411761660000,
|
||||
'y': 17
|
||||
},
|
||||
{
|
||||
'x': 1411761690000,
|
||||
'y': 15
|
||||
},
|
||||
{
|
||||
'x': 1411761720000,
|
||||
'y': 19
|
||||
},
|
||||
{
|
||||
'x': 1411761750000,
|
||||
'y': 11
|
||||
},
|
||||
{
|
||||
'x': 1411761780000,
|
||||
'y': -13
|
||||
},
|
||||
{
|
||||
'x': 1411761810000,
|
||||
'y': -24
|
||||
},
|
||||
{
|
||||
'x': 1411761840000,
|
||||
'y': -20
|
||||
},
|
||||
{
|
||||
'x': 1411761870000,
|
||||
'y': -20
|
||||
},
|
||||
{
|
||||
'x': 1411761900000,
|
||||
'y': -21
|
||||
},
|
||||
{
|
||||
'x': 1411761930000,
|
||||
'y': 17
|
||||
},
|
||||
{
|
||||
'x': 1411761960000,
|
||||
'y': 20
|
||||
},
|
||||
{
|
||||
'x': 1411761990000,
|
||||
'y': -13
|
||||
},
|
||||
{
|
||||
'x': 1411762020000,
|
||||
'y': -14
|
||||
},
|
||||
{
|
||||
'x': 1411762050000,
|
||||
'y': 25
|
||||
},
|
||||
{
|
||||
'x': 1411762080000,
|
||||
'y': -17
|
||||
},
|
||||
{
|
||||
'x': 1411762110000,
|
||||
'y': -14
|
||||
},
|
||||
{
|
||||
'x': 1411762140000,
|
||||
'y': -22
|
||||
},
|
||||
{
|
||||
'x': 1411762170000,
|
||||
'y': -14
|
||||
},
|
||||
{
|
||||
'x': 1411762200000,
|
||||
'y': 19
|
||||
},
|
||||
{
|
||||
'x': 1411762230000,
|
||||
'y': 22
|
||||
},
|
||||
{
|
||||
'x': 1411762260000,
|
||||
'y': 17
|
||||
},
|
||||
{
|
||||
'x': 1411762290000,
|
||||
'y': 8
|
||||
},
|
||||
{
|
||||
'x': 1411762320000,
|
||||
'y': -15
|
||||
},
|
||||
{
|
||||
'x': 1411762350000,
|
||||
'y': -4
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
'hits': 533,
|
||||
'xAxisFormatter': function (thing) {
|
||||
return moment(thing);
|
||||
},
|
||||
'tooltipFormatter': function (d) {
|
||||
return d;
|
||||
}
|
||||
};
|
||||
});
|
|
@ -230,20 +230,68 @@ define(function (require) {
|
|||
colOut = colIn.flatten();
|
||||
});
|
||||
|
||||
it('should return an array of arrays', function () {
|
||||
expect(_.isArray(serOut)).to.be(true);
|
||||
it('should return an array of value objects from every series', function () {
|
||||
expect(serOut.every(_.isObject)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return array length 3', function () {
|
||||
expect(serOut[0][0].length).to.be(3);
|
||||
function testLength(inputData) {
|
||||
return function () {
|
||||
var data = new dataFactory(inputData, {});
|
||||
var len = _.reduce(data.chartData(), function (sum, chart) {
|
||||
return sum + chart.series.reduce(function (sum, series) {
|
||||
return sum + series.values.length;
|
||||
}, 0);
|
||||
}, 0);
|
||||
|
||||
expect(data.flatten()).to.have.length(len);
|
||||
};
|
||||
}
|
||||
|
||||
it('should return all points from every series', testLength(seriesData));
|
||||
it('should return all points from every series', testLength(rowsData));
|
||||
it('should return all points from every series', testLength(colsData));
|
||||
});
|
||||
|
||||
describe('getYMinValue method', function () {
|
||||
var Data;
|
||||
var dataSeries;
|
||||
var stackedDataSeries;
|
||||
var visData;
|
||||
var stackedVisData;
|
||||
var series;
|
||||
var stackedSeries;
|
||||
var minValue;
|
||||
var stackedMinValue;
|
||||
|
||||
beforeEach(function () {
|
||||
module('DataFactory');
|
||||
});
|
||||
|
||||
it('should return array length 3', function () {
|
||||
expect(rowOut[0][0].length).to.be(3);
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
Data = Private(require('components/vislib/lib/data'));
|
||||
dataSeries = require('vislib_fixtures/mock_data/date_histogram/_series');
|
||||
stackedDataSeries = require('vislib_fixtures/mock_data/stacked/_stacked');
|
||||
visData = new Data(dataSeries, {});
|
||||
stackedVisData = new Data(stackedDataSeries, { type: 'histogram' });
|
||||
series = _.pluck(visData.chartData(), 'series');
|
||||
stackedSeries = _.pluck(stackedVisData.chartData(), 'series');
|
||||
minValue = 4;
|
||||
stackedMinValue = 15;
|
||||
});
|
||||
});
|
||||
|
||||
it('should return array length 3', function () {
|
||||
expect(colOut[0][0].length).to.be(3);
|
||||
// The first value in the time series is less than the min date in the
|
||||
// date range. It also has the largest y value. This value should be excluded
|
||||
// when calculating the Y max value since it falls outside of the range.
|
||||
it('should return the Y domain min value', function () {
|
||||
expect(visData.getYMinValue()).to.be(minValue);
|
||||
expect(stackedVisData.getYMinValue()).to.be(stackedMinValue);
|
||||
});
|
||||
|
||||
it('should have a minimum date value that is greater than the max value within the date range', function () {
|
||||
expect(_.min(series.values, function (d) { return d.x; })).to.be.greaterThan(minValue);
|
||||
expect(_.min(stackedSeries.values, function (d) { return d.x; })).to.be.greaterThan(stackedMinValue);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -268,9 +316,9 @@ define(function (require) {
|
|||
dataSeries = require('vislib_fixtures/mock_data/date_histogram/_series');
|
||||
stackedDataSeries = require('vislib_fixtures/mock_data/stacked/_stacked');
|
||||
visData = new Data(dataSeries, {});
|
||||
stackedVisData = new Data(stackedDataSeries, {});
|
||||
series = visData.flatten();
|
||||
stackedSeries = stackedVisData.flatten();
|
||||
stackedVisData = new Data(stackedDataSeries, { type: 'histogram' });
|
||||
series = _.pluck(visData.chartData(), 'series');
|
||||
stackedSeries = _.pluck(stackedVisData.chartData(), 'series');
|
||||
maxValue = 41;
|
||||
stackedMaxValue = 115;
|
||||
});
|
||||
|
@ -279,13 +327,9 @@ define(function (require) {
|
|||
// The first value in the time series is less than the min date in the
|
||||
// date range. It also has the largest y value. This value should be excluded
|
||||
// when calculating the Y max value since it falls outside of the range.
|
||||
it('should return the Y domain max value', function () {
|
||||
series.forEach(function (data) {
|
||||
expect(visData._getYMax(data, visData._getY)).to.be(maxValue);
|
||||
});
|
||||
stackedSeries.forEach(function (data) {
|
||||
expect(stackedVisData._getYMax(data, visData._getYStack)).to.be(stackedMaxValue);
|
||||
});
|
||||
it('should return the Y domain min value', function () {
|
||||
expect(visData.getYMaxValue()).to.be(maxValue);
|
||||
expect(stackedVisData.getYMaxValue()).to.be(stackedMaxValue);
|
||||
});
|
||||
|
||||
it('should have a minimum date value that is greater than the max value within the date range', function () {
|
||||
|
|
|
@ -20,9 +20,7 @@ define(function (require) {
|
|||
vis = Private(require('vislib_fixtures/_vis_fixture'))();
|
||||
require('css!components/vislib/styles/main');
|
||||
|
||||
vis.on('brush', function (e) {
|
||||
console.log(e);
|
||||
});
|
||||
vis.on('brush', _.noop);
|
||||
|
||||
vis.render(data);
|
||||
});
|
||||
|
@ -36,12 +34,8 @@ define(function (require) {
|
|||
describe('addEvent method', function () {
|
||||
it('should return a function', function () {
|
||||
vis.handler.charts.forEach(function (chart) {
|
||||
var clickEvent = function (e) {
|
||||
console.log(e);
|
||||
};
|
||||
var addEvent = chart.events.addEvent;
|
||||
|
||||
expect(_.isFunction(addEvent('click', clickEvent))).to.be(true);
|
||||
expect(_.isFunction(addEvent('click', _.noop))).to.be(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,18 +1,42 @@
|
|||
define(function (require) {
|
||||
var angular = require('angular');
|
||||
var _ = require('lodash');
|
||||
var d3 = require('d3');
|
||||
var $ = require('jquery');
|
||||
|
||||
angular.module('YAxisFactory', ['kibana']);
|
||||
var YAxis;
|
||||
var Data;
|
||||
var el;
|
||||
var yAxis;
|
||||
var yAxisDiv;
|
||||
|
||||
describe('Vislib yAxis Class Test Suite', function () {
|
||||
var YAxis;
|
||||
var Data;
|
||||
var yAxis;
|
||||
var el;
|
||||
var yAxisDiv;
|
||||
var dataObj;
|
||||
var timeSeries = [
|
||||
1408734060000,
|
||||
1408734090000,
|
||||
1408734120000,
|
||||
1408734150000,
|
||||
1408734180000,
|
||||
1408734210000,
|
||||
1408734240000,
|
||||
1408734270000,
|
||||
1408734300000,
|
||||
1408734330000
|
||||
];
|
||||
|
||||
var defaultGraphData = [
|
||||
[ 8, 23, 30, 28, 36, 30, 26, 22, 29, 24 ],
|
||||
[ 2, 13, 20, 18, 26, 20, 16, 12, 19, 14 ]
|
||||
];
|
||||
|
||||
function makeSeriesData(data) {
|
||||
return timeSeries.map(function (timestamp, i) {
|
||||
return {
|
||||
x: timestamp,
|
||||
y: data[i] || 0
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function createData(seriesData) {
|
||||
var data = {
|
||||
hits: 621,
|
||||
label: 'test',
|
||||
|
@ -22,129 +46,51 @@ define(function (require) {
|
|||
max: 1408734982458,
|
||||
min: 1408734082458
|
||||
},
|
||||
series: [
|
||||
{
|
||||
values: [
|
||||
{
|
||||
x: 1408734060000,
|
||||
y: 8
|
||||
},
|
||||
{
|
||||
x: 1408734090000,
|
||||
y: 23
|
||||
},
|
||||
{
|
||||
x: 1408734120000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734150000,
|
||||
y: 28
|
||||
},
|
||||
{
|
||||
x: 1408734180000,
|
||||
y: 36
|
||||
},
|
||||
{
|
||||
x: 1408734210000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734240000,
|
||||
y: 26
|
||||
},
|
||||
{
|
||||
x: 1408734270000,
|
||||
y: 22
|
||||
},
|
||||
{
|
||||
x: 1408734300000,
|
||||
y: 29
|
||||
},
|
||||
{
|
||||
x: 1408734330000,
|
||||
y: 24
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
values: [
|
||||
{
|
||||
x: 1408734060000,
|
||||
y: 8
|
||||
},
|
||||
{
|
||||
x: 1408734090000,
|
||||
y: 23
|
||||
},
|
||||
{
|
||||
x: 1408734120000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734150000,
|
||||
y: 28
|
||||
},
|
||||
{
|
||||
x: 1408734180000,
|
||||
y: 36
|
||||
},
|
||||
{
|
||||
x: 1408734210000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734240000,
|
||||
y: 26
|
||||
},
|
||||
{
|
||||
x: 1408734270000,
|
||||
y: 22
|
||||
},
|
||||
{
|
||||
x: 1408734300000,
|
||||
y: 29
|
||||
},
|
||||
{
|
||||
x: 1408734330000,
|
||||
y: 24
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
series: seriesData.map(function (series) {
|
||||
return { values: makeSeriesData(series) };
|
||||
}),
|
||||
xAxisLabel: 'Date Histogram',
|
||||
yAxisLabel: 'Count'
|
||||
};
|
||||
|
||||
beforeEach(module('YAxisFactory'));
|
||||
beforeEach(inject(function (d3, Private) {
|
||||
var node = $('<div>').css({
|
||||
height: 40,
|
||||
width: 40
|
||||
})
|
||||
.appendTo('body')
|
||||
.addClass('y-axis-wrapper')
|
||||
.get(0);
|
||||
|
||||
el = d3.select(node).datum(data);
|
||||
|
||||
yAxisDiv = el.append('div')
|
||||
.attr('class', 'y-axis-div');
|
||||
|
||||
var dataObj = new Data(data, {
|
||||
defaultYMin: true
|
||||
});
|
||||
|
||||
yAxis = new YAxis({
|
||||
el: node,
|
||||
yMin: dataObj.getYMinValue(),
|
||||
yMax: dataObj.getYMaxValue(),
|
||||
_attr: {
|
||||
margin: { top: 0, right: 0, bottom: 0, left: 0 }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
describe('Vislib yAxis Class Test Suite', function () {
|
||||
var d3Provider;
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
|
||||
beforeEach(inject(function (Private, _d3_) {
|
||||
d3Provider = _d3_;
|
||||
Data = Private(require('components/vislib/lib/data'));
|
||||
YAxis = Private(require('components/vislib/lib/y_axis'));
|
||||
|
||||
expect($('.y-axis-wrapper')).to.have.length(0);
|
||||
|
||||
var $node = $('<div>').css({
|
||||
height: 40,
|
||||
width: 40
|
||||
})
|
||||
.appendTo('body')
|
||||
.addClass('y-axis-wrapper');
|
||||
|
||||
var node = $node.get(0);
|
||||
|
||||
el = d3.select(node).datum(data);
|
||||
|
||||
yAxisDiv = el.append('div')
|
||||
.attr('class', 'y-axis-div');
|
||||
|
||||
dataObj = new Data(data, {});
|
||||
yAxis = new YAxis({
|
||||
el: node,
|
||||
yMax: dataObj.getYMaxValue(),
|
||||
_attr: {
|
||||
margin: { top: 0, right: 0, bottom: 0, left: 0 }
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
afterEach(function () {
|
||||
|
@ -154,6 +100,7 @@ define(function (require) {
|
|||
|
||||
describe('render Method', function () {
|
||||
beforeEach(function () {
|
||||
createData(defaultGraphData);
|
||||
expect(d3.select(yAxis.el).selectAll('.y-axis-div')).to.have.length(1);
|
||||
yAxis.render();
|
||||
});
|
||||
|
@ -173,26 +120,89 @@ define(function (require) {
|
|||
|
||||
describe('getYScale Method', function () {
|
||||
var yScale;
|
||||
var graphData;
|
||||
var height = 50;
|
||||
|
||||
beforeEach(function () {
|
||||
yScale = yAxis.getYScale(height);
|
||||
});
|
||||
function checkDomain(min, max) {
|
||||
var domain = yScale.domain();
|
||||
expect(domain[0]).to.be.lessThan(min + 1);
|
||||
expect(domain[1]).to.be.greaterThan(max - 1);
|
||||
return domain;
|
||||
}
|
||||
|
||||
it('should return a function', function () {
|
||||
expect(_.isFunction(yScale)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return the correct domain', function () {
|
||||
expect(yScale.domain()[0]).to.be(0);
|
||||
// Should be greater than 36 since we are using .nice()
|
||||
expect(yScale.domain()[1]).to.be.greaterThan(36);
|
||||
});
|
||||
|
||||
it('should return the correct range', function () {
|
||||
function checkRange() {
|
||||
expect(yScale.range()[0]).to.be(height);
|
||||
// The yScale range should always start from 0
|
||||
expect(yScale.range()[1]).to.be(0);
|
||||
}
|
||||
|
||||
describe('API', function () {
|
||||
beforeEach(function () {
|
||||
createData(defaultGraphData);
|
||||
yScale = yAxis.getYScale(height);
|
||||
});
|
||||
|
||||
it('should return a function', function () {
|
||||
expect(_.isFunction(yScale)).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('positive values', function () {
|
||||
beforeEach(function () {
|
||||
graphData = defaultGraphData;
|
||||
createData(graphData);
|
||||
yScale = yAxis.getYScale(height);
|
||||
});
|
||||
|
||||
|
||||
it('should have domain between 0 and max value', function () {
|
||||
var min = 0;
|
||||
var max = _.max(_.flatten(graphData));
|
||||
var domain = checkDomain(min, max);
|
||||
expect(domain[1]).to.be.greaterThan(0);
|
||||
checkRange();
|
||||
});
|
||||
});
|
||||
|
||||
describe('negative values', function () {
|
||||
beforeEach(function () {
|
||||
graphData = [
|
||||
[ -8, -23, -30, -28, -36, -30, -26, -22, -29, -24 ],
|
||||
[ -22, -8, -30, -4, 0, 0, -3, -22, -14, -24 ]
|
||||
];
|
||||
createData(graphData);
|
||||
yScale = yAxis.getYScale(height);
|
||||
});
|
||||
|
||||
|
||||
it('should have domain between min value and 0', function () {
|
||||
var min = _.min(_.flatten(graphData));
|
||||
var max = 0;
|
||||
var domain = checkDomain(min, max);
|
||||
expect(domain[0]).to.be.lessThan(0);
|
||||
checkRange();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('positive and negative values', function () {
|
||||
beforeEach(function () {
|
||||
graphData = [
|
||||
[ 8, 23, 30, 28, 36, 30, 26, 22, 29, 24 ],
|
||||
[ 22, 8, -30, -4, 0, 0, 3, -22, 14, 24 ]
|
||||
];
|
||||
createData(graphData);
|
||||
yScale = yAxis.getYScale(height);
|
||||
});
|
||||
|
||||
|
||||
it('should have domain between min and max values', function () {
|
||||
var min = _.min(_.flatten(graphData));
|
||||
var max = _.max(_.flatten(graphData));
|
||||
var domain = checkDomain(min, max);
|
||||
expect(domain[0]).to.be.lessThan(0);
|
||||
expect(domain[1]).to.be.greaterThan(0);
|
||||
checkRange();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -201,6 +211,7 @@ define(function (require) {
|
|||
var val;
|
||||
|
||||
beforeEach(function () {
|
||||
createData(defaultGraphData);
|
||||
val = yAxis.formatAxisLabel(num);
|
||||
});
|
||||
|
||||
|
@ -212,6 +223,7 @@ define(function (require) {
|
|||
describe('getYAxis method', function () {
|
||||
var mode, yMax, yScale;
|
||||
beforeEach(function () {
|
||||
createData(defaultGraphData);
|
||||
mode = yAxis._attr.mode;
|
||||
yMax = yAxis.yMax;
|
||||
yScale = yAxis.getYScale;
|
||||
|
@ -244,12 +256,20 @@ define(function (require) {
|
|||
});
|
||||
|
||||
describe('draw Method', function () {
|
||||
beforeEach(function () {
|
||||
createData(defaultGraphData);
|
||||
});
|
||||
|
||||
it('should be a function', function () {
|
||||
expect(_.isFunction(yAxis.draw())).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('tickScale Method', function () {
|
||||
beforeEach(function () {
|
||||
createData(defaultGraphData);
|
||||
});
|
||||
|
||||
it('should return the correct number of ticks', function () {
|
||||
expect(yAxis.tickScale(1000)).to.be(11);
|
||||
expect(yAxis.tickScale(40)).to.be(3);
|
||||
|
@ -257,4 +277,4 @@ define(function (require) {
|
|||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -130,12 +130,8 @@ define(function (require) {
|
|||
|
||||
beforeEach(function () {
|
||||
listeners = [];
|
||||
listener1 = function (e) {
|
||||
console.log(e, 'listener1');
|
||||
};
|
||||
listener2 = function (e) {
|
||||
console.log(e, 'listener2');
|
||||
};
|
||||
listener1 = function () {};
|
||||
listener2 = function () {};
|
||||
listeners.push(listener1);
|
||||
listeners.push(listener2);
|
||||
|
||||
|
@ -198,12 +194,8 @@ define(function (require) {
|
|||
|
||||
beforeEach(function () {
|
||||
listeners = [];
|
||||
listener1 = function (e) {
|
||||
console.log(e, 'listener1');
|
||||
};
|
||||
listener2 = function (e) {
|
||||
console.log(e, 'listener2');
|
||||
};
|
||||
listener1 = function () {};
|
||||
listener2 = function () {};
|
||||
listeners.push(listener1);
|
||||
listeners.push(listener2);
|
||||
|
||||
|
|
|
@ -4,20 +4,26 @@ define(function (require) {
|
|||
var $ = require('jquery');
|
||||
|
||||
// Data
|
||||
var series = require('vislib_fixtures/mock_data/date_histogram/_series');
|
||||
var seriesPos = require('vislib_fixtures/mock_data/date_histogram/_series');
|
||||
var seriesPosNeg = require('vislib_fixtures/mock_data/date_histogram/_series_pos_neg');
|
||||
var seriesNeg = require('vislib_fixtures/mock_data/date_histogram/_series_neg');
|
||||
var termColumns = require('vislib_fixtures/mock_data/terms/_columns');
|
||||
var rangeRows = require('vislib_fixtures/mock_data/range/_rows');
|
||||
var stackedSeries = require('vislib_fixtures/mock_data/date_histogram/_stacked_series');
|
||||
|
||||
var dataArray = [
|
||||
series,
|
||||
seriesPos,
|
||||
seriesPosNeg,
|
||||
seriesNeg,
|
||||
termColumns,
|
||||
rangeRows,
|
||||
stackedSeries,
|
||||
];
|
||||
|
||||
var names = [
|
||||
'series',
|
||||
'series pos',
|
||||
'series pos neg',
|
||||
'series neg',
|
||||
'term columns',
|
||||
'range rows',
|
||||
'stackedSeries',
|
||||
|
@ -26,7 +32,8 @@ define(function (require) {
|
|||
var visLibParams = {
|
||||
type: 'area',
|
||||
addLegend: true,
|
||||
addTooltip: true
|
||||
addTooltip: true,
|
||||
defaultYExtents: false
|
||||
};
|
||||
|
||||
angular.module('AreaChartFactory', ['kibana']);
|
||||
|
@ -44,9 +51,7 @@ define(function (require) {
|
|||
vis = Private(require('vislib_fixtures/_vis_fixture'))(visLibParams);
|
||||
require('css!components/vislib/styles/main');
|
||||
|
||||
vis.on('brush', function (e) {
|
||||
console.log(e);
|
||||
});
|
||||
vis.on('brush', _.noop);
|
||||
|
||||
vis.render(data);
|
||||
});
|
||||
|
@ -218,6 +223,25 @@ define(function (require) {
|
|||
expect(_.isFunction(chart.draw())).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a yMin and yMax', function () {
|
||||
vis.handler.charts.forEach(function (chart) {
|
||||
var yAxis = chart.handler.yAxis;
|
||||
|
||||
expect(yAxis.yMin).to.not.be(undefined);
|
||||
expect(yAxis.yMax).to.not.be(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
it('should render a zero axis line', function () {
|
||||
vis.handler.charts.forEach(function (chart) {
|
||||
var yAxis = chart.handler.yAxis;
|
||||
|
||||
if (yAxis.yMin < 0 && yAxis.yMax > 0) {
|
||||
expect($(chart.chartEl).find('line.zero-line').length).to.be(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('containerTooSmall error', function () {
|
||||
|
@ -234,6 +258,23 @@ define(function (require) {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('defaultYExtents is true', function () {
|
||||
beforeEach(function () {
|
||||
vis._attr.defaultYExtents = true;
|
||||
vis.render(data);
|
||||
});
|
||||
|
||||
it('should return yAxis extents equal to data extents', function () {
|
||||
vis.handler.charts.forEach(function (chart) {
|
||||
var yAxis = chart.handler.yAxis;
|
||||
var yVals = [vis.handler.data.getYMinValue(), vis.handler.data.getYMaxValue()];
|
||||
|
||||
expect(yAxis.yMin).to.equal(yVals[0]);
|
||||
expect(yAxis.yMax).to.equal(yVals[1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,22 +6,30 @@ define(function (require) {
|
|||
|
||||
// Data
|
||||
var series = require('vislib_fixtures/mock_data/date_histogram/_series');
|
||||
var seriesPosNeg = require('vislib_fixtures/mock_data/date_histogram/_series_pos_neg');
|
||||
var seriesNeg = require('vislib_fixtures/mock_data/date_histogram/_series_neg');
|
||||
var termsColumns = require('vislib_fixtures/mock_data/terms/_columns');
|
||||
//var histogramRows = require('vislib_fixtures/mock_data/histogram/_rows');
|
||||
var stackedSeries = require('vislib_fixtures/mock_data/date_histogram/_stacked_series');
|
||||
var dataArray = [
|
||||
series,
|
||||
seriesPosNeg,
|
||||
seriesNeg,
|
||||
termsColumns,
|
||||
//histogramRows,
|
||||
stackedSeries
|
||||
];
|
||||
var names = [
|
||||
'series',
|
||||
'series with positive and negative values',
|
||||
'series with negative values',
|
||||
'terms columns',
|
||||
//'histogram rows',
|
||||
'stackedSeries'
|
||||
];
|
||||
var modes = [
|
||||
'stacked',
|
||||
'stacked',
|
||||
'stacked',
|
||||
'grouped',
|
||||
//'percentage',
|
||||
|
@ -153,6 +161,25 @@ define(function (require) {
|
|||
expect(_.isFunction(chart.draw())).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a yMin and yMax', function () {
|
||||
vis.handler.charts.forEach(function (chart) {
|
||||
var yAxis = chart.handler.yAxis;
|
||||
|
||||
expect(yAxis.yMin).to.not.be(undefined);
|
||||
expect(yAxis.yMax).to.not.be(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
it('should render a zero axis line', function () {
|
||||
vis.handler.charts.forEach(function (chart) {
|
||||
var yAxis = chart.handler.yAxis;
|
||||
|
||||
if (yAxis.yMin < 0 && yAxis.yMax > 0) {
|
||||
expect($(chart.chartEl).find('line.zero-line').length).to.be(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('containerTooSmall error', function () {
|
||||
|
@ -169,6 +196,23 @@ define(function (require) {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('defaultYExtents is true', function () {
|
||||
beforeEach(function () {
|
||||
vis._attr.defaultYExtents = true;
|
||||
vis.render(data);
|
||||
});
|
||||
|
||||
it('should return yAxis extents equal to data extents', function () {
|
||||
vis.handler.charts.forEach(function (chart) {
|
||||
var yAxis = chart.handler.yAxis;
|
||||
var yVals = [vis.handler.data.getYMinValue(), vis.handler.data.getYMaxValue()];
|
||||
|
||||
expect(yAxis.yMin).to.equal(yVals[0]);
|
||||
expect(yAxis.yMax).to.equal(yVals[1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,18 +4,24 @@ define(function (require) {
|
|||
var $ = require('jquery');
|
||||
|
||||
// Data
|
||||
var series = require('vislib_fixtures/mock_data/date_histogram/_series');
|
||||
var seriesPos = require('vislib_fixtures/mock_data/date_histogram/_series');
|
||||
var seriesPosNeg = require('vislib_fixtures/mock_data/date_histogram/_series_pos_neg');
|
||||
var seriesNeg = require('vislib_fixtures/mock_data/date_histogram/_series_neg');
|
||||
var histogramColumns = require('vislib_fixtures/mock_data/histogram/_columns');
|
||||
var rangeRows = require('vislib_fixtures/mock_data/range/_rows');
|
||||
var termSeries = require('vislib_fixtures/mock_data/terms/_series');
|
||||
var dateHistogramArray = [
|
||||
series,
|
||||
seriesPos,
|
||||
seriesPosNeg,
|
||||
seriesNeg,
|
||||
histogramColumns,
|
||||
rangeRows,
|
||||
termSeries,
|
||||
];
|
||||
var names = [
|
||||
'series',
|
||||
'series pos',
|
||||
'series pos neg',
|
||||
'series neg',
|
||||
'histogram columns',
|
||||
'range rows',
|
||||
'term series',
|
||||
|
@ -41,9 +47,7 @@ define(function (require) {
|
|||
vis = Private(require('vislib_fixtures/_vis_fixture'))(visLibParams);
|
||||
require('css!components/vislib/styles/main');
|
||||
|
||||
vis.on('brush', function (e) {
|
||||
console.log(e);
|
||||
});
|
||||
vis.on('brush', _.noop);
|
||||
|
||||
vis.render(data);
|
||||
});
|
||||
|
@ -133,6 +137,25 @@ define(function (require) {
|
|||
expect(chart.draw()).to.be.a(Function);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a yMin and yMax', function () {
|
||||
vis.handler.charts.forEach(function (chart) {
|
||||
var yAxis = chart.handler.yAxis;
|
||||
|
||||
expect(yAxis.yMin).to.not.be(undefined);
|
||||
expect(yAxis.yMax).to.not.be(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
it('should render a zero axis line', function () {
|
||||
vis.handler.charts.forEach(function (chart) {
|
||||
var yAxis = chart.handler.yAxis;
|
||||
|
||||
if (yAxis.yMin < 0 && yAxis.yMax > 0) {
|
||||
expect($(chart.chartEl).find('line.zero-line').length).to.be(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('containerTooSmall error', function () {
|
||||
|
@ -150,6 +173,22 @@ define(function (require) {
|
|||
});
|
||||
});
|
||||
|
||||
describe('defaultYExtents is true', function () {
|
||||
beforeEach(function () {
|
||||
vis._attr.defaultYExtents = true;
|
||||
vis.render(data);
|
||||
});
|
||||
|
||||
it('should return yAxis extents equal to data extents', function () {
|
||||
vis.handler.charts.forEach(function (chart) {
|
||||
var yAxis = chart.handler.yAxis;
|
||||
var yVals = [vis.handler.data.getYMinValue(), vis.handler.data.getYMaxValue()];
|
||||
|
||||
expect(yAxis.yMin).to.equal(yVals[0]);
|
||||
expect(yAxis.yMax).to.equal(yVals[1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue