Skip to content

Commit

Permalink
updated tootlip to append to body and to display within a container, c…
Browse files Browse the repository at this point in the history
…loses #299, removed k4tip from column chart layout, added basic tests for tooltip
  • Loading branch information
Juan Thomassie committed Sep 16, 2014
1 parent af355dd commit cee548a
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,6 @@ define(function (require) {
{
type: 'div',
class: 'legend-col-wrapper'
},
{
type: 'div',
class: 'k4tip'
}
]
}
Expand Down
2 changes: 1 addition & 1 deletion src/kibana/components/vislib/components/styles/main.less
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ path, line, .axis line, .axis path {
stroke: 3px;
}

.k4tip {
.k4tip, .vis-tooltip {
line-height: 1.1;
font-size: 12px;
font-weight: normal;
Expand Down
111 changes: 69 additions & 42 deletions src/kibana/components/vislib/lib/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,80 +16,107 @@ define(function (require) {
if (!(this instanceof Tooltip)) {
return new Tooltip(el, formatter);
}

this.el = el;
this.tooltipFormatter = formatter;
// hard coded class name for the tooltip `div`
this.tooltipClass = 'k4tip';
// reference to the width and height of the chart DOM elements
// establishes the bounds for the tooltip per chart
this.chartWidth = $('.chart').width();
this.chartHeight = $('.chart').height();
this.tooltipClass = 'vis-tooltip';
this.containerClass = 'vis-wrapper';
}

Tooltip.prototype.render = function () {
var self = this;

return function (selection) {

// if tooltip not appended to body, append one
if (d3.select('body').select('.' + self.tooltipClass)[0][0] === null) {
d3.select('body').append('div').attr('class', self.tooltipClass);
}

// if container not saved on Tooltip, save it
if (self.container === undefined || self.container !== d3.select(self.el).select('.' + self.containerClass)) {
self.container = d3.select(self.el).select('.' + self.containerClass);
}

var tooltipDiv = d3.select('.' + self.tooltipClass);

selection.each(function () {
var tooltipDiv = d3.select(self.el).select('.' + self.tooltipClass);

// DOM element on which the tooltip is called
var element = d3.select(this);

// define selections relative to el of tooltip
var chartXoffset;
var chartWidth;
var chartHeight;
var yaxisWidth;
var offset;
var tipWidth;
var tipHeight;

element
.on('mousemove.tip', function (d) {
// Calculate the x and y coordinates of the mouse on the page
// get x and y coordinates of the mouse event
var mouseMove = {
left: d3.event.clientX,
top: d3.event.clientY
};

// hack to keep active tooltip in front of gridster/dashboard list
if ($('.gridster').length) {
var gridsterUl = $('.gridster');
var gridsterLis = $('.gridster').find('li').removeClass('player-revert');
var tipLi = $(tooltipDiv.node()).closest('li').addClass('player-revert');
}

var chartWidth = $(tooltipDiv.node()).closest('.vis-wrapper').width();
var yaxisWidth = $('.y-axis-col-wrapper').width();
var offsetX = d3.event.offsetX === undefined ? d3.event.layerX : d3.event.offsetX;
var tipWidth = tooltipDiv[0][0].clientWidth;
var xOffset = 10;

// check position of tooltip relative to chart width
// to apply offset if tooltip should flip 'west'
// if tip width + offset puts it off chart, flip direction
// unless flip puts it off the left edge of vis wrapper
if ((chartWidth - offsetX) < (tipWidth + yaxisWidth + 10) && (offsetX + yaxisWidth + 10) > (tipWidth + 10)) {
xOffset = -10 - tipWidth;
}

var chartHeight = self.chartHeight;
var offsetY = d3.event.offsetY === undefined ? d3.event.layerY : d3.event.offsetY;
var tipHeight = tooltipDiv[0][0].clientHeight;
var yOffset = 5;
// apply y offset to keep tooltip within bottom of chart
if ((chartHeight - offsetY + 5) < (tipHeight)) {
yOffset = tipHeight - (chartHeight - offsetY + 0);
}

offset = self.getOffsets(tooltipDiv, mouseMove);

// return text and position for tooltip
return tooltipDiv.datum(d)
.text(self.tooltipFormatter)
.style('visibility', 'visible')
.style('left', mouseMove.left + xOffset + 'px')
.style('top', mouseMove.top - yOffset + 'px');
.style('left', mouseMove.left + offset.left + 'px')
.style('top', mouseMove.top - offset.top + 'px');
})

.on('mouseout.tip', function () {
// Hide tooltip
// hide tooltip
return tooltipDiv.style('visibility', 'hidden');
});
});
};
};

Tooltip.prototype.getOffsets = function (tooltipDiv, mouseMove) {

var self = this;
var offset = {top: 10, left: 10};
var container;
var chartXoffset;
var chartYoffset;
var chartWidth;
var chartHeight;
var tipWidth;
var tipHeight;

if ($(self.el).find('.' + self.containerClass)) {
container = $(self.el).find('.' + self.containerClass);
chartXoffset = container.offset().left;
chartYoffset = container.offset().top;
chartWidth = container.width();
chartHeight = container.height();
tipWidth = tooltipDiv[0][0].clientWidth;
tipHeight = tooltipDiv[0][0].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
if ((mouseMove.left + offset.left + tipWidth) > (chartXoffset + chartWidth) &&
(mouseMove.left - tipWidth - 10) > chartXoffset) {
offset.left = -10 - tipWidth;
}

// change yOffset to keep tooltip within container
if ((mouseMove.top + tipHeight - 10) > (chartYoffset + chartHeight)) {
offset.top = chartYoffset + chartHeight;
}
}

return offset;
};

return Tooltip;
};
});
1 change: 1 addition & 0 deletions test/unit/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
'specs/vislib/column_layout',
'specs/vislib/index',
'specs/vislib/vis',
'specs/vislib/tooltip',
'specs/vislib/handler',
'specs/vislib/_error_handler',
'specs/vislib/data',
Expand Down
99 changes: 99 additions & 0 deletions test/unit/specs/vislib/tooltip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
define(function (require) {
var angular = require('angular');
var _ = require('lodash');
var $ = require('jquery');

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
}
];

beforeEach(function () {
module('TooltipFactory');
});

beforeEach(function () {
inject(function (d3, Private) {
Tooltip = Private(require('components/vislib/lib/tooltip'));

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;
});

chart = el.append('div').attr('class', 'chart');
el.append('div').attr('class', 'y-axis-col-wrapper');
el.append('div').attr('class', 'k4tip');

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;
});

bars.call(tip.render());

// d3.select('.bars').on('mousemove.tip')(d3.select('.bars').node().__data__, 0);
});



});

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');
});
});

});

0 comments on commit cee548a

Please sign in to comment.