From 0e8e5222ff9d48e72cb270ddf7b924e7c86760f0 Mon Sep 17 00:00:00 2001 From: Adrien Jarthon Date: Wed, 25 Mar 2015 18:05:58 +0100 Subject: [PATCH 1/3] Tooltip/popover: Fix auto placement to use viewport Currently, auto placement is using the container dimensions (if provided) or the element's parent to determine where to open the tooltip: ```javascript var $container = this.options.container ? $(this.options.container) : this.$element.parent() var containerDim = this.getPosition($container) ``` This is quite broken in fact, because the parent element could be just a small div outside the element for example, leading in a totally random placement (placing the tooltip on top even if there's no room). And the container can also be outside of the viewport. This fix simply uses the viewport instead, that's the purpose of the viewport actually, to position the tooltip. So the auto placement should use it to find where there's more room. By default this is body, which is good. --- js/tooltip.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/js/tooltip.js b/js/tooltip.js index 27367880f7..58ec5328e7 100644 --- a/js/tooltip.js +++ b/js/tooltip.js @@ -192,13 +192,12 @@ if (autoPlace) { var orgPlacement = placement - var $container = this.options.container ? $(this.options.container) : this.$element.parent() - var containerDim = this.getPosition($container) + var viewportDim = this.getPosition(this.$viewport) - placement = placement == 'bottom' && pos.bottom + actualHeight > containerDim.bottom ? 'top' : - placement == 'top' && pos.top - actualHeight < containerDim.top ? 'bottom' : - placement == 'right' && pos.right + actualWidth > containerDim.width ? 'left' : - placement == 'left' && pos.left - actualWidth < containerDim.left ? 'right' : + placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' : + placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' : + placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' : + placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' : placement $tip From df96c3e615919c008c113187ef07530a3bdc9f6f Mon Sep 17 00:00:00 2001 From: Adrien Jarthon Date: Sun, 29 Mar 2015 14:59:21 +0200 Subject: [PATCH 2/3] Added non-regression test & fixed existing one --- js/tests/unit/tooltip.js | 45 +++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/js/tests/unit/tooltip.js b/js/tests/unit/tooltip.js index 57a59db183..21d2641951 100644 --- a/js/tests/unit/tooltip.js +++ b/js/tests/unit/tooltip.js @@ -362,23 +362,19 @@ $(function () { assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom') }) - QUnit.test('should be placed dynamically with the dynamic placement option', function (assert) { + QUnit.test('should be placed dynamically to viewport with the dynamic placement option', function (assert) { assert.expect(6) - var $style = $('') + var $style = $('').appendTo('head') var $container = $('
') .css({ - position: 'absolute', - overflow: 'hidden', - width: 600, - height: 400, - top: 0, - left: 0 + position: 'relative', + height: '100%' }) - .appendTo(document.body) + .appendTo('#qunit-fixture') var $topTooltip = $('
Top Dynamic Tooltip
') .appendTo($container) - .bootstrapTooltip({ placement: 'auto' }) + .bootstrapTooltip({ placement: 'auto', viewport: '#qunit-fixture' }) $topTooltip.bootstrapTooltip('show') assert.ok($('.tooltip').is('.bottom'), 'top positioned tooltip is dynamically positioned to bottom') @@ -388,7 +384,7 @@ $(function () { var $rightTooltip = $('
Right Dynamic Tooltip
') .appendTo($container) - .bootstrapTooltip({ placement: 'right auto' }) + .bootstrapTooltip({ placement: 'right auto', viewport: '#qunit-fixture' }) $rightTooltip.bootstrapTooltip('show') assert.ok($('.tooltip').is('.left'), 'right positioned tooltip is dynamically positioned left') @@ -398,7 +394,7 @@ $(function () { var $leftTooltip = $('
Left Dynamic Tooltip
') .appendTo($container) - .bootstrapTooltip({ placement: 'auto left' }) + .bootstrapTooltip({ placement: 'auto left', viewport: '#qunit-fixture' }) $leftTooltip.bootstrapTooltip('show') assert.ok($('.tooltip').is('.right'), 'left positioned tooltip is dynamically positioned right') @@ -436,6 +432,31 @@ $(function () { $styles.remove() }) + QUnit.test('should position tip on top if viewport has enough space and is not parent', function (assert) { + assert.expect(2) + var styles = '' + var $styles = $(styles).appendTo('head') + + var $container = $('
').appendTo('#qunit-fixture') + var $target = $('
') + .appendTo($container) + .bootstrapTooltip({ + placement: 'auto top', + viewport: '#qunit-fixture' + }) + + $target.bootstrapTooltip('show') + assert.ok($('.tooltip').is('.top'), 'top positioned tooltip is dynamically positioned to top') + + $target.bootstrapTooltip('hide') + assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom') + + $styles.remove() + }) + QUnit.test('should position tip on bottom if the tip\'s dimension exceeds the viewport area and placement is "auto top"', function (assert) { assert.expect(2) var styles = '').appendTo('head') + var $style = $('').appendTo('head') var $container = $('
') .css({ position: 'relative',