mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
Merge pull request #437 from spenceralger/better_tooltips
Better tooltips
This commit is contained in:
commit
7dfb1cb746
19 changed files with 754 additions and 208 deletions
|
@ -122,6 +122,11 @@ define(function (require) {
|
|||
);
|
||||
};
|
||||
|
||||
AggConfig.prototype.makeLabel = function () {
|
||||
if (!this.type) return '';
|
||||
return this.type.makeLabel(this);
|
||||
};
|
||||
|
||||
return AggConfig;
|
||||
};
|
||||
});
|
|
@ -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 = [];
|
||||
|
|
|
@ -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(' ', 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;
|
||||
};
|
||||
|
||||
|
||||
|
|
14
src/kibana/components/vis_types/tooltips/histogram.html
Normal file
14
src/kibana/components/vis_types/tooltips/histogram.html
Normal 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>
|
16
src/kibana/components/vis_types/tooltips/pie.html
Normal file
16
src/kibana/components/vis_types/tooltips/pie.html
Normal 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>
|
|
@ -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
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
412
test/unit/specs/vislib/fixture/mock_data/series/data0.js
Normal file
412
test/unit/specs/vislib/fixture/mock_data/series/data0.js
Normal 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;
|
||||
}
|
||||
};
|
||||
});
|
21
test/unit/specs/vislib/fixture/vis_fixture.js
Normal file
21
test/unit/specs/vislib/fixture/vis_fixture.js
Normal 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);
|
||||
};
|
||||
};
|
||||
});
|
|
@ -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 () {});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue