Merge pull request #437 from spenceralger/better_tooltips

Better tooltips
This commit is contained in:
Spencer 2014-10-01 12:06:08 -07:00
commit 7dfb1cb746
19 changed files with 754 additions and 208 deletions

View file

@ -122,6 +122,11 @@ define(function (require) {
);
};
AggConfig.prototype.makeLabel = function () {
if (!this.type) return '';
return this.type.makeLabel(this);
};
return AggConfig;
};
});

View file

@ -1,9 +1,14 @@
define(function (require) {
return function HistogramConverterFn(Private, timefilter) {
return function HistogramConverterFn(Private, timefilter, $compile, $rootScope) {
var _ = require('lodash');
var $ = require('jquery');
var moment = require('moment');
var interval = require('utils/interval');
var $tooltipScope = $rootScope.$new();
var $tooltip = $(require('text!components/vis_types/tooltips/histogram.html'));
$compile($tooltip)($tooltipScope);
return function (chart, columns, rows) {
// index of color
var iColor = _.findIndex(columns, { categoryName: 'group' });
@ -79,24 +84,41 @@ define(function (require) {
// setup the formatter for the label
chart.tooltipFormatter = function (datum) {
var vals = [['x', colX], ['y', colY]]
.map(function (set) {
var axis = set[0];
var col = set[1];
var val = datum[axis];
chart.tooltipFormatter = function (event) {
$tooltipScope.details = columns.map(function (col) {
var datum = event.point;
var aggConfig = col.aggConfig;
var name = (col.field && col.field.name) || col.label || axis;
var label;
var val;
switch (col) {
case colX:
label = 'x';
val = datum.x;
break;
case colY:
label = 'y';
val = datum.y;
break;
case colColor:
label = 'color';
val = datum.label;
break;
}
label = aggConfig.makeLabel() || (col.field && col.field.name) || label;
if (col.field) val = col.field.format.convert(val);
return name + ': ' + val;
}).join('<br>');
return {
label: label,
value: val
};
var out = '';
if (datum.label) out += colColor.field.name + ': ' + datum.label + '<br>';
out += vals;
});
return out;
$tooltipScope.$apply();
return $tooltip[0].outerHTML;
};
var series = chart.series = [];

View file

@ -1,9 +1,14 @@
define(function (require) {
return function HistogramConverterFn(Private, timefilter) {
return function HistogramConverterFn(Private, timefilter, $compile, $rootScope, $sce) {
var _ = require('lodash');
var $ = require('jquery');
var moment = require('moment');
var interval = require('utils/interval');
var $tooltip = $(require('text!components/vis_types/tooltips/pie.html'));
var $tooltipScope = $rootScope.$new();
$compile($tooltip)($tooltipScope);
return function (chart, columns, rows) {
// Checks for obj.parent.name and
@ -24,31 +29,44 @@ define(function (require) {
}
// tooltip formatter for pie charts
chart.tooltipFormatter = function (datum) {
function sumValue(sum, cur) {
return sum + cur.value;
chart.tooltipFormatter = function (event) {
var datum = event.point;
var parent;
var sum;
// the sum of values at all levels/generations is the same, but levels
// are seperated by their parents so the root is the simplest to find
for (parent = datum; parent; parent = parent.parent) {
sum = parent.value;
}
// find the root datum
var root = datum;
while (root.parent) root = root.parent;
var rows = $tooltipScope.rows = [];
for (parent = datum; parent.parent; parent = parent.parent) {
var i = parent.depth - 1;
var col = columns[i];
// the value of the root datum is the sum of every row. coincidental? not certain
var sum = root.value;
// field/agg details
var group = (col.field && col.field.name) || col.label || ('level ' + datum.depth);
var labels = [];
for (var cur = datum; cur.parent; cur = cur.parent) {
var label = cur.name + ': ' + cur.value;
label += ' (' + Math.round((cur.value / sum) * 100) + '%)';
// field value that defines the bucket
var bucket = parent.name;
if (col.field) bucket = col.field.format.convert(bucket);
if (cur === datum) {
label = '<b>' + label + '</b>';
}
// metric for the bucket
var val = parent.value;
labels.unshift(label);
rows.unshift({
spacer: $sce.trustAsHtml(_.repeat('&nbsp;', i)),
field: group,
bucket: bucket,
metric: val + ' (' + Math.round((parent.value / sum) * 100) + '%)'
});
}
return labels.join('<br>');
$tooltipScope.metricCol = _.find(columns, { categoryName: 'metric' });
$tooltipScope.$apply();
return $tooltip[0].outerHTML;
};

View file

@ -0,0 +1,14 @@
<table>
<thead>
<tr>
<th></th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="detail in details">
<td>{{detail.label}}</td>
<td>{{detail.value}}</td>
</tr>
</tbody>
</table>

View file

@ -0,0 +1,16 @@
<table>
<thead>
<tr>
<th>field</th>
<th>value</th>
<th>{{metricCol.label}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows track by $index">
<td><span ng-bind-html="row.spacer"></span>{{row.field}}</td>
<td>{{row.bucket}}</td>
<td>{{row.metric}}</td>
</tr>
</tbody>
</table>

View file

@ -5,16 +5,16 @@ define(function (require) {
/**
* Events Class
*/
function Dispatch(vis, chartData) {
function Dispatch(handler, chartData) {
if (!(this instanceof Dispatch)) {
return new Dispatch(vis, chartData);
return new Dispatch(handler, chartData);
}
var type = vis._attr.type;
var type = handler._attr.type;
this.vis = vis;
this.handler = handler;
this.chartData = chartData;
this.color = type === 'pie' ? vis.data.getPieColorFunc() : vis.data.getColorFunc();
this._attr = _.defaults(vis._attr || {}, {
this.color = type === 'pie' ? handler.data.getPieColorFunc() : handler.data.getColorFunc();
this._attr = _.defaults(handler._attr || {}, {
yValue: function (d) {
return d.y;
},
@ -24,24 +24,48 @@ define(function (require) {
// Response to `click` and `hover` events
Dispatch.prototype.eventResponse = function (d, i) {
var label = d.label ? d.label : d.name;
var label = d.label;
var getYValue = this._attr.yValue;
var color = this.color;
var chartData = this.chartData;
var attr = this._attr;
var vis = this.vis;
var handler = this.handler;
return {
value : getYValue(d, i),
point : d,
label : label,
color : color(label),
value: getYValue(d, i),
point: d,
label: label,
color: color(label),
pointIndex: i,
series : chartData.series,
config : attr,
data : chartData,
e : d3.event,
vis : vis
series: chartData.series,
config: attr,
data: chartData,
e: d3.event,
handler: handler
};
};
// Pie response to `click` and `hover` events
Dispatch.prototype.pieResponse = function (d, i) {
var label = d.key;
var color = this.color;
var chartData = this.chartData;
var attr = this._attr;
var handler = this.handler;
return {
value: d.value,
point: d,
label: label,
color: color(label),
pointIndex: i,
children: d.children ? d.children : undefined,
parent: d.parent ? d.parent : undefined,
appConfig: d.appConfig,
config: attr,
data: chartData,
e: d3.event,
handler: handler
};
};

View file

@ -4,7 +4,6 @@ define(function (require) {
var Data = Private(require('components/vislib/lib/data'));
var Layout = Private(require('components/vislib/lib/layout/layout'));
var Tooltip = Private(require('components/vislib/lib/tooltip'));
/*
* Handles building all the components of the visualization
@ -34,10 +33,6 @@ define(function (require) {
this.chartTitle = opts.chartTitle;
this.axisTitle = opts.axisTitle;
if (this._attr.addTooltip) {
this.tooltip = new Tooltip(this.el, this.data.get('tooltipFormatter'));
}
if (this._attr.addLegend && this.data.isLegendShown()) {
this.legend = opts.legend;
}

View file

@ -25,7 +25,6 @@ define(function (require) {
this.el = el;
this.labels = labels;
this.color = color;
this.tooltip = new Tooltip(this.el, function (d) { return d; });
this._attr = _.defaults(_attr || {}, {
// Legend specific attributes
'legendClass' : 'legend-col-wrapper',
@ -109,10 +108,6 @@ define(function (require) {
}
});
headerIcon
.datum(['Legend'])
.call(self.tooltip.render());
visEl.selectAll('.color')
.on('mouseover', function (d) {
var liClass = '.' + self.colorToClass(self.color(d));

View file

@ -12,18 +12,20 @@ define(function (require) {
* el => reference to DOM element
* formatter => tooltip formatter
*/
function Tooltip(el, formatter) {
function Tooltip(el, formatter, events) {
if (!(this instanceof Tooltip)) {
return new Tooltip(el, formatter);
return new Tooltip(el, formatter, events);
}
this.el = el;
this.tooltipFormatter = formatter;
this.formatter = formatter;
this.events = events;
this.tooltipClass = 'vis-tooltip';
this.containerClass = 'vis-wrapper';
}
Tooltip.prototype.render = function () {
var self = this;
var tooltipFormatter = this.formatter;
return function (selection) {
@ -36,62 +38,94 @@ define(function (require) {
var tooltipDiv = d3.select('.' + self.tooltipClass);
selection.each(function () {
selection.each(function (data, i) {
var element = d3.select(this);
var offset;
element
.on('mousemove.tip', function (d) {
var mouseMove = {
left: d3.event.clientX,
top: d3.event.clientY
};
offset = self.getOffsets(mouseMove);
return tooltipDiv.datum(d)
.html(self.tooltipFormatter)
var placement = self.getTooltipPlacement(d3.event);
// return text and position for tooltip
return tooltipDiv.datum(self.events.eventResponse(d, i))
.html(tooltipFormatter)
.style('visibility', 'visible')
.style('left', mouseMove.left + offset.left + 'px')
.style('top', mouseMove.top + offset.top + 'px');
.style('left', placement.left + 'px')
.style('top', placement.top + 'px');
})
.on('mouseout.tip', function () {
return tooltipDiv.style('visibility', 'hidden')
.style('left', '-500px')
.style('top', '-500px');
});
});
};
};
Tooltip.prototype.getOffsets = function (mouseMove) {
Tooltip.prototype.getTooltipPlacement = function (event) {
var self = this;
var offset = {top: -10, left: 10};
if ($(self.el).find('.' + self.containerClass)) {
var container = $(self.el).find('.' + self.containerClass);
var chartXOffset = container.offset().left;
var chartYOffset = container.offset().top;
var chartWidth = container.width();
var chartHeight = container.height();
var tip = $('.' + self.tooltipClass)[0];
var tipWidth = tip.clientWidth;
var tipHeight = tip.clientHeight;
// change xOffset to keep tooltip within container
// if tip width + xOffset puts it over right edge of container, flip left
// unless flip left puts it over left edge of container
// change yOffset to keep tooltip within container
if (mouseMove.left + offset.left + tipWidth > chartXOffset + chartWidth &&
mouseMove.left - tipWidth - 10 > chartXOffset) {
offset.left = -10 - tipWidth;
var OFFSET = 10;
var chart = $(self.el).find('.' + self.containerClass);
if (!chart.size()) return;
var chartPos = chart.offset();
chartPos.right = chartPos.left + chart.outerWidth();
chartPos.bottom = chartPos.top + chart.outerHeight();
var tip = $('.' + self.tooltipClass);
var tipWidth = tip.outerWidth();
var tipHeight = tip.outerHeight();
return _.mapValues({
left: {
default: event.clientX + OFFSET
}
if (mouseMove.top + tipHeight - 10 > chartYOffset + chartHeight) {
offset.top = chartYOffset + chartHeight;
});
// the placements if we were to place the tip east or west
var left = {
east: ,
west: event.clientX - tipWidth - OFFSET
};
// the placements if we were to place the tip north or south
var top = {
south: event.clientY + OFFSET,
north: event.clientY - tipHeight - OFFSET
};
// number of pixels that the toolip would overflow it's far
// side, if we placed it that way. (negative === no overflow)
var overflow = {
north: chartPos.top - top.north,
east: (left.east + tipWidth) - chartPos.right,
south: (top.south + tipHeight) - chartPos.bottom,
west: chartPos.left - left.west
};
var placement = {};
if (overflow.south > 0) {
if (overflow.north < 0) {
placement.top = top.north;
} else {
placement.top = top.south - overflow.south;
}
} else {
placement.top = top.south;
}
return offset;
if (overflow.east > 0) {
if (overflow.west < 0) {
placement.left = left.west;
} else {
placement.left = left.east - overflow.east;
}
} else {
placement.left = left.east;
}
return placement;
};
return Tooltip;

View file

@ -1,3 +1,4 @@
@import (reference) "../../../styles/main.less";
@import (reference) "lesshat.less";
/* vislib styles */
@ -218,12 +219,27 @@ p.rows-label, p.columns-label {
font-weight: normal;
padding: 8px;
background: rgba(70, 82, 93, 0.95);
color: #fff;
color: @gray-lighter;
border-radius: 4px;
position: fixed;
z-index: 120;
word-wrap: break-word;
max-width: 40%;
table {
td,th {
padding: 2px 4px;
}
tr {
border-bottom: 1px solid @gray;
}
// only apply to tr in the body, not the header
tbody tr:last-child {
border-bottom: none;
}
}
}
.rect {

View file

@ -4,21 +4,30 @@ define(function (require) {
var Legend = Private(require('components/vislib/lib/legend'));
var Dispatch = Private(require('components/vislib/lib/dispatch'));
var Tooltip = Private(require('components/vislib/lib/tooltip'));
/*
* Base Class for all visualizations.
* Exposes a render method.
*/
function Chart(vis, el, chartData) {
function Chart(handler, el, chartData) {
if (!(this instanceof Chart)) {
return new Chart(vis, el, chartData);
return new Chart(handler, el, chartData);
}
this.vis = vis;
this.handler = handler;
this.chartEl = el;
this.chartData = chartData;
this.events = new Dispatch(vis, chartData);
this._attr = _.defaults(vis._attr || {}, {});
var events = this.events = new Dispatch(handler, chartData);
if (handler._attr.addTooltip) {
var $el = this.handler.el;
var formatter = this.handler.data.get('tooltipFormatter');
// Add tooltip
this.tooltip = new Tooltip($el, formatter, events);
}
this._attr = _.defaults(handler._attr || {}, {});
}
// Render the visualization.

View file

@ -13,14 +13,14 @@ define(function (require) {
* Column chart visualization => vertical bars, stacked bars
*/
_(ColumnChart).inherits(Chart);
function ColumnChart(vis, chartEl, chartData) {
function ColumnChart(handler, chartEl, chartData) {
if (!(this instanceof ColumnChart)) {
return new ColumnChart(vis, chartEl, chartData);
return new ColumnChart(handler, chartEl, chartData);
}
ColumnChart.Super.apply(this, arguments);
// Column chart specific attributes
this._attr = _.defaults(vis._attr || {}, {
this._attr = _.defaults(handler._attr || {}, {
xValue: function (d, i) { return d.x; },
yValue: function (d, i) { return d.y; }
});
@ -46,10 +46,10 @@ define(function (require) {
ColumnChart.prototype.addBars = function (svg, layers) {
var self = this;
var data = this.chartData;
var color = this.vis.data.getColorFunc();
var xScale = this.vis.xAxis.xScale;
var yScale = this.vis.yAxis.yScale;
var tooltip = this.vis.tooltip;
var color = this.handler.data.getColorFunc();
var xScale = this.handler.xAxis.xScale;
var yScale = this.handler.yAxis.yScale;
var tooltip = this.tooltip;
var isTooltip = this._attr.addTooltip;
var layer;
var bars;
@ -117,7 +117,7 @@ define(function (require) {
ColumnChart.prototype.addBarEvents = function (svg, bars, brush) {
var events = this.events;
var dispatch = this.events._attr.dispatch;
var xScale = this.vis.xAxis.xScale;
var xScale = this.handler.xAxis.xScale;
var startXInv;
bars
@ -158,7 +158,7 @@ define(function (require) {
ColumnChart.prototype.draw = function () {
// Attributes
var self = this;
var xScale = this.vis.xAxis.xScale;
var xScale = this.handler.xAxis.xScale;
var $elem = $(this.chartEl);
var margin = this._attr.margin;
var elWidth = this._attr.width = $elem.width();

View file

@ -9,14 +9,14 @@ define(function (require) {
require('css!components/vislib/styles/main');
_(LineChart).inherits(Chart);
function LineChart(vis, chartEl, chartData) {
function LineChart(handler, chartEl, chartData) {
if (!(this instanceof LineChart)) {
return new LineChart(vis, chartEl, chartData);
return new LineChart(handler, chartEl, chartData);
}
LineChart.Super.apply(this, arguments);
// Line chart specific attributes
this._attr = _.defaults(vis._attr || {}, {
this._attr = _.defaults(handler._attr || {}, {
interpolate: 'linear',
xValue: function (d) { return d.x; },
yValue: function (d) { return d.y; }
@ -54,13 +54,13 @@ define(function (require) {
LineChart.prototype.addCircles = function (svg, data) {
var self = this;
var color = this.vis.data.getColorFunc();
var xScale = this.vis.xAxis.xScale;
var yScale = this.vis.yAxis.yScale;
var ordered = this.vis.data.get('ordered');
var color = this.handler.data.getColorFunc();
var xScale = this.handler.xAxis.xScale;
var yScale = this.handler.yAxis.yScale;
var ordered = this.handler.data.get('ordered');
var circleRadius = 4;
var circleStrokeWidth = 1;
var tooltip = this.vis.tooltip;
var tooltip = this.tooltip;
var isTooltip = this._attr.addTooltip;
var layer;
var circles;
@ -121,11 +121,11 @@ define(function (require) {
LineChart.prototype.addLines = function (svg, data) {
var self = this;
var xScale = this.vis.xAxis.xScale;
var yScale = this.vis.yAxis.yScale;
var xAxisFormatter = this.vis.data.get('xAxisFormatter');
var color = this.vis.data.getColorFunc();
var ordered = this.vis.data.get('ordered');
var xScale = this.handler.xAxis.xScale;
var yScale = this.handler.yAxis.yScale;
var xAxisFormatter = this.handler.data.get('xAxisFormatter');
var color = this.handler.data.getColorFunc();
var ordered = this.handler.data.get('ordered');
var interpolate = this._attr.interpolate;
var line = d3.svg.line()
.interpolate(interpolate)
@ -190,7 +190,7 @@ 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.vis.xAxis.xScale;
var xScale = this.handler.xAxis.xScale;
var chartToSmallError = 'The height and/or width of this container is too small for this chart.';
var minWidth = 20;
var minHeight = 20;

View file

@ -13,13 +13,13 @@ define(function (require) {
* Column chart visualization => vertical bars, stacked bars
*/
_(PieChart).inherits(Chart);
function PieChart(vis, chartEl, chartData) {
function PieChart(handler, chartEl, chartData) {
if (!(this instanceof PieChart)) {
return new PieChart(vis, chartEl, chartData);
return new PieChart(handler, chartEl, chartData);
}
PieChart.Super.apply(this, arguments);
this._attr = _.defaults(vis._attr || {}, {
this._attr = _.defaults(handler._attr || {}, {
getSize: function (d) { return d.size; },
dispatch: d3.dispatch('brush', 'click', 'hover', 'mouseenter', 'mouseleave', 'mouseover', 'mouseout')
});
@ -35,11 +35,11 @@ define(function (require) {
.classed('hover', true)
.style('cursor', 'pointer');
dispatch.hover(events.eventResponse(d, i));
dispatch.hover(events.pieResponse(d, i));
d3.event.stopPropagation();
})
.on('click.pie', function clickPie(d, i) {
dispatch.click(events.eventResponse(d, i));
dispatch.click(events.pieResponse(d, i));
d3.event.stopPropagation();
})
.on('mouseout.pie', function mouseOutPie() {
@ -51,7 +51,7 @@ define(function (require) {
PieChart.prototype.addPath = function (width, height, svg, slices) {
var isDonut = this._attr.isDonut;
var radius = Math.min(width, height) / 2;
var color = this.vis.data.getPieColorFunc();
var color = this.handler.data.getPieColorFunc();
var partition = d3.layout.partition()
.sort(null)
.value(function (d) {
@ -80,7 +80,7 @@ define(function (require) {
.outerRadius(function (d) {
return Math.max(0, y(d.y + d.dy));
});
var tooltip = this.vis.tooltip;
var tooltip = this.tooltip;
var isTooltip = this._attr.addTooltip;
var self = this;
var path;
@ -100,8 +100,7 @@ define(function (require) {
.style('fill', function (d) {
if (d.depth === 0) { return 'none'; }
return color(d.name);
})
.attr('fill-rule', 'evenodd');
});
// Add tooltip
if (isTooltip) {

View file

@ -159,6 +159,11 @@ define(function (require) {
chunks[i] = arr.slice(start, start + size);
}
return chunks;
},
repeat: function (string, times) {
var out = '';
for (var i = 0; i < times; i++) out += string;
return out;
}
});

View file

@ -24,6 +24,7 @@
config: '/config',
test_utils: '../../test/utils',
fixtures: '../../test/unit/fixtures',
vislib_fixtures: '../../test/unit/specs/vislib/fixture',
specs: '../../test/unit/specs',
sinon: '../../test/utils/sinon',
bluebird: 'bower_components/bluebird/js/browser/bluebird',

View file

@ -0,0 +1,412 @@
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': 21
},
{
'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
}
]
}
],
'raw': {
'splitColumns': [],
'splitValStack': [],
'columns': [
{
'categoryName': 'segment',
'id': 'agg_12',
'aggConfig': {
'type': 'date_histogram',
'schema': 'segment',
'params': {
'field': '@timestamp',
'interval': 'auto',
'min_doc_count': 1,
'extended_bounds': {}
}
},
'aggType': {
'name': 'date_histogram',
'title': 'Date Histogram',
'ordered': {
'date': true
},
'hasNoDsl': false,
'params': [
{
'name': 'field',
'filterFieldTypes': 'date'
},
{
'name': 'interval',
'default': 'auto',
'options': [
{
'display': 'Auto',
'val': 'auto'
},
{
'display': 'Second',
'val': 'second',
'ms': 1000
},
{
'display': 'Minute',
'val': 'minute',
'ms': 60000
},
{
'display': 'Hourly',
'val': 'hour',
'ms': 3600000
},
{
'display': 'Daily',
'val': 'day',
'ms': 86400000
},
{
'display': 'Weekly',
'val': 'week',
'ms': 604800000
},
{
'display': 'Monthly',
'val': 'month',
'ms': 2592000000
},
{
'display': 'Yearly',
'val': 'year',
'ms': 31536000000
}
],
'editor': '<div class=\'form-group\'>\n <label>Interval</label>\n <select\n ' +
'ng-if=\'aggParam.options\'\n ng-model=\'params.interval\'\n required\n ' +
'ng-options=\'opt as opt.display for opt in aggParam.options\'\n class=\'form-control\'\n ' +
'name=\'interval\'>\n </select>\n <input\n ng-if=\'!aggParam.options\'\n ' +
'ng-model=\'params.interval\'\n required\n type=\'number\'\n class=\'form-control\'\n ' +
'name=\'interval\' />\n</div>\n'
},
{
'name': 'format'
},
{
'name': 'min_doc_count',
'default': 1
},
{
'name': 'extended_bounds',
'default': {}
}
],
'type': 'buckets'
},
'field': {
'type': 'date',
'indexed': true,
'analyzed': false,
'doc_values': false,
'name': '@timestamp',
'count': 0
},
'label': '@timestamp per 30 sec',
'params': {
'field': '@timestamp',
'interval': '30000ms',
'min_doc_count': 1,
'extended_bounds': {
'min': '2014-09-26T19:57:37.633Z',
'max': '2014-09-26T20:12:37.633Z'
}
},
'metricScaleText': '30 sec'
},
{
'categoryName': 'metric',
'id': 'agg_11',
'aggConfig': {
'type': 'count',
'schema': 'metric',
'params': {}
},
'aggType': {
'name': 'count',
'title': 'Count',
'hasNoDsl': true,
'params': [],
'type': 'metrics'
},
'label': 'Count of documents',
'params': {}
}
],
'rows': [
[
1411761450000,
21
],
[
1411761480000,
18
],
[
1411761510000,
22
],
[
1411761540000,
17
],
[
1411761570000,
17
],
[
1411761600000,
21
],
[
1411761630000,
16
],
[
1411761660000,
17
],
[
1411761690000,
15
],
[
1411761720000,
19
],
[
1411761750000,
11
],
[
1411761780000,
13
],
[
1411761810000,
24
],
[
1411761840000,
20
],
[
1411761870000,
20
],
[
1411761900000,
21
],
[
1411761930000,
17
],
[
1411761960000,
20
],
[
1411761990000,
13
],
[
1411762020000,
14
],
[
1411762050000,
25
],
[
1411762080000,
17
],
[
1411762110000,
14
],
[
1411762140000,
22
],
[
1411762170000,
14
],
[
1411762200000,
19
],
[
1411762230000,
22
],
[
1411762260000,
17
],
[
1411762290000,
8
],
[
1411762320000,
15
],
[
1411762350000,
4
]
]
},
'hits': 533,
'xAxisFormatter': function (thing) {
return moment(thing);
},
'tooltipFormatter': function (d) {
return d;
}
};
});

View file

@ -0,0 +1,21 @@
define(function (require) {
return function VisLibFixtures(Private) {
var $ = require('jquery');
return function (type) {
var Vis = Private(require('components/vislib/vis'));
var visChart = $('body').append('<div class=visualize-chart></div>');
var $el = $('.visualize-chart');
var config = {
shareYAxis: true,
addTooltip: true,
addLegend: true,
addEvents: true,
addBrushing: true,
type: type
};
return new Vis($el, config);
};
};
});

View file

@ -6,38 +6,9 @@ define(function (require) {
angular.module('TooltipFactory', ['kibana']);
describe('Vislib Tooltip', function () {
var Tooltip;
var bars;
var tip;
var el;
var chart;
var data = [
{
x: 10,
y: 8
},
{
x: 11,
y: 23
},
{
x: 12,
y: 30
},
{
x: 13,
y: 28
},
{
x: 14,
y: 36
},
{
x: 15,
y: 30
}
];
var vis;
var data;
var tooltips = [];
beforeEach(function () {
module('TooltipFactory');
@ -45,55 +16,44 @@ define(function (require) {
beforeEach(function () {
inject(function (d3, Private) {
Tooltip = Private(require('components/vislib/lib/tooltip'));
vis = Private(require('vislib_fixtures/vis_fixture'))('histogram');
data = require('vislib_fixtures/mock_data/series/data0');
require('css!components/vislib/styles/main');
el = d3.select('body')
.append('div')
.attr('class', 'vis-col-wrapper')
.style('width', '40px')
.style('height', '40px');
tip = new Tooltip(el[0][0], function (d) {
return 'd.x: ' + d.x + ', d.y: ' + d.y;
vis.render(data);
vis.handler.charts.forEach(function (chart) {
tooltips.push(chart.tooltip);
});
});
});
chart = el.append('div').attr('class', 'chart');
el.append('div').attr('class', 'y-axis-col-wrapper');
el.append('div').attr('class', 'k4tip');
// Placeholder until we know better regarding garbage collection
afterEach(function () {
$(vis.el).remove();
vis = null;
});
bars = chart.selectAll('div')
.data(data)
.enter()
.append('div')
.attr('class', 'bars')
.style('width', function (d) {
return d.y;
})
.style('height', '10px')
.text(function (d) {
return d.y;
});
describe('render Method', function () {
var isObject;
var isFunction;
bars.call(tip.render());
// d3.select('.bars').on('mousemove.tip')(d3.select('.bars').node().__data__, 0);
beforeEach(function () {
_.forEach(tooltips, function (tooltip) {
isObject = _.isObject(tooltip);
isFunction = _.isFunction(tooltip.render());
});
});
it('should be an object', function () {
expect(isObject).to.be(true);
});
it('should return a function', function () {
expect(isFunction).to.be(true);
});
});
afterEach(function () {
el.remove();
});
it('should be an object', function () {
expect(_.isObject(Tooltip)).to.be(true);
});
it('should return a function', function () {
expect(typeof Tooltip).to.be('function');
});
describe('getOffsets Method', function () {});
});
});