Skip to content

Commit

Permalink
Merge pull request #482 from CartoDB/140-tooltip-overflow
Browse files Browse the repository at this point in the history
Handle tooltip overflow
  • Loading branch information
alonsogarciapablo committed May 20, 2015
2 parents 7cd9f98 + c20d29e commit 9f6985f
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 37 deletions.
3 changes: 2 additions & 1 deletion NEWS
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
3.14.X ()
* Hide <img> tag of infowindow covers when the url is invalid.
* Expose legend model in sublayers/layers so that users can customize layers (#480).
* Expose legend model in sublayers/layers so that users can customize legends (#480).
* Handle tooltip overflow (#482).

3.14.2 (06//05//2015)
* Allow to specify a template for the items of a custom legend.
Expand Down
55 changes: 36 additions & 19 deletions src/geo/ui/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ cdb.geo.ui.Tooltip = cdb.geo.ui.InfoBox.extend({
},

initialize: function() {
if(!this.options.mapView) {
throw new Error("mapView should be present");
}
this.options.template = this.options.template || this.defaultTemplate;
cdb.geo.ui.InfoBox.prototype.initialize.call(this);
this._filter = null;
Expand Down Expand Up @@ -130,39 +133,53 @@ cdb.geo.ui.Tooltip = cdb.geo.ui.InfoBox.extend({
},

setPosition: function(point) {
var props = {
left: 0,
top: 0
};

var pos = this.options.position;
var $el = this.$el;
var h = $el.innerHeight();
var w = $el.innerWidth();
var height = this.$el.innerHeight();
var width = this.$el.innerWidth();
var mapViewSize = this.options.mapView.getSize();
var top = 0;
var left = 0;

// Vertically
if (pos.indexOf('top') !== -1) {
props.top = -h;
top = point.y - height;
} else if (pos.indexOf('middle') !== -1) {
props.top = -(h/2);
top = point.y - (height/2);
} else { // bottom
top = point.y;
}

// Fix vertical overflow
if (top < 0) {
top = point.y;
} else if (top + height > mapViewSize.y) {
top = point.y - height;
}

// Horizontally
if(pos.indexOf('left') !== -1) {
props.left = -w;
left = point.x - width;
} else if(pos.indexOf('center') !== -1) {
props.left = -(w/2);
left = point.x - (width/2);
} else { // right
left = point.x;
}

// Offsets
props.top += this.options.vertical_offset;
props.left += this.options.horizontal_offset;
// Fix horizontal overflow
if (left < 0) {
left = point.x;
} else if (left + width > mapViewSize.x) {
left = point.x - width;
}

$el.css({
top: (point.y + props.top),
left: (point.x + props.left)
});
// Add offsets
top += this.options.vertical_offset;
left += this.options.horizontal_offset;

this.$el.css({
top: top,
left: left
});
},

render: function(data) {
Expand Down
1 change: 1 addition & 0 deletions src/vis/overlays.js
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ cdb.vis.Overlay.register('tooltip', function(data, vis) {
}
data.layer = data.layer || vis.getLayers()[1];
data.layer.setInteraction(true);
data.mapView = vis.mapView;
return new cdb.geo.ui.Tooltip(data);
});

Expand Down
1 change: 1 addition & 0 deletions src/vis/vis.js
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,7 @@ var Vis = cdb.core.View.extend({
if (t) {
if (!layerView.tooltip) {
var tooltip = new cdb.geo.ui.Tooltip({
mapView: this.mapView,
layer: layerView,
template: t.template,
position: 'bottom|right',
Expand Down
109 changes: 92 additions & 17 deletions test/spec/geo/ui/tooltip.spec.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@

describe('cdb.geo.Tooltip', function() {

var tooltip, layer;
var tooltip, layer, container, mapView;
beforeEach(function() {
container = $("<div id='map'>").css('height', '1000px');
$('body').append(container)
var map = new cdb.geo.Map();
mapView = new cdb.geo.LeafletMapView({
el: $('#map'),
map: map
});

layer = new Backbone.Model();
tooltip = new cdb.geo.ui.Tooltip({
template: '{{#fields}}{{{ value }}},{{/fields}}',
layer: layer
layer: layer,
mapView: mapView
});
});

afterEach(function() {
$('#map').remove();
});

it ("should render fields in specified order", function() {
tooltip.setFields([{
Expand Down Expand Up @@ -60,35 +72,98 @@ describe('cdb.geo.Tooltip', function() {
expect(tooltip.$el.html()).toEqual('test2,testnamed,huracan,');
});

it ("should positionate the element correctly", function() {
it ("should position the element correctly", function() {
tooltip.$el.css('width', '200px');
tooltip.$el.css('height', '20px');
var data = { cartodb_id: 2, description: 'test' };

tooltip.options.position = 'bottom|right';
tooltip.show({ x:0, y:0 }, data);
expect(tooltip.$el.css('top')).toBe('0px');
expect(tooltip.$el.css('left')).toBe('0px');
tooltip.show({ x:10, y:10 }, data);
expect(tooltip.$el.css('top')).toBe('10px');
expect(tooltip.$el.css('left')).toBe('10px');

tooltip.options.position = 'top|left';
tooltip.show({ x:10, y:10 }, data);
expect(tooltip.$el.css('top')).toBe('-10px');
expect(tooltip.$el.css('left')).toBe('-190px');
tooltip.show({ x:210, y:40 }, data);
expect(tooltip.$el.css('top')).toBe('20px');
expect(tooltip.$el.css('left')).toBe('10px');

tooltip.options.position = 'middle|center';
tooltip.show({ x:10, y:10 }, data);
expect(tooltip.$el.css('top')).toBe('0px');
expect(tooltip.$el.css('left')).toBe('-90px');
tooltip.show({ x:150, y:30 }, data);
expect(tooltip.$el.css('top')).toBe('20px');
expect(tooltip.$el.css('left')).toBe('50px');

// With offsets

tooltip.options.position = 'middle|center';
tooltip.options.vertical_offset = -10;
tooltip.options.horizontal_offset = -10;
tooltip.show({ x:10, y:10 }, data);
expect(tooltip.$el.css('top')).toBe('-10px');
expect(tooltip.$el.css('left')).toBe('-100px');

tooltip.show({ x:150, y:30 }, data);
expect(tooltip.$el.css('top')).toBe('10px');
expect(tooltip.$el.css('left')).toBe('40px');
});

describe('overflow positioning', function() {
var data;

beforeEach(function() {
data = { cartodb_id: 2, description: 'test' };
$('#map').css('height', '100px');
$('#map').css('width', '100px');
tooltip.$el.css('height', '80px');
tooltip.$el.css('width', '80px');
mapView.invalidateSize();
});

it('should position the element on top when bottom overflow occurs', function() {
tooltip.options.position = 'bottom|right';

tooltip.show({ x:0, y:90 }, data);
expect(tooltip.$el.css('top')).toBe('10px');
});

it('should position the element on the bottom when top overflow occurs', function() {
tooltip.options.position = 'top|right';

tooltip.show({ x:0, y:10 }, data);
expect(tooltip.$el.css('top')).toBe('10px');
});

it('should position the element on top/bottom when overflow vertically centered and overflow occurs', function() {
tooltip.options.position = 'middle|right';

tooltip.show({ x:0, y:90 }, data);
expect(tooltip.$el.css('top')).toBe('10px');

tooltip.options.position = 'middle|right';

tooltip.show({ x:0, y:10 }, data);
expect(tooltip.$el.css('top')).toBe('10px');
})

it('should position the element on the left when right overflow occurs', function() {
tooltip.options.position = 'top|right';

tooltip.show({ x:90, y:10 }, data);
expect(tooltip.$el.css('left')).toBe('10px');
});

it('should position the element on the right when left overflow occurs', function() {
tooltip.options.position = 'top|left';

tooltip.show({ x:10, y:10 }, data);
expect(tooltip.$el.css('left')).toBe('10px');
});

it('should position the element on the left/right when horizontally centered and overflow occurs', function() {
tooltip.options.position = 'top|center';

tooltip.show({ x:10, y:10 }, data);
expect(tooltip.$el.css('left')).toBe('10px');

tooltip.options.position = 'top|center';

tooltip.show({ x:90, y:10 }, data);
expect(tooltip.$el.css('left')).toBe('10px');
});

});
});

0 comments on commit 9f6985f

Please sign in to comment.