Skip to content

Commit

Permalink
Tooltip test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
MeoMix committed May 15, 2015
1 parent 562f8ea commit 5152bf5
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 23 deletions.
32 changes: 19 additions & 13 deletions src/js/foreground/view/behavior/tooltipable.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,26 +70,32 @@
if (needShowTooltip) {
var boundingClientRect = target.getBoundingClientRect();
this._showTooltip(boundingClientRect, text);
this._watchTooltipText(target, boundingClientRect);
}
},

// If the data-tooltip-text attribute is modified while the tooltip is shown - refresh the tooltip.
this.mutationObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
var newText = $(mutation.target).attr(mutation.attributeName);
this._showTooltip(boundingClientRect, newText);
}.bind(this));
// Create a mutation observer which watches the target for changes to its tooltip-text data attribute.
// If that attribute changes then refresh the tooltip to reflect the new text.
_watchTooltipText: function(target, boundingClientRect) {
var mutationObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
var newText = mutation.target.getAttribute(mutation.attributeName);
this._showTooltip(boundingClientRect, newText);
}.bind(this));
}.bind(this));

this.mutationObserver.observe(target, {
attributes: true,
attributeFilter: ['data-tooltip-text'],
subtree: false
});
}
mutationObserver.observe(target, {
attributes: true,
attributeFilter: ['data-tooltip-text'],
subtree: false
});

this.mutationObserver = mutationObserver;
},

_needShowTooltip: function(target) {
// If the user is still hovering the element after the delay then go ahead and confirm the tooltip should be shown.
var showTooltip = $(target).data('is-hovered');
var showTooltip = $(target).data('is-hovered') || false;

if (showTooltip) {
// Some elements only want to show a tooltip if their text can't all be seen
Expand Down
19 changes: 10 additions & 9 deletions src/js/foreground/view/tooltip/tooltipRegion.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,24 @@
this.show(tooltipView);

// Let the DOM update so that offsetWidth and offsetHeight return correct values
requestAnimationFrame(function() {
var tooltipWidth = tooltipView.el.offsetWidth;
var tooltipHeight = tooltipView.el.offsetHeight;

// Figure out the desired offset of the tooltip based on its dimensions, location, and the dimensions of its target
var offset = this._getAdjustedOffset(options.targetBoundingClientRect, tooltipWidth, tooltipHeight);
tooltipView.showAtOffset(offset);
}.bind(this));
requestAnimationFrame(this._setTooltipViewOffset.bind(this, tooltipView, options.targetBoundingClientRect));
},

_hideTooltip: function() {
// TODO: I'd prefer doing this logic on the region itself, but making the region a panel conflicts with :hover once visible.
if (!_.isUndefined(this.currentView)) {
this.currentView.hide();
}
},

_setTooltipViewOffset: function(tooltipView, boundingClientRect) {
var tooltipWidth = tooltipView.el.offsetWidth;
var tooltipHeight = tooltipView.el.offsetHeight;

// Figure out the desired offset of the tooltip based on its dimensions, location, and the dimensions of its target
var offset = this._getAdjustedOffset(boundingClientRect, tooltipWidth, tooltipHeight);
tooltipView.showAtOffset(offset);
},

// Perform math in an attempt to center the tooltip along the bottom of a given element.
// If it can't go along the bottom then flip it and place it along the top.
// If it can't fit in the center then shift it to the left or right until it fits.
Expand Down
2 changes: 1 addition & 1 deletion src/js/foreground/view/tooltip/tooltipView.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

// Fade out the tooltip and then destroy it once completely hidden
hide: function() {
this.$el.off('webkitTransitionEnd').one('webkitTransitionEnd', this._onTransitionOutComplete.bind(this));
this.$el.one('webkitTransitionEnd', this._onTransitionOutComplete.bind(this));
this.$el.removeClass('is-visible');
},

Expand Down
6 changes: 6 additions & 0 deletions src/js/test/foreground/foregroundSpecLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
'use strict';

// /view/
// /view/behavior/
require('test/foreground/view/behavior/tooltipable.spec');

// /view/dialog/
require('test/foreground/view/dialog/browserSettingsDialogView.spec');
require('test/foreground/view/dialog/browserSettingsView.spec');
Expand All @@ -23,4 +26,7 @@

// /view/search/
require('test/foreground/view/search/searchView.spec');

// /view/tooltip/
require('test/foreground/view/tooltip/tooltipRegion.spec');
});
100 changes: 100 additions & 0 deletions src/js/test/foreground/view/behavior/tooltipable.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
define(function(require) {
'use strict';

var Tooltipable = require('foreground/view/behavior/tooltipable');

describe('Tooltipable', function() {
beforeEach(function() {
this.tooltipable = new Tooltipable();
});

it('should hide any tooltips when its view is destroyed', function() {
sinon.stub(this.tooltipable, '_hideTooltip');
this.tooltipable.onBeforeDestroy();
expect(this.tooltipable._hideTooltip.calledOnce).to.equal(true);
this.tooltipable._hideTooltip.restore();
});

it('should be able to mark a target as hovered', function() {
sinon.stub(window, 'setTimeout');
var target = document.createElement('div');
target.setAttribute('data-tooltip-text', 'hello, world');
this.tooltipable._setHovered(target);
expect($(target).data('is-hovered')).to.equal(true);
expect(window.setTimeout.calledOnce).to.equal(true);
window.setTimeout.restore();
});

it('should not re-mark an already hovered target', function() {
sinon.stub(window, 'setTimeout');
var target = document.createElement('div');
$(target).data('is-hovered', true);
this.tooltipable._setHovered(target);
expect(window.setTimeout.calledOnce).to.equal(false);
window.setTimeout.restore();
});

it('should not mark a target which does not have tooltip text', function() {
sinon.stub(window, 'setTimeout');
var target = document.createElement('div');
this.tooltipable._setHovered(target);
expect(window.setTimeout.calledOnce).to.equal(false);
window.setTimeout.restore();
});

it('should not mark a target which does has an empty string for tooltip text', function() {
sinon.stub(window, 'setTimeout');
var target = document.createElement('div');
target.setAttribute('data-tooltip-text', '');
this.tooltipable._setHovered(target);
expect(window.setTimeout.calledOnce).to.equal(false);
window.setTimeout.restore();
});

it('should be able to hide shown tooltips', function() {
this.tooltipable.isShowingTooltip = true;
this.tooltipable.mutationObserver = new MutationObserver(_.noop);
this.tooltipable._hideTooltip();
expect(this.tooltipable.isShowingTooltip).to.equal(false);
});

it('should set a mutation observer when watching a target', function() {
var target = document.createElement('div');
this.tooltipable._watchTooltipText(target, {});
expect(this.tooltipable.mutationObserver).not.to.equal(null);
});

it('should indicate that a tooltip should not be shown if target is not hovered', function() {
var target = document.createElement('div');
var showTooltip = this.tooltipable._needShowTooltip(target);
expect(showTooltip).to.equal(false);
});

it('should indicate that a tooltip should be shown if target is hovered and does not need an overflow check', function() {
var target = document.createElement('div');
$(target).data('is-hovered', true);
var showTooltip = this.tooltipable._needShowTooltip(target);
expect(showTooltip).to.equal(true);
});

it('should indicate that a tooltip should not be shown if target is hovered and fails an overflow check', function() {
var target = document.createElement('div');
$(target).data('is-hovered', true);
target.classList.add('js-textTooltipable');
var showTooltip = this.tooltipable._needShowTooltip(target);
expect(showTooltip).to.equal(false);
});

it('should be able to show a tooltip', function() {
sinon.stub(Streamus.channels.tooltip.commands, 'trigger');

this.tooltipable._showTooltip({}, '');

expect(this.tooltipable.isShowingTooltip).to.equal(true);
expect(Streamus.channels.tooltip.commands.trigger.calledOnce).to.equal(true);
expect(Streamus.channels.tooltip.commands.trigger.calledWithMatch('show:tooltip', {})).to.equal(true);

Streamus.channels.tooltip.commands.trigger.restore();
});
});
});
76 changes: 76 additions & 0 deletions src/js/test/foreground/view/tooltip/tooltipRegion.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
define(function(require) {
'use strict';

var TooltipRegion = require('foreground/view/tooltip/tooltipRegion');
var TooltipView = require('foreground/view/tooltip/tooltipView');

describe('TooltipRegion', function() {

beforeEach(function() {
this.tooltipRegion = new TooltipRegion({
el: document.createDocumentFragment()
});
});

it('should be able to show a tooltip view', function() {
sinon.stub(window, 'requestAnimationFrame');

this.tooltipRegion._showTooltip({
text: 'Hello, world',
targetBoundingClientRect: {
top: 0,
left: 0,
bottom: 0,
right: 0,
width: 0,
height: 0
}
});

expect(this.tooltipRegion.currentView).not.to.equal(undefined);
expect(window.requestAnimationFrame.calledOnce).to.equal(true);

window.requestAnimationFrame.restore();
});

it('should not throw an error if asked to hide a non-existing tooltip view', function() {
sinon.spy(this.tooltipRegion, '_hideTooltip');
this.tooltipRegion._hideTooltip();
expect(this.tooltipRegion._hideTooltip.threw()).to.equal(false);
this.tooltipRegion._hideTooltip.restore();
});

it('should be able to hide an existing view', function() {
var tooltipView = new TooltipView();
sinon.stub(tooltipView, 'hide');

this.tooltipRegion.currentView = tooltipView;
this.tooltipRegion._hideTooltip();
expect(tooltipView.hide.calledOnce).to.equal(true);
tooltipView.hide.restore();
});

it('should be able to set a tooltipview offset', function() {
var tooltipView = new TooltipView();
sinon.stub(tooltipView, 'showAtOffset');

var boundingClientRect = {};
this.tooltipRegion._setTooltipViewOffset(tooltipView, boundingClientRect);

expect(tooltipView.showAtOffset.calledOnce).to.equal(true);
tooltipView.showAtOffset.restore();
});

xit('should be able to get the adjusted offset', function() {

});

xit('should be able to get the shiftLeft offset', function() {

});

xit('should be able to get the flipInvertTop offset', function() {

});
});
});
1 change: 1 addition & 0 deletions src/js/test/plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
simpleMenu: Backbone.Wreqr.radio.channel('simpleMenu'),
video: Backbone.Wreqr.radio.channel('video'),
playPauseButton: Backbone.Wreqr.radio.channel('playPauseButton'),
tooltip: Backbone.Wreqr.radio.channel('tooltip'),

// BACKGROUND:
tab: Backbone.Wreqr.radio.channel('tab'),
Expand Down

0 comments on commit 5152bf5

Please sign in to comment.