Skip to content

Commit

Permalink
fix click handling, remove hover listeners, add/cleanup tests
Browse files Browse the repository at this point in the history
  • Loading branch information
John Fellman committed Jan 2, 2018
1 parent a44778b commit 92b7f48
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 19 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,12 @@ ember install ember-frost-popover

If the `frost-popover` component is placed next to the `target`, be careful to use a selector that will uniquely
identify the `target`. If it is nested inside the `target`, you can set `closest` to true which will search the
nearest ancestor from the `popover` - which is far more performant than a full dom traversal.
nearest ancestor from the `popover`.

### Hover Behavior

The `popover` will by default maintain its visible state when hovered.
If the events are `mouseenter` and `mouseleave`, adding a `hideDelay` makes hovering over the popover much easier for the user.

### A Note On Positioning

Expand Down
65 changes: 50 additions & 15 deletions addon/components/frost-popover.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ export default Component.extend(PropTypeMixin, {
let handlerIn = this.get('handlerIn')
let handlerOut = this.get('handlerOut')

if (event.split(' ').length === 2) { [handlerIn, handlerOut] = event.split(' ') }
if (event && event.split(' ').length === 2) {
[handlerIn, handlerOut] = event.split(' ')
this.setProperties({handlerIn, handlerOut})
}

if (handlerIn && handlerOut) {
this._eventHandlerIn = (event) => {
Expand Down Expand Up @@ -144,43 +147,75 @@ export default Component.extend(PropTypeMixin, {
$(target).on(event, this._eventHandler)
}

// handle mouse events on visible popover
$(popover).on('mouseenter', event => {
if (this.get('visible')) this.cancelShowDelayTask()
})
$(popover).on('mouseleave', event => {
if (this.get('visible')) {
if (hideDelay) {
this.showDelay(event, hideDelay)
} else {
this.togglePopover(event)
// add handlers for persisting visible state when hovering
if (handlerIn === 'mouseenter' && handlerOut === 'mouseleave') {
// functions declared here for scope
this._hoverHandlerIn = event => {
if (this.get('visible')) {
this.cancelShowDelayTask()
}
}
})
$(popover).on('click', event => event.stopPropagation())
this._hoverHandlerOut = event => {
const hideDelay = this.get('hideDelay')
if (this.get('visible')) {
if (hideDelay) {
this.showDelay(event, hideDelay)
} else {
this.togglePopover(event)
}
}
}
this._hoverClickHandler = event => {
if (this.get('stopPropagation')) {
event.stopPropagation()
}
}

// handle mouse events on visible popover
$(popover).on(handlerIn, this._hoverHandlerIn)
$(popover).on(handlerOut, this._hoverHandlerOut)
$(popover).on('click', this._hoverClickHandler)
}
},

willDestroyElement () {
const target = this.getTarget()
const event = this.get('event')
const handlerIn = this.get('handlerIn')
const handlerOut = this.get('handlerOut')
const popover = this.get('element')

if (handlerIn && handlerOut) {
$(target).off(handlerIn, this._eventHandlerIn)
$(target).off(handlerOut, this._eventHandlerOut)
} else {
$(target).off(event, this._eventHandler)
}

this.cancelShowDelayTask()
this.unregisterClickOff()

// remove listeners attached for hover behavior
if (handlerIn === 'mouseenter' && handlerOut === 'mouseleave') {
$(popover).off(handlerIn, this._hoverHandlerIn)
$(popover).off(handlerOut, this._hoverHandlerOut)
$(popover).off('click', this._hoverClickHandler)
}
},

/**
* Toggles the popover
* @param {DOMEvent} event - click event
* @param {DOMEvent} event - mouse event
*/
togglePopover (event) {
this.send('togglePopover', event)
const handlerIn = this.get('handlerIn')
const handlerOut = this.get('handlerOut')
const popover = this.get('element')

if ($(event.target).closest(popover).length === 0 ||
(handlerIn === 'mouseenter' && handlerOut === 'mouseleave')) {
this.send('togglePopover', event)
}
},

/**
Expand Down
6 changes: 3 additions & 3 deletions tests/dummy/app/pods/demo/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@

<h2>Activating Event</h2>
{{#frost-button hook='mouseEnterButton' size='small' priority='primary' class='event-mouse' text='Mouseenter'}}
{{#frost-popover target='.event-mouse' closest=true handlerIn='mouseenter' handlerOut='mouseleave'}}
{{#frost-popover target='.event-mouse' closest=true handlerIn='mouseenter' handlerOut='mouseleave' hideDelay=100}}
<span class='inside'>Tooltip is toggled on mouse enter and mouse leave</span>
{{/frost-popover}}
{{/frost-button}}
Expand All @@ -66,15 +66,15 @@
{{/frost-button}}

{{#frost-button hook='mouseEnterDelayButton' size='small' priority='primary' class='child' text='Hover me!'}}
{{#frost-popover target='.child' delay=500 handlerIn='mouseenter' handlerOut='mouseleave' excludePadding=true closest=true includeContentInEvents=true}}
{{#frost-popover target='.child' delay=500 handlerIn='mouseenter' handlerOut='mouseleave' excludePadding=true closest=true}}
<span class='inside'>Hover me delayed!</span>
{{/frost-popover}}
{{/frost-button}}

<h2>Hide Delay display</h2>
This feature doesn't properly work with 'click' at the moment.<br><br>
{{#frost-button hook='mouseEnterHideDelayButton' size='small' priority='primary' class='child' text='Hover me!'}}
{{#frost-popover target='.child' hideDelay=500 handlerIn='mouseenter' handlerOut='mouseleave' excludePadding=true closest=true includeContentInEvents=true}}
{{#frost-popover target='.child' hideDelay=500 handlerIn='mouseenter' handlerOut='mouseleave' excludePadding=true closest=true}}
<span class='inside'>Hover me hide delayed!</span>
{{/frost-popover}}
{{/frost-button}}
Expand Down
48 changes: 48 additions & 0 deletions tests/integration/components/frost-popover-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,54 @@ describe(test.label, function () {
})
})

describe('when hovering with mouseenter/mouseleave for handlerIn/handlerOut', function () {
this.timeout(5000)

beforeEach(function () {
this.render(hbs`
<div id='foo' class='target'>
hover test
</div>
{{#frost-popover target='#foo' hideDelay=500 event='mouseenter mouseleave'}}
<span class='inside'>Inside</span>
{{/frost-popover}}
`)

return wait()
.then(() => {
$('#foo').mouseenter()
return wait()
})
.then(() => {
$('#foo').mouseleave()

run.later(function () {
$('.tooltip-frost-popover').mouseenter()
}, 100)

return wait()
})
})

it('should still be visible 700ms later if mouseenter has been triggered on popover', function (done) {
run.later(function () {
expect($('.visible')).to.have.length(1)
done()
}, 700)
})

it('should dismiss after mouseleave after hovering', function (done) {
run.later(function () {
$('.tooltip-frost-popover').mouseleave()
}, 700)

run.later(function () {
expect($('.visible')).to.have.length(0)
done()
}, 1300)
})
})

it('should constrain to the viewport', function (done) {
this.render(hbs`
<div id='viewport' style='width: 400px; height: 400px;'>
Expand Down

0 comments on commit 92b7f48

Please sign in to comment.