From 35c3f5fcd7d882fa385a8d2a2fdf058ca8e96d15 Mon Sep 17 00:00:00 2001 From: a15n Date: Mon, 29 Aug 2016 12:59:23 -0700 Subject: [PATCH 01/38] initial commit --- addon/styles/addon.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/addon/styles/addon.scss b/addon/styles/addon.scss index 847ddfd2..506110a1 100644 --- a/addon/styles/addon.scss +++ b/addon/styles/addon.scss @@ -25,6 +25,10 @@ } } +.ember-popover { + +} + .ember-tooltip.ember-tooltip-target-attached-top:after, .ember-tooltip.ember-tooltip-target-attached-top-left:after, .ember-tooltip.ember-tooltip-target-attached-top-right:after { bottom: 0; } .ember-tooltip.ember-tooltip-target-attached-bottom:after, .ember-tooltip.ember-tooltip-target-attached-bottom-left:after, .ember-tooltip.ember-tooltip-target-attached-bottom-right:after { top: 0; } .ember-tooltip.ember-tooltip-target-attached-top:after, .ember-tooltip.ember-tooltip-target-attached-bottom:after { left: 50%; } From c5da8b755e141b49cc3b0cb64b71ece927da5527 Mon Sep 17 00:00:00 2001 From: a15n Date: Mon, 29 Aug 2016 13:04:25 -0700 Subject: [PATCH 02/38] git mv tooltip-on-element -> tooltip-and-popover --- .../components/{tooltip-on-element.js => tooltip-and-popover.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename addon/components/{tooltip-on-element.js => tooltip-and-popover.js} (100%) diff --git a/addon/components/tooltip-on-element.js b/addon/components/tooltip-and-popover.js similarity index 100% rename from addon/components/tooltip-on-element.js rename to addon/components/tooltip-and-popover.js From 324740826b98dd62a46b3b2ecc2bd9199fa3c12a Mon Sep 17 00:00:00 2001 From: a15n Date: Mon, 29 Aug 2016 13:11:59 -0700 Subject: [PATCH 03/38] add back original tooltip-on-element.js --- addon/components/tooltip-on-element.js | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 addon/components/tooltip-on-element.js diff --git a/addon/components/tooltip-on-element.js b/addon/components/tooltip-on-element.js new file mode 100644 index 00000000..92d99713 --- /dev/null +++ b/addon/components/tooltip-on-element.js @@ -0,0 +1,3 @@ +import TooltipAndPopoverComponent from 'ember-tooltips/components/tooltip-and-popover'; + +export default TooltipAndPopoverComponent.extend(); From 2b99f85f041003a3bfb5fa227afd41af196e1bf3 Mon Sep 17 00:00:00 2001 From: a15n Date: Mon, 29 Aug 2016 13:20:41 -0700 Subject: [PATCH 04/38] move .on event handling into tooltip-on-element.didInsertElement --- addon/components/tooltip-and-popover.js | 60 -------------------- addon/components/tooltip-on-element.js | 75 ++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 61 deletions(-) diff --git a/addon/components/tooltip-and-popover.js b/addon/components/tooltip-and-popover.js index 814ede22..ddad7ced 100644 --- a/addon/components/tooltip-and-popover.js +++ b/addon/components/tooltip-and-popover.js @@ -229,72 +229,12 @@ export default EmberTetherComponent.extend({ Ember.assert('You must specify a target attribute in the format target="#element-id" for the tooltip component'); } - const event = this.get('event'); const $target = $(this.get('target')); const _tether = this.get('_tether'); const $_tether = $(_tether.element); this.sendAction('onTooltipRender', this); - /* Setup event handling to hide and show the tooltip */ - - if (event !== 'none') { - const _hideOn = this.get('_hideOn'); - const _showOn = this.get('_showOn'); - - /* If show and hide are the same (e.g. click), toggle - the visibility */ - - if (_showOn === _hideOn) { - $target.on(_showOn, () => { - this.toggle(); - }); - } else { - - /* Else, add the show and hide events individually */ - - if (_showOn !== 'none') { - $target.on(_showOn, () => { - this.show(); - }); - } - - if (_hideOn !== 'none') { - $target.on(_hideOn, () => { - this.hide(); - }); - } - } - - /* Hide and show the tooltip on focus and escape - for acessibility */ - - if (event !== 'focus') { - - /* If the event is click, we don't want the - click to also trigger focusin */ - - if (event !== 'click') { - $target.focusin(() => { - this.show(); - }); - } - - $target.focusout(() => { - this.hide(); - }); - } - - $target.keydown((keyEvent) => { - if (keyEvent.which === 27) { - this.hide(); - keyEvent.preventDefault(); - - return false; - } - }); - } - $target.attr({ 'aria-describedby': `${this.get('elementId')}`, tabindex: $target.attr('tabindex') || this.get('tabindex'), diff --git a/addon/components/tooltip-on-element.js b/addon/components/tooltip-on-element.js index 92d99713..599456c9 100644 --- a/addon/components/tooltip-on-element.js +++ b/addon/components/tooltip-on-element.js @@ -1,3 +1,76 @@ +import Ember from 'ember'; import TooltipAndPopoverComponent from 'ember-tooltips/components/tooltip-and-popover'; -export default TooltipAndPopoverComponent.extend(); +const { $ } = Ember; + +export default TooltipAndPopoverComponent.extend({ + + didInsertElement() { + this._super(...arguments); + + /* Setup event handling to hide and show the tooltip */ + + const $target = $(this.get('target')); + const event = $(this.get('event')); + + /* Setup event handling to hide and show the tooltip */ + + if (event !== 'none') { + const _hideOn = this.get('_hideOn'); + const _showOn = this.get('_showOn'); + + /* If show and hide are the same (e.g. click), toggle + the visibility */ + + if (_showOn === _hideOn) { + $target.on(_showOn, () => { + this.toggle(); + }); + } else { + + /* Else, add the show and hide events individually */ + + if (_showOn !== 'none') { + $target.on(_showOn, () => { + this.show(); + }); + } + + if (_hideOn !== 'none') { + $target.on(_hideOn, () => { + this.hide(); + }); + } + } + + /* Hide and show the tooltip on focus and escape + for acessibility */ + + if (event !== 'focus') { + + /* If the event is click, we don't want the + click to also trigger focusin */ + + if (event !== 'click') { + $target.focusin(() => { + this.show(); + }); + } + + $target.focusout(() => { + this.hide(); + }); + } + + $target.keydown((keyEvent) => { + if (keyEvent.which === 27) { + this.hide(); + keyEvent.preventDefault(); + + return false; + } + }); + } + }, + +}); From e583c747db39ae145b1497f4d6ff7c466701d7cc Mon Sep 17 00:00:00 2001 From: a15n Date: Mon, 29 Aug 2016 13:29:41 -0700 Subject: [PATCH 05/38] add popover tests for TDD --- addon/components/popover-on-element.js | 76 +++++++++++++++++++ app/components/popover-on-element.js | 1 + .../components/popover/duration-test.js | 34 +++++++++ .../components/popover/side-test.js | 51 +++++++++++++ .../components/popover/spacing-test.js | 43 +++++++++++ .../popover/tooltip-is-visible-test.js | 28 +++++++ 6 files changed, 233 insertions(+) create mode 100644 addon/components/popover-on-element.js create mode 100644 app/components/popover-on-element.js create mode 100644 tests/integration/components/popover/duration-test.js create mode 100644 tests/integration/components/popover/side-test.js create mode 100644 tests/integration/components/popover/spacing-test.js create mode 100644 tests/integration/components/popover/tooltip-is-visible-test.js diff --git a/addon/components/popover-on-element.js b/addon/components/popover-on-element.js new file mode 100644 index 00000000..599456c9 --- /dev/null +++ b/addon/components/popover-on-element.js @@ -0,0 +1,76 @@ +import Ember from 'ember'; +import TooltipAndPopoverComponent from 'ember-tooltips/components/tooltip-and-popover'; + +const { $ } = Ember; + +export default TooltipAndPopoverComponent.extend({ + + didInsertElement() { + this._super(...arguments); + + /* Setup event handling to hide and show the tooltip */ + + const $target = $(this.get('target')); + const event = $(this.get('event')); + + /* Setup event handling to hide and show the tooltip */ + + if (event !== 'none') { + const _hideOn = this.get('_hideOn'); + const _showOn = this.get('_showOn'); + + /* If show and hide are the same (e.g. click), toggle + the visibility */ + + if (_showOn === _hideOn) { + $target.on(_showOn, () => { + this.toggle(); + }); + } else { + + /* Else, add the show and hide events individually */ + + if (_showOn !== 'none') { + $target.on(_showOn, () => { + this.show(); + }); + } + + if (_hideOn !== 'none') { + $target.on(_hideOn, () => { + this.hide(); + }); + } + } + + /* Hide and show the tooltip on focus and escape + for acessibility */ + + if (event !== 'focus') { + + /* If the event is click, we don't want the + click to also trigger focusin */ + + if (event !== 'click') { + $target.focusin(() => { + this.show(); + }); + } + + $target.focusout(() => { + this.hide(); + }); + } + + $target.keydown((keyEvent) => { + if (keyEvent.which === 27) { + this.hide(); + keyEvent.preventDefault(); + + return false; + } + }); + } + }, + +}); diff --git a/app/components/popover-on-element.js b/app/components/popover-on-element.js new file mode 100644 index 00000000..d7da7eca --- /dev/null +++ b/app/components/popover-on-element.js @@ -0,0 +1 @@ +export { default } from 'ember-tooltips/components/popover-on-element'; diff --git a/tests/integration/components/popover/duration-test.js b/tests/integration/components/popover/duration-test.js new file mode 100644 index 00000000..7aaef2ce --- /dev/null +++ b/tests/integration/components/popover/duration-test.js @@ -0,0 +1,34 @@ +import Ember from 'ember'; +import { moduleForComponent, test } from 'ember-qunit'; +import { assertHide, assertShow } from '../../../helpers/sync/assert-visibility'; +import hbs from 'htmlbars-inline-precompile'; + +const { run } = Ember; + +moduleForComponent('popover-on-element', 'Integration | Option | duration', { + integration: true +}); + +test('Popover hides after the given duration', function(assert) { + const done = assert.async(); + + assert.expect(3); + + this.render(hbs`{{popover-on-element duration=300}}`); + + assertHide(assert, this); + + /* Check the tooltip is hidden after the duration */ + + run(() => { + this.$().trigger('mouseover'); + }); + + assertShow(assert, this); + + run.later(() => { + assertHide(assert, this); + done(); + }, 320); + +}); diff --git a/tests/integration/components/popover/side-test.js b/tests/integration/components/popover/side-test.js new file mode 100644 index 00000000..55b24c17 --- /dev/null +++ b/tests/integration/components/popover/side-test.js @@ -0,0 +1,51 @@ +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; + +function assertPosition(assert, context, expectedSide) { + const $this = context.$(); + const targetPosition = $this.position(); + const tooltipPosition = $this.find('.ember-tooltip').position(); + + if (expectedSide === 'top') { + assert.ok(targetPosition.top > tooltipPosition.top, + 'Popover should be above the target'); + } else if (expectedSide === 'right') { + assert.ok(targetPosition.left < tooltipPosition.left, + 'Popover should be right of the target'); + } else if (expectedSide === 'bottom') { + assert.ok(targetPosition.top < tooltipPosition.top, + 'Popover should be below the target'); + } else if (expectedSide === 'left') { + assert.ok(targetPosition.left > tooltipPosition.left, + 'Popover should be left of the target'); + } +} + +moduleForComponent('popover-on-element', 'Integration | Option | side and keepInWindow', { + integration: true +}); + +test('Popover shows with showOn', function(assert) { + + assert.expect(4); + + /* Test the positions without forcing the tooltip + to stay in the window. */ + + this.render(hbs`{{popover-on-element side='top' keepInWindow=false}}`); + + assertPosition(assert, this, 'top'); + + this.render(hbs`{{popover-on-element side='right' keepInWindow=false}}`); + + assertPosition(assert, this, 'right'); + + this.render(hbs`{{popover-on-element side='bottom' keepInWindow=false}}`); + + assertPosition(assert, this, 'bottom'); + + this.render(hbs`{{popover-on-element side='left' keepInWindow=false}}`); + + assertPosition(assert, this, 'left'); + +}); diff --git a/tests/integration/components/popover/spacing-test.js b/tests/integration/components/popover/spacing-test.js new file mode 100644 index 00000000..514f30ab --- /dev/null +++ b/tests/integration/components/popover/spacing-test.js @@ -0,0 +1,43 @@ +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; + +function assertSpacing(assert, context, expectedSpacing) { + const $this = context.$(); + const targetPosition = $this.position(); + const $tooltip = $this.find('.ember-tooltip'); + const tooltipPosition = $tooltip.position(); + const offset = Math.floor(targetPosition.top - tooltipPosition.top); + + const paddingTop = parseInt($tooltip.css('padding-top')); + const paddingBottom = parseInt($tooltip.css('padding-bottom')); + const actualSpacing = offset - paddingTop - paddingBottom; + + /* Allow a small margin of error because of how browsers + render pixels */ + + assert.ok(expectedSpacing - 2 < actualSpacing && actualSpacing < expectedSpacing + 2, + `Popover should be ${expectedSpacing}px from the target`); + +} + +moduleForComponent('popover-on-element', 'Integration | Option | spacing', { + integration: true +}); + +test('It shows with showOn', function(assert) { + + assert.expect(2); + + /* Check the default spacing */ + + this.render(hbs`{{popover-on-element}}`); + + assertSpacing(assert, this, 10); + + /* Check custom spacing */ + + this.render(hbs`{{popover-on-element spacing=20}}`); + + assertSpacing(assert, this, 20); + +}); diff --git a/tests/integration/components/popover/tooltip-is-visible-test.js b/tests/integration/components/popover/tooltip-is-visible-test.js new file mode 100644 index 00000000..08e55724 --- /dev/null +++ b/tests/integration/components/popover/tooltip-is-visible-test.js @@ -0,0 +1,28 @@ +import Ember from 'ember'; +import { moduleForComponent, test } from 'ember-qunit'; +import { assertHide, assertShow } from '../../../helpers/sync/assert-visibility'; +import hbs from 'htmlbars-inline-precompile'; + +const { run } = Ember; + +moduleForComponent('popover-on-element', 'Integration | Option | tooltipIsVisible', { + integration: true +}); + +test('Popover toggles with tooltipIsVisible', function(assert) { + + assert.expect(2); + + this.set('showTooltip', true); + + this.render(hbs`{{popover-on-element tooltipIsVisible=showTooltip}}`); + + assertShow(assert, this); + + run(() => { + this.set('showTooltip', false); + }); + + assertHide(assert, this); + +}); From 3758f3b112d5a36453ed6df15fb218a4c60bca8e Mon Sep 17 00:00:00 2001 From: a15n Date: Mon, 29 Aug 2016 13:35:15 -0700 Subject: [PATCH 06/38] split the classes. removed transition to test visibility (will revert) --- addon/components/popover-on-element.js | 3 ++- addon/components/tooltip-and-popover.js | 2 +- addon/components/tooltip-on-element.js | 3 ++- addon/styles/addon.scss | 11 ++++++----- tests/helpers/sync/assert-visibility.js | 8 ++++++-- tests/integration/components/actions-test.js | 2 +- tests/integration/components/delay-on-change-test.js | 2 +- tests/integration/components/delay-test.js | 4 ++-- tests/integration/components/duration-test.js | 6 +++--- tests/integration/components/event-test.js | 8 ++++---- tests/integration/components/hide-on-test.js | 2 +- tests/integration/components/popover/duration-test.js | 2 +- tests/integration/components/popover/side-test.js | 4 ++-- tests/integration/components/popover/spacing-test.js | 4 ++-- .../components/popover/tooltip-is-visible-test.js | 2 +- tests/integration/components/show-on-test.js | 2 +- tests/integration/components/side-test.js | 4 ++-- tests/integration/components/spacing-test.js | 2 +- .../integration/components/tooltip-is-visible-test.js | 2 +- .../integration/components/tooltip-on-element-test.js | 2 +- 20 files changed, 41 insertions(+), 34 deletions(-) diff --git a/addon/components/popover-on-element.js b/addon/components/popover-on-element.js index 599456c9..f9662d2f 100644 --- a/addon/components/popover-on-element.js +++ b/addon/components/popover-on-element.js @@ -5,13 +5,14 @@ const { $ } = Ember; export default TooltipAndPopoverComponent.extend({ + classNames: ['ember-popover'], didInsertElement() { this._super(...arguments); /* Setup event handling to hide and show the tooltip */ const $target = $(this.get('target')); - const event = $(this.get('event')); + const event = this.get('event'); /* Setup event handling to hide and show the tooltip */ diff --git a/addon/components/tooltip-and-popover.js b/addon/components/tooltip-and-popover.js index ddad7ced..cc6b892f 100644 --- a/addon/components/tooltip-and-popover.js +++ b/addon/components/tooltip-and-popover.js @@ -67,7 +67,7 @@ export default EmberTetherComponent.extend({ attributeBindings: ['aria-hidden', 'role', 'tabindex'], classNameBindings: ['effectClass'], classPrefix: 'ember-tooltip', - classNames: ['ember-tooltip'], + classNames: ['tooltip-and-popover'], _didUpdateTimeoutLength: 1000, // 1000 ms or 0 ms, depending whether in test mode _hideTimer: null, diff --git a/addon/components/tooltip-on-element.js b/addon/components/tooltip-on-element.js index 599456c9..db111c05 100644 --- a/addon/components/tooltip-on-element.js +++ b/addon/components/tooltip-on-element.js @@ -5,13 +5,14 @@ const { $ } = Ember; export default TooltipAndPopoverComponent.extend({ + classNames: ['ember-tooltip'], didInsertElement() { this._super(...arguments); /* Setup event handling to hide and show the tooltip */ const $target = $(this.get('target')); - const event = $(this.get('event')); + const event = this.get('event'); /* Setup event handling to hide and show the tooltip */ diff --git a/addon/styles/addon.scss b/addon/styles/addon.scss index 506110a1..1faf3f1d 100644 --- a/addon/styles/addon.scss +++ b/addon/styles/addon.scss @@ -1,4 +1,4 @@ -.ember-tooltip { +.tooltip-and-popover { max-width: 200px; color: #fff; background: #3a3c47; @@ -43,13 +43,13 @@ /* Fade */ -.ember-tooltip { +.tooltip-and-popover { opacity: 0; &.ember-tooltip-slide, &.ember-tooltip-fade { - -webkit-transition: opacity 200ms ease-out, margin 200ms ease-out; - transition: opacity 200ms ease-out, margin 200ms ease-out; + // -webkit-transition: opacity 200ms ease-out, margin 200ms ease-out; + // transition: opacity 200ms ease-out, margin 200ms ease-out; } &.ember-tooltip-slide { @@ -72,7 +72,8 @@ &[aria-hidden="false"] { opacity: 1; - transition-duration: 100ms; // Shorter hide duration + // transition-duration: 100ms; // Shorter hide duration + // TODO(Andrew) revert this &.ember-tooltip-slide { margin: 0px; diff --git a/tests/helpers/sync/assert-visibility.js b/tests/helpers/sync/assert-visibility.js index cf0a5e96..05ec5bff 100644 --- a/tests/helpers/sync/assert-visibility.js +++ b/tests/helpers/sync/assert-visibility.js @@ -1,13 +1,17 @@ export function assertShow(assert, context) { - assert.equal(context.$().find('.ember-tooltip').attr('aria-hidden'), 'false', + assert.equal(context.$().find('.tooltip-and-popover').attr('aria-hidden'), 'false', 'Should show tooltip'); + assert.equal(context.$().find('.tooltip-and-popover').css('opacity'), '1', + 'Should show tooltip (opacity == 1)'); // TODO(Andrew) remove this, rely on aria } export function assertHide(assert, context) { - assert.equal(context.$().find('.ember-tooltip').attr('aria-hidden'), 'true', + assert.equal(context.$().find('.tooltip-and-popover').attr('aria-hidden'), 'true', 'Should hide tooltip'); + assert.equal(context.$().find('.tooltip-and-popover').css('opacity'), '0', + 'Should hide tooltip (opacity == 0)'); // TODO(Andrew) remove this, rely on aria } diff --git a/tests/integration/components/actions-test.js b/tests/integration/components/actions-test.js index c9fa7250..43c4331e 100644 --- a/tests/integration/components/actions-test.js +++ b/tests/integration/components/actions-test.js @@ -16,7 +16,7 @@ test('It animates with delay passed as a number', function(assert) { 'onTooltipShow': 0, }; - assert.expect(10); + // assert.expect(10); /* Setup the actions and handlers... */ diff --git a/tests/integration/components/delay-on-change-test.js b/tests/integration/components/delay-on-change-test.js index 6cf4f2d0..f4a6289c 100644 --- a/tests/integration/components/delay-on-change-test.js +++ b/tests/integration/components/delay-on-change-test.js @@ -11,7 +11,7 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | delayOnChange test('It animates with a delay', function(assert) { const done = assert.async(); - assert.expect(2); + // assert.expect(2); /* Create two tooltips and show one */ diff --git a/tests/integration/components/delay-test.js b/tests/integration/components/delay-test.js index 089882c6..b410f594 100644 --- a/tests/integration/components/delay-test.js +++ b/tests/integration/components/delay-test.js @@ -12,7 +12,7 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | delay', { test('It animates with delay passed as a number', function(assert) { const done = assert.async(); - assert.expect(4); + // assert.expect(4); this.render(hbs`{{tooltip-on-component delay=300}}`); @@ -49,7 +49,7 @@ test('It animates with delay passed as a number', function(assert) { test('It animates with delay passed as a string', function(assert) { const done = assert.async(); - assert.expect(4); + // assert.expect(4); this.render(hbs`{{tooltip-on-component delay='300'}}`); diff --git a/tests/integration/components/duration-test.js b/tests/integration/components/duration-test.js index b728cfc8..66c585fe 100644 --- a/tests/integration/components/duration-test.js +++ b/tests/integration/components/duration-test.js @@ -12,7 +12,7 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | duration', { test('It hides after the given duration', function(assert) { const done = assert.async(); - assert.expect(3); + // assert.expect(3); this.render(hbs`{{tooltip-on-component duration=300}}`); @@ -35,7 +35,7 @@ test('It hides after the given duration', function(assert) { test('It hides before the given duration, if requested', function(assert) { - assert.expect(3); + // assert.expect(3); this.render(hbs`{{tooltip-on-component duration=300}}`); @@ -58,7 +58,7 @@ test('It hides before the given duration, if requested', function(assert) { test('It uses duration after the first show', function(assert) { const done = assert.async(); - assert.expect(5); + // assert.expect(5); this.render(hbs`{{tooltip-on-component duration=300}}`); diff --git a/tests/integration/components/event-test.js b/tests/integration/components/event-test.js index 03a5ba34..5a1b874f 100644 --- a/tests/integration/components/event-test.js +++ b/tests/integration/components/event-test.js @@ -11,7 +11,7 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | event', { test('It toggles with hover', function(assert) { - assert.expect(3); + // assert.expect(3); this.render(hbs`{{tooltip-on-component}}`); @@ -33,7 +33,7 @@ test('It toggles with hover', function(assert) { test('It toggles with click', function(assert) { - assert.expect(3); + // assert.expect(3); this.render(hbs`{{tooltip-on-component event='click'}}`); @@ -55,7 +55,7 @@ test('It toggles with click', function(assert) { test('It toggles with focus', function(assert) { - assert.expect(3); + // assert.expect(3); this.render(hbs`{{tooltip-on-component event='focus'}}`); @@ -77,7 +77,7 @@ test('It toggles with focus', function(assert) { test('It does not show with none', function(assert) { - assert.expect(4); + // assert.expect(4); this.render(hbs`{{tooltip-on-component event='none'}}`); diff --git a/tests/integration/components/hide-on-test.js b/tests/integration/components/hide-on-test.js index f79db7de..c85bd299 100644 --- a/tests/integration/components/hide-on-test.js +++ b/tests/integration/components/hide-on-test.js @@ -11,7 +11,7 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | hideOn', { test('It hides with hideOn', function(assert) { - assert.expect(3); + // assert.expect(3); this.render(hbs`{{tooltip-on-component hideOn='click'}}`); diff --git a/tests/integration/components/popover/duration-test.js b/tests/integration/components/popover/duration-test.js index 7aaef2ce..3233ad12 100644 --- a/tests/integration/components/popover/duration-test.js +++ b/tests/integration/components/popover/duration-test.js @@ -12,7 +12,7 @@ moduleForComponent('popover-on-element', 'Integration | Option | duration', { test('Popover hides after the given duration', function(assert) { const done = assert.async(); - assert.expect(3); + // assert.expect(3); this.render(hbs`{{popover-on-element duration=300}}`); diff --git a/tests/integration/components/popover/side-test.js b/tests/integration/components/popover/side-test.js index 55b24c17..e5831f51 100644 --- a/tests/integration/components/popover/side-test.js +++ b/tests/integration/components/popover/side-test.js @@ -4,7 +4,7 @@ import hbs from 'htmlbars-inline-precompile'; function assertPosition(assert, context, expectedSide) { const $this = context.$(); const targetPosition = $this.position(); - const tooltipPosition = $this.find('.ember-tooltip').position(); + const tooltipPosition = $this.find('.ember-popover').position(); if (expectedSide === 'top') { assert.ok(targetPosition.top > tooltipPosition.top, @@ -27,7 +27,7 @@ moduleForComponent('popover-on-element', 'Integration | Option | side and keepIn test('Popover shows with showOn', function(assert) { - assert.expect(4); + // assert.expect(4); /* Test the positions without forcing the tooltip to stay in the window. */ diff --git a/tests/integration/components/popover/spacing-test.js b/tests/integration/components/popover/spacing-test.js index 514f30ab..4cfd8b39 100644 --- a/tests/integration/components/popover/spacing-test.js +++ b/tests/integration/components/popover/spacing-test.js @@ -4,7 +4,7 @@ import hbs from 'htmlbars-inline-precompile'; function assertSpacing(assert, context, expectedSpacing) { const $this = context.$(); const targetPosition = $this.position(); - const $tooltip = $this.find('.ember-tooltip'); + const $tooltip = $this.find('.ember-popover'); const tooltipPosition = $tooltip.position(); const offset = Math.floor(targetPosition.top - tooltipPosition.top); @@ -26,7 +26,7 @@ moduleForComponent('popover-on-element', 'Integration | Option | spacing', { test('It shows with showOn', function(assert) { - assert.expect(2); + // assert.expect(2); /* Check the default spacing */ diff --git a/tests/integration/components/popover/tooltip-is-visible-test.js b/tests/integration/components/popover/tooltip-is-visible-test.js index 08e55724..d9489dca 100644 --- a/tests/integration/components/popover/tooltip-is-visible-test.js +++ b/tests/integration/components/popover/tooltip-is-visible-test.js @@ -11,7 +11,7 @@ moduleForComponent('popover-on-element', 'Integration | Option | tooltipIsVisibl test('Popover toggles with tooltipIsVisible', function(assert) { - assert.expect(2); + // assert.expect(2); this.set('showTooltip', true); diff --git a/tests/integration/components/show-on-test.js b/tests/integration/components/show-on-test.js index b5f0631a..7f00a50a 100644 --- a/tests/integration/components/show-on-test.js +++ b/tests/integration/components/show-on-test.js @@ -11,7 +11,7 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | showOn', { test('It shows with showOn', function(assert) { - assert.expect(3); + // assert.expect(3); this.render(hbs`{{tooltip-on-component showOn='click'}}`); diff --git a/tests/integration/components/side-test.js b/tests/integration/components/side-test.js index a9f6bff9..44eb107f 100644 --- a/tests/integration/components/side-test.js +++ b/tests/integration/components/side-test.js @@ -27,7 +27,7 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | side and keep test('It shows with showOn', function(assert) { - assert.expect(4); + // assert.expect(4); /* Test the positions without forcing the tooltip to stay in the window. */ @@ -54,7 +54,7 @@ test('It shows with showOn', function(assert) { // test('It stays in the window', function(assert) { -// assert.expect(1); +// // assert.expect(1); // /* Test the position switches form left to right automatically. */ diff --git a/tests/integration/components/spacing-test.js b/tests/integration/components/spacing-test.js index 983d3621..ae059140 100644 --- a/tests/integration/components/spacing-test.js +++ b/tests/integration/components/spacing-test.js @@ -26,7 +26,7 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | spacing', { test('It shows with showOn', function(assert) { - assert.expect(2); + // assert.expect(2); /* Check the default spacing */ diff --git a/tests/integration/components/tooltip-is-visible-test.js b/tests/integration/components/tooltip-is-visible-test.js index 72d4a98b..56e72915 100644 --- a/tests/integration/components/tooltip-is-visible-test.js +++ b/tests/integration/components/tooltip-is-visible-test.js @@ -11,7 +11,7 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | tooltipIsVisi test('It toggles with tooltipIsVisible', function(assert) { - assert.expect(2); + // assert.expect(2); this.set('showTooltip', true); diff --git a/tests/integration/components/tooltip-on-element-test.js b/tests/integration/components/tooltip-on-element-test.js index 8775d555..80117712 100644 --- a/tests/integration/components/tooltip-on-element-test.js +++ b/tests/integration/components/tooltip-on-element-test.js @@ -27,7 +27,7 @@ test('It renders', function(assert) { }); test('it has the proper aria-describedby tag', function(assert) { - assert.expect(2); + // assert.expect(2); this.render(hbs`
From 451d6f81f7a5f65eae72eadd7b4c4d2a588c3407 Mon Sep 17 00:00:00 2001 From: a15n Date: Mon, 29 Aug 2016 14:09:37 -0700 Subject: [PATCH 07/38] handle the popover events correctly --- addon/components/popover-on-element.js | 128 ++++++++++++++----------- 1 file changed, 70 insertions(+), 58 deletions(-) diff --git a/addon/components/popover-on-element.js b/addon/components/popover-on-element.js index f9662d2f..10588d5b 100644 --- a/addon/components/popover-on-element.js +++ b/addon/components/popover-on-element.js @@ -1,77 +1,89 @@ import Ember from 'ember'; import TooltipAndPopoverComponent from 'ember-tooltips/components/tooltip-and-popover'; -const { $ } = Ember; +const { $, run } = Ember; export default TooltipAndPopoverComponent.extend({ - classNames: ['ember-popover'], - didInsertElement() { - this._super(...arguments); - - /* Setup event handling to hide and show the tooltip */ - - const $target = $(this.get('target')); - const event = this.get('event'); - - /* Setup event handling to hide and show the tooltip */ - - if (event !== 'none') { - const _hideOn = this.get('_hideOn'); - const _showOn = this.get('_showOn'); + hideDelay: '250', - /* If show and hide are the same (e.g. click), toggle - the visibility */ - - if (_showOn === _hideOn) { - $target.on(_showOn, () => { - this.toggle(); - }); - } else { - - /* Else, add the show and hide events individually */ - - if (_showOn !== 'none') { - $target.on(_showOn, () => { - this.show(); - }); - } + classNames: ['ember-popover'], + _isMouseInside: false, + _isMouseOutside: Ember.computed.not('_isMouseInside'), + _isMouseInsidePopover: false, /* see note below why this is necessary */ + didInsertElement() { + this._super(...arguments); + + const event = this.get('event'); + const $target = $(this.get('target')); + const $popover = $(this.get('_tether.element')); + + if (event === 'none') { + return; + } - if (_hideOn !== 'none') { - $target.on(_hideOn, () => { - this.hide(); - }); + const hideAfterDelayIfMouseIsOutside = () => { + run.later(() => { + if (this.get('_isMouseOutside')) { + this.hide(); } - } - - /* Hide and show the tooltip on focus and escape - for acessibility */ - - if (event !== 'focus') { - - /* If the event is click, we don't want the - click to also trigger focusin */ - - if (event !== 'click') { - $target.focusin(() => { - this.show(); - }); + }, +this.get('hideDelay')); + }; + + /* record _isMouseInside events */ + $target.on('mouseenter', () => { + this.set('_isMouseInside', true); + }); + $popover.on('mouseenter', () => { + this.set('_isMouseInsidePopover', true); + this.set('_isMouseInside', true); + }); + + /* record !_isMouseInside events */ + $target.on('mouseleave', () => { + this.set('_isMouseInside', false); + }); + $popover.on('mouseleave', () => { + this.set('_isMouseInsidePopover', false); + this.set('_isMouseInside', false); + }); + + const _showOn = this.get('_showOn'); + const _hideOn = this.get('_hideOn'); + + if (_showOn === 'mouseenter') { + /* handle the popover hover */ + + $target.on(_showOn, () => this.show()); + + $target.on(_hideOn, () => hideAfterDelayIfMouseIsOutside()); + + $popover.on(_hideOn, () => hideAfterDelayIfMouseIsOutside()); + + } else if (_showOn === 'click') { + /* handle the popover click */ + + $target.on(_showOn, () => { + /* $target.on('click'... ) gets called when the $popover is clicked + we need _isMouseInsidePopover to reject this event */ + if (this.get('_isMouseInsidePopover')) { + return; } - $target.focusout(() => { + if (this.get('tooltipIsVisible')) { this.hide(); - }); - } + } else { + this.show(); + } + }); - $target.keydown((keyEvent) => { - if (keyEvent.which === 27) { + $target.on('focusout', () => { + if (this.get('_isMouseOutside')) { this.hide(); - keyEvent.preventDefault(); - - return false; } }); + } - }, + }, }); From 53c71beebd452a261dcac5993046da5efc7f4278 Mon Sep 17 00:00:00 2001 From: a15n Date: Mon, 29 Aug 2016 14:28:26 -0700 Subject: [PATCH 08/38] fixed the CSS --- addon/styles/addon.scss | 64 ++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/addon/styles/addon.scss b/addon/styles/addon.scss index 1faf3f1d..6adbc28f 100644 --- a/addon/styles/addon.scss +++ b/addon/styles/addon.scss @@ -1,12 +1,15 @@ .tooltip-and-popover { - max-width: 200px; - color: #fff; - background: #3a3c47; - text-shadow: -1px -1px 0 rgba(0,0,0,0.2); -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; pointer-events: none; +} + +.ember-tooltip { + max-width: 200px; + color: #fff; + background: #3a3c47; + text-shadow: -1px -1px 0 rgba(0,0,0,0.2); font-size: 14px; padding: 6px 10px; border-radius: 3px; @@ -25,47 +28,43 @@ } } -.ember-popover { - -} +.ember-tooltip.tooltip-and-popover-target-attached-top:after, .ember-tooltip.tooltip-and-popover-target-attached-top-left:after, .ember-tooltip.tooltip-and-popover-target-attached-top-right:after { bottom: 0; } +.ember-tooltip.tooltip-and-popover-target-attached-bottom:after, .ember-tooltip.tooltip-and-popover-target-attached-bottom-left:after, .ember-tooltip.tooltip-and-popover-target-attached-bottom-right:after { top: 0; } +.ember-tooltip.tooltip-and-popover-target-attached-top:after, .ember-tooltip.tooltip-and-popover-target-attached-bottom:after { left: 50%; } +.ember-tooltip.tooltip-and-popover-target-attached-top-left:after, .ember-tooltip.tooltip-and-popover-target-attached-bottom-left:after { right: 15px; } +.ember-tooltip.tooltip-and-popover-target-attached-top-right:after, .ember-tooltip.tooltip-and-popover-target-attached-bottom-right:after { left: 15px; } -.ember-tooltip.ember-tooltip-target-attached-top:after, .ember-tooltip.ember-tooltip-target-attached-top-left:after, .ember-tooltip.ember-tooltip-target-attached-top-right:after { bottom: 0; } -.ember-tooltip.ember-tooltip-target-attached-bottom:after, .ember-tooltip.ember-tooltip-target-attached-bottom-left:after, .ember-tooltip.ember-tooltip-target-attached-bottom-right:after { top: 0; } -.ember-tooltip.ember-tooltip-target-attached-top:after, .ember-tooltip.ember-tooltip-target-attached-bottom:after { left: 50%; } -.ember-tooltip.ember-tooltip-target-attached-top-left:after, .ember-tooltip.ember-tooltip-target-attached-bottom-left:after { right: 15px; } -.ember-tooltip.ember-tooltip-target-attached-top-right:after, .ember-tooltip.ember-tooltip-target-attached-bottom-right:after { left: 15px; } - -.ember-tooltip.ember-tooltip-target-attached-left:after, .ember-tooltip.ember-tooltip-target-attached-left-top:after, .ember-tooltip.ember-tooltip-target-attached-left-bottom:after { right: 0; } -.ember-tooltip.ember-tooltip-target-attached-right:after, .ember-tooltip.ember-tooltip-target-attached-right-top:after, .ember-tooltip.ember-tooltip-target-attached-right-bottom:after { left: 0; } -.ember-tooltip.ember-tooltip-target-attached-left:after, .ember-tooltip.ember-tooltip-target-attached-right:after { top: 50%; } -.ember-tooltip.ember-tooltip-target-attached-left-top:after, .ember-tooltip.ember-tooltip-target-attached-right-top:after { bottom: 15px; } -.ember-tooltip.ember-tooltip-target-attached-left-bottom:after, .ember-tooltip.ember-tooltip-target-attached-right-bottom:after { top: 15px; } +.ember-tooltip.tooltip-and-popover-target-attached-left:after, .ember-tooltip.tooltip-and-popover-target-attached-left-top:after, .ember-tooltip.tooltip-and-popover-target-attached-left-bottom:after { right: 0; } +.ember-tooltip.tooltip-and-popover-target-attached-right:after, .ember-tooltip.tooltip-and-popover-target-attached-right-top:after, .ember-tooltip.tooltip-and-popover-target-attached-right-bottom:after { left: 0; } +.ember-tooltip.tooltip-and-popover-target-attached-left:after, .ember-tooltip.tooltip-and-popover-target-attached-right:after { top: 50%; } +.ember-tooltip.tooltip-and-popover-target-attached-left-top:after, .ember-tooltip.tooltip-and-popover-target-attached-right-top:after { bottom: 15px; } +.ember-tooltip.tooltip-and-popover-target-attached-left-bottom:after, .ember-tooltip.tooltip-and-popover-target-attached-right-bottom:after { top: 15px; } /* Fade */ .tooltip-and-popover { opacity: 0; - &.ember-tooltip-slide, - &.ember-tooltip-fade { - // -webkit-transition: opacity 200ms ease-out, margin 200ms ease-out; - // transition: opacity 200ms ease-out, margin 200ms ease-out; + &.tooltip-and-popover-slide, + &.tooltip-and-popover-fade { + -webkit-transition: opacity 200ms ease-out, margin 200ms ease-out; + transition: opacity 200ms ease-out, margin 200ms ease-out; } - &.ember-tooltip-slide { - &.ember-tooltip-target-attached-top { + &.tooltip-and-popover-slide { + &.tooltip-and-popover-target-attached-top { margin-top: 10px; } - &.ember-tooltip-target-attached-right { + &.tooltip-and-popover-target-attached-right { margin-left: -10px; } - &.ember-tooltip-target-attached-bottom { + &.tooltip-and-popover-target-attached-bottom { margin-top: -10px; } - &.ember-tooltip-target-attached-left { + &.tooltip-and-popover-target-attached-left { margin-left: 10px; } } @@ -73,10 +72,17 @@ &[aria-hidden="false"] { opacity: 1; // transition-duration: 100ms; // Shorter hide duration - // TODO(Andrew) revert this - &.ember-tooltip-slide { + &.tooltip-and-popover-slide { margin: 0px; } } } + +.ember-popover[aria-hidden="false"] { + pointer-events: initial; + cursor: initial; + -webkit-touch-callout: auto; + -webkit-user-select: auto; + user-select: auto; +} From 69a0c8c20baf4ff4706c57fac90f1c8115157c06 Mon Sep 17 00:00:00 2001 From: a15n Date: Mon, 29 Aug 2016 14:48:43 -0700 Subject: [PATCH 09/38] make popover-on-component and add popover-specific tests --- addon/components/popover-on-component.js | 20 ++++ addon/components/popover-on-element.js | 6 +- app/components/popover-on-component.js | 1 + tests/helpers/sync/assert-visibility.js | 4 - tests/integration/components/actions-test.js | 2 +- .../components/delay-on-change-test.js | 2 +- tests/integration/components/delay-test.js | 4 +- tests/integration/components/duration-test.js | 6 +- tests/integration/components/event-test.js | 8 +- tests/integration/components/hide-on-test.js | 2 +- .../components/popover/click-test.js | 70 ++++++++++++ .../components/popover/duration-test.js | 34 ------ .../components/popover/hover-test.js | 105 ++++++++++++++++++ .../components/popover/side-test.js | 51 --------- .../components/popover/spacing-test.js | 43 ------- .../components/popover/toggle-test.js | 92 +++++++++++++++ .../popover/tooltip-is-visible-test.js | 28 ----- tests/integration/components/show-on-test.js | 2 +- tests/integration/components/side-test.js | 4 +- tests/integration/components/spacing-test.js | 2 +- .../components/tooltip-is-visible-test.js | 2 +- .../components/tooltip-on-element-test.js | 2 +- 22 files changed, 309 insertions(+), 181 deletions(-) create mode 100644 addon/components/popover-on-component.js create mode 100644 app/components/popover-on-component.js create mode 100644 tests/integration/components/popover/click-test.js delete mode 100644 tests/integration/components/popover/duration-test.js create mode 100644 tests/integration/components/popover/hover-test.js delete mode 100644 tests/integration/components/popover/side-test.js delete mode 100644 tests/integration/components/popover/spacing-test.js create mode 100644 tests/integration/components/popover/toggle-test.js delete mode 100644 tests/integration/components/popover/tooltip-is-visible-test.js diff --git a/addon/components/popover-on-component.js b/addon/components/popover-on-component.js new file mode 100644 index 00000000..3e184e61 --- /dev/null +++ b/addon/components/popover-on-component.js @@ -0,0 +1,20 @@ +import Ember from 'ember'; +import PopoverOnElementComponent from 'ember-tooltips/components/popover-on-element'; + +const { computed } = Ember; + +export default PopoverOnElementComponent.extend({ + + target: computed(function() { + const parentView = this.get('parentView'); + + if (!parentView) { + console.warn('No parentView found'); + + return null; + } else { + return `#${parentView.get('elementId')}`; + } + }), + +}); diff --git a/addon/components/popover-on-element.js b/addon/components/popover-on-element.js index 10588d5b..4e35cdd5 100644 --- a/addon/components/popover-on-element.js +++ b/addon/components/popover-on-element.js @@ -22,7 +22,7 @@ export default TooltipAndPopoverComponent.extend({ return; } - const hideAfterDelayIfMouseIsOutside = () => { + const hideAfterDelayIfIsMouseOutside = () => { run.later(() => { if (this.get('_isMouseOutside')) { this.hide(); @@ -56,9 +56,9 @@ export default TooltipAndPopoverComponent.extend({ $target.on(_showOn, () => this.show()); - $target.on(_hideOn, () => hideAfterDelayIfMouseIsOutside()); + $target.on(_hideOn, () => hideAfterDelayIfIsMouseOutside()); - $popover.on(_hideOn, () => hideAfterDelayIfMouseIsOutside()); + $popover.on(_hideOn, () => hideAfterDelayIfIsMouseOutside()); } else if (_showOn === 'click') { /* handle the popover click */ diff --git a/app/components/popover-on-component.js b/app/components/popover-on-component.js new file mode 100644 index 00000000..8071254d --- /dev/null +++ b/app/components/popover-on-component.js @@ -0,0 +1 @@ +export { default } from 'ember-tooltips/components/popover-on-component'; diff --git a/tests/helpers/sync/assert-visibility.js b/tests/helpers/sync/assert-visibility.js index 05ec5bff..28c04d74 100644 --- a/tests/helpers/sync/assert-visibility.js +++ b/tests/helpers/sync/assert-visibility.js @@ -2,8 +2,6 @@ export function assertShow(assert, context) { assert.equal(context.$().find('.tooltip-and-popover').attr('aria-hidden'), 'false', 'Should show tooltip'); - assert.equal(context.$().find('.tooltip-and-popover').css('opacity'), '1', - 'Should show tooltip (opacity == 1)'); // TODO(Andrew) remove this, rely on aria } @@ -11,7 +9,5 @@ export function assertHide(assert, context) { assert.equal(context.$().find('.tooltip-and-popover').attr('aria-hidden'), 'true', 'Should hide tooltip'); - assert.equal(context.$().find('.tooltip-and-popover').css('opacity'), '0', - 'Should hide tooltip (opacity == 0)'); // TODO(Andrew) remove this, rely on aria } diff --git a/tests/integration/components/actions-test.js b/tests/integration/components/actions-test.js index 43c4331e..c9fa7250 100644 --- a/tests/integration/components/actions-test.js +++ b/tests/integration/components/actions-test.js @@ -16,7 +16,7 @@ test('It animates with delay passed as a number', function(assert) { 'onTooltipShow': 0, }; - // assert.expect(10); + assert.expect(10); /* Setup the actions and handlers... */ diff --git a/tests/integration/components/delay-on-change-test.js b/tests/integration/components/delay-on-change-test.js index f4a6289c..6cf4f2d0 100644 --- a/tests/integration/components/delay-on-change-test.js +++ b/tests/integration/components/delay-on-change-test.js @@ -11,7 +11,7 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | delayOnChange test('It animates with a delay', function(assert) { const done = assert.async(); - // assert.expect(2); + assert.expect(2); /* Create two tooltips and show one */ diff --git a/tests/integration/components/delay-test.js b/tests/integration/components/delay-test.js index b410f594..089882c6 100644 --- a/tests/integration/components/delay-test.js +++ b/tests/integration/components/delay-test.js @@ -12,7 +12,7 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | delay', { test('It animates with delay passed as a number', function(assert) { const done = assert.async(); - // assert.expect(4); + assert.expect(4); this.render(hbs`{{tooltip-on-component delay=300}}`); @@ -49,7 +49,7 @@ test('It animates with delay passed as a number', function(assert) { test('It animates with delay passed as a string', function(assert) { const done = assert.async(); - // assert.expect(4); + assert.expect(4); this.render(hbs`{{tooltip-on-component delay='300'}}`); diff --git a/tests/integration/components/duration-test.js b/tests/integration/components/duration-test.js index 66c585fe..b728cfc8 100644 --- a/tests/integration/components/duration-test.js +++ b/tests/integration/components/duration-test.js @@ -12,7 +12,7 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | duration', { test('It hides after the given duration', function(assert) { const done = assert.async(); - // assert.expect(3); + assert.expect(3); this.render(hbs`{{tooltip-on-component duration=300}}`); @@ -35,7 +35,7 @@ test('It hides after the given duration', function(assert) { test('It hides before the given duration, if requested', function(assert) { - // assert.expect(3); + assert.expect(3); this.render(hbs`{{tooltip-on-component duration=300}}`); @@ -58,7 +58,7 @@ test('It hides before the given duration, if requested', function(assert) { test('It uses duration after the first show', function(assert) { const done = assert.async(); - // assert.expect(5); + assert.expect(5); this.render(hbs`{{tooltip-on-component duration=300}}`); diff --git a/tests/integration/components/event-test.js b/tests/integration/components/event-test.js index 5a1b874f..03a5ba34 100644 --- a/tests/integration/components/event-test.js +++ b/tests/integration/components/event-test.js @@ -11,7 +11,7 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | event', { test('It toggles with hover', function(assert) { - // assert.expect(3); + assert.expect(3); this.render(hbs`{{tooltip-on-component}}`); @@ -33,7 +33,7 @@ test('It toggles with hover', function(assert) { test('It toggles with click', function(assert) { - // assert.expect(3); + assert.expect(3); this.render(hbs`{{tooltip-on-component event='click'}}`); @@ -55,7 +55,7 @@ test('It toggles with click', function(assert) { test('It toggles with focus', function(assert) { - // assert.expect(3); + assert.expect(3); this.render(hbs`{{tooltip-on-component event='focus'}}`); @@ -77,7 +77,7 @@ test('It toggles with focus', function(assert) { test('It does not show with none', function(assert) { - // assert.expect(4); + assert.expect(4); this.render(hbs`{{tooltip-on-component event='none'}}`); diff --git a/tests/integration/components/hide-on-test.js b/tests/integration/components/hide-on-test.js index c85bd299..f79db7de 100644 --- a/tests/integration/components/hide-on-test.js +++ b/tests/integration/components/hide-on-test.js @@ -11,7 +11,7 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | hideOn', { test('It hides with hideOn', function(assert) { - // assert.expect(3); + assert.expect(3); this.render(hbs`{{tooltip-on-component hideOn='click'}}`); diff --git a/tests/integration/components/popover/click-test.js b/tests/integration/components/popover/click-test.js new file mode 100644 index 00000000..157f4162 --- /dev/null +++ b/tests/integration/components/popover/click-test.js @@ -0,0 +1,70 @@ +import Ember from 'ember'; +import { moduleForComponent, test } from 'ember-qunit'; +import { assertHide, assertShow } from '../../../helpers/sync/assert-visibility'; +import hbs from 'htmlbars-inline-precompile'; + +const { run } = Ember; + +moduleForComponent('popover-on-element', 'Integration | Option | event', { + integration: true +}); + +test('Popover with click toggle', function(assert) { + + assert.expect(3); + + this.render(hbs`{{popover-on-element event="click"}}`); + + const $target = this.$(); + + assertHide(assert, this); + + run(() => { + $target.trigger('click'); + }); + + assertShow(assert, this); + + run(() => { + $target.trigger('click'); + }); + + assertHide(assert, this); + +}); + +test('Popover with click on popover and focusout', function(assert) { + + assert.expect(4); + + this.render(hbs` + {{popover-on-element event="click"}} + `); + + const $target = this.$(); + const $popover = $target.find('.ember-popover'); + + assertHide(assert, this); + + run(() => { + $target.trigger('click'); + }); + + assertShow(assert, this); + + run(() => { + $popover.trigger('mouseenter'); + $popover.trigger('click'); + }); + + assertShow(assert, this); + + run(() => { + /* mouseleave the popover and focusout */ + $popover.trigger('mouseleave'); + $popover.trigger('focusout'); + }); + + assertHide(assert, this); + +}); diff --git a/tests/integration/components/popover/duration-test.js b/tests/integration/components/popover/duration-test.js deleted file mode 100644 index 3233ad12..00000000 --- a/tests/integration/components/popover/duration-test.js +++ /dev/null @@ -1,34 +0,0 @@ -import Ember from 'ember'; -import { moduleForComponent, test } from 'ember-qunit'; -import { assertHide, assertShow } from '../../../helpers/sync/assert-visibility'; -import hbs from 'htmlbars-inline-precompile'; - -const { run } = Ember; - -moduleForComponent('popover-on-element', 'Integration | Option | duration', { - integration: true -}); - -test('Popover hides after the given duration', function(assert) { - const done = assert.async(); - - // assert.expect(3); - - this.render(hbs`{{popover-on-element duration=300}}`); - - assertHide(assert, this); - - /* Check the tooltip is hidden after the duration */ - - run(() => { - this.$().trigger('mouseover'); - }); - - assertShow(assert, this); - - run.later(() => { - assertHide(assert, this); - done(); - }, 320); - -}); diff --git a/tests/integration/components/popover/hover-test.js b/tests/integration/components/popover/hover-test.js new file mode 100644 index 00000000..075fb8df --- /dev/null +++ b/tests/integration/components/popover/hover-test.js @@ -0,0 +1,105 @@ +import Ember from 'ember'; +import { moduleForComponent, test } from 'ember-qunit'; +import { assertHide, assertShow } from '../../../helpers/sync/assert-visibility'; +import hbs from 'htmlbars-inline-precompile'; + +const { run } = Ember; + +moduleForComponent('popover-on-element', 'Integration | Option | event', { + integration: true +}); + +test('Popover with hover visible with long delay', function(assert) { + + const done = assert.async(); + const HIDE_DELAY = 500; + + this.render(hbs`{{popover-on-element hideDelay="500"}}`); + + const $target = this.$(); + + assertHide(assert, this); + + run(() => { + $target.trigger('mouseover'); + }); + + assertShow(assert, this); + + run(() => { + $target.trigger('mouseleave'); + }); + + run.later(() => { + assertShow(assert, this); + }, HIDE_DELAY - 100); + + run.later(() => { + assertHide(assert, this); + done(); + }, HIDE_DELAY + 100); + +}); + +test('Popover with hover shown with interaction', function(assert) { + /* + Timeline: the popover should only hide if neither elements + have been moused-over within the 250ms default hideDelay + 0 hidden + 0 target.mouseover + 0 shown + 0 target.mouseleave + 0 shown + 100 popover.mouseover + 100 shown + 350 shown + 400 popover.mouseleave + 400 shown + 750 hidden + */ + + const done = assert.async(); + + assert.expect(7); + + this.render(hbs`{{popover-on-element}}`); + + const $target = this.$(); + const $popover = $target.find('.ember-popover'); + + assertHide(assert, this); + + run(() => { + $target.trigger('mouseover'); + }); + + assertShow(assert, this); + + run(() => { + $target.trigger('mouseleave'); + }); + + assertShow(assert, this); + + run.later(() => { + $popover.trigger('mouseover'); + }, 100); + + assertShow(assert, this); + + run.later(() => { + assertShow(assert, this); + }, 350); + + run.later(() => { + $popover.trigger('mouseleave'); + }, 400); + + assertShow(assert, this); + + run.later(() => { + assertHide(assert, this); + done(); + }, 750); + +}); diff --git a/tests/integration/components/popover/side-test.js b/tests/integration/components/popover/side-test.js deleted file mode 100644 index e5831f51..00000000 --- a/tests/integration/components/popover/side-test.js +++ /dev/null @@ -1,51 +0,0 @@ -import { moduleForComponent, test } from 'ember-qunit'; -import hbs from 'htmlbars-inline-precompile'; - -function assertPosition(assert, context, expectedSide) { - const $this = context.$(); - const targetPosition = $this.position(); - const tooltipPosition = $this.find('.ember-popover').position(); - - if (expectedSide === 'top') { - assert.ok(targetPosition.top > tooltipPosition.top, - 'Popover should be above the target'); - } else if (expectedSide === 'right') { - assert.ok(targetPosition.left < tooltipPosition.left, - 'Popover should be right of the target'); - } else if (expectedSide === 'bottom') { - assert.ok(targetPosition.top < tooltipPosition.top, - 'Popover should be below the target'); - } else if (expectedSide === 'left') { - assert.ok(targetPosition.left > tooltipPosition.left, - 'Popover should be left of the target'); - } -} - -moduleForComponent('popover-on-element', 'Integration | Option | side and keepInWindow', { - integration: true -}); - -test('Popover shows with showOn', function(assert) { - - // assert.expect(4); - - /* Test the positions without forcing the tooltip - to stay in the window. */ - - this.render(hbs`{{popover-on-element side='top' keepInWindow=false}}`); - - assertPosition(assert, this, 'top'); - - this.render(hbs`{{popover-on-element side='right' keepInWindow=false}}`); - - assertPosition(assert, this, 'right'); - - this.render(hbs`{{popover-on-element side='bottom' keepInWindow=false}}`); - - assertPosition(assert, this, 'bottom'); - - this.render(hbs`{{popover-on-element side='left' keepInWindow=false}}`); - - assertPosition(assert, this, 'left'); - -}); diff --git a/tests/integration/components/popover/spacing-test.js b/tests/integration/components/popover/spacing-test.js deleted file mode 100644 index 4cfd8b39..00000000 --- a/tests/integration/components/popover/spacing-test.js +++ /dev/null @@ -1,43 +0,0 @@ -import { moduleForComponent, test } from 'ember-qunit'; -import hbs from 'htmlbars-inline-precompile'; - -function assertSpacing(assert, context, expectedSpacing) { - const $this = context.$(); - const targetPosition = $this.position(); - const $tooltip = $this.find('.ember-popover'); - const tooltipPosition = $tooltip.position(); - const offset = Math.floor(targetPosition.top - tooltipPosition.top); - - const paddingTop = parseInt($tooltip.css('padding-top')); - const paddingBottom = parseInt($tooltip.css('padding-bottom')); - const actualSpacing = offset - paddingTop - paddingBottom; - - /* Allow a small margin of error because of how browsers - render pixels */ - - assert.ok(expectedSpacing - 2 < actualSpacing && actualSpacing < expectedSpacing + 2, - `Popover should be ${expectedSpacing}px from the target`); - -} - -moduleForComponent('popover-on-element', 'Integration | Option | spacing', { - integration: true -}); - -test('It shows with showOn', function(assert) { - - // assert.expect(2); - - /* Check the default spacing */ - - this.render(hbs`{{popover-on-element}}`); - - assertSpacing(assert, this, 10); - - /* Check custom spacing */ - - this.render(hbs`{{popover-on-element spacing=20}}`); - - assertSpacing(assert, this, 20); - -}); diff --git a/tests/integration/components/popover/toggle-test.js b/tests/integration/components/popover/toggle-test.js new file mode 100644 index 00000000..367564ce --- /dev/null +++ b/tests/integration/components/popover/toggle-test.js @@ -0,0 +1,92 @@ +import Ember from 'ember'; +import { moduleForComponent, test } from 'ember-qunit'; +import { assertHide, assertShow } from '../../../helpers/sync/assert-visibility'; +import hbs from 'htmlbars-inline-precompile'; + +const { run } = Ember; + +moduleForComponent('popover-on-component', 'Integration | Option | event', { + integration: true +}); + +test('Popover toggles with hover', function(assert) { + + const done = assert.async(); + + assert.expect(3); + + this.render(hbs`{{popover-on-component}}`); + + assertHide(assert, this); + + run(() => { + this.$().trigger('mouseover'); + }); + + assertShow(assert, this); + + run(() => { + this.$().trigger('mouseleave'); + }); + + run.later(() => { + assertHide(assert, this); + done(); + }, 350); +}); + +test('Popover toggles with click', function(assert) { + + assert.expect(3); + + this.render(hbs`{{popover-on-component event='click'}}`); + + assertHide(assert, this); + + run(() => { + this.$().click(); + }); + + assertShow(assert, this); + + run(this, () => { + this.$().click(); + }); + + assertHide(assert, this); + +}); + +test('Popover does not show with none', function(assert) { + + assert.expect(4); + + this.render(hbs`{{popover-on-component event='none'}}`); + + assertHide(assert, this); + + /* Check focus */ + + run(() => { + this.$().trigger('focus'); + }); + + assertHide(assert, this); + + /* Check hover */ + + run(this, () => { + this.$().trigger('mouseover'); + }); + + assertHide(assert, this); + + /* Check click */ + + run(this, () => { + this.$().click(); + }); + + assertHide(assert, this); + +}); diff --git a/tests/integration/components/popover/tooltip-is-visible-test.js b/tests/integration/components/popover/tooltip-is-visible-test.js deleted file mode 100644 index d9489dca..00000000 --- a/tests/integration/components/popover/tooltip-is-visible-test.js +++ /dev/null @@ -1,28 +0,0 @@ -import Ember from 'ember'; -import { moduleForComponent, test } from 'ember-qunit'; -import { assertHide, assertShow } from '../../../helpers/sync/assert-visibility'; -import hbs from 'htmlbars-inline-precompile'; - -const { run } = Ember; - -moduleForComponent('popover-on-element', 'Integration | Option | tooltipIsVisible', { - integration: true -}); - -test('Popover toggles with tooltipIsVisible', function(assert) { - - // assert.expect(2); - - this.set('showTooltip', true); - - this.render(hbs`{{popover-on-element tooltipIsVisible=showTooltip}}`); - - assertShow(assert, this); - - run(() => { - this.set('showTooltip', false); - }); - - assertHide(assert, this); - -}); diff --git a/tests/integration/components/show-on-test.js b/tests/integration/components/show-on-test.js index 7f00a50a..b5f0631a 100644 --- a/tests/integration/components/show-on-test.js +++ b/tests/integration/components/show-on-test.js @@ -11,7 +11,7 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | showOn', { test('It shows with showOn', function(assert) { - // assert.expect(3); + assert.expect(3); this.render(hbs`{{tooltip-on-component showOn='click'}}`); diff --git a/tests/integration/components/side-test.js b/tests/integration/components/side-test.js index 44eb107f..a9f6bff9 100644 --- a/tests/integration/components/side-test.js +++ b/tests/integration/components/side-test.js @@ -27,7 +27,7 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | side and keep test('It shows with showOn', function(assert) { - // assert.expect(4); + assert.expect(4); /* Test the positions without forcing the tooltip to stay in the window. */ @@ -54,7 +54,7 @@ test('It shows with showOn', function(assert) { // test('It stays in the window', function(assert) { -// // assert.expect(1); +// assert.expect(1); // /* Test the position switches form left to right automatically. */ diff --git a/tests/integration/components/spacing-test.js b/tests/integration/components/spacing-test.js index ae059140..983d3621 100644 --- a/tests/integration/components/spacing-test.js +++ b/tests/integration/components/spacing-test.js @@ -26,7 +26,7 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | spacing', { test('It shows with showOn', function(assert) { - // assert.expect(2); + assert.expect(2); /* Check the default spacing */ diff --git a/tests/integration/components/tooltip-is-visible-test.js b/tests/integration/components/tooltip-is-visible-test.js index 56e72915..72d4a98b 100644 --- a/tests/integration/components/tooltip-is-visible-test.js +++ b/tests/integration/components/tooltip-is-visible-test.js @@ -11,7 +11,7 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | tooltipIsVisi test('It toggles with tooltipIsVisible', function(assert) { - // assert.expect(2); + assert.expect(2); this.set('showTooltip', true); diff --git a/tests/integration/components/tooltip-on-element-test.js b/tests/integration/components/tooltip-on-element-test.js index 80117712..8775d555 100644 --- a/tests/integration/components/tooltip-on-element-test.js +++ b/tests/integration/components/tooltip-on-element-test.js @@ -27,7 +27,7 @@ test('It renders', function(assert) { }); test('it has the proper aria-describedby tag', function(assert) { - // assert.expect(2); + assert.expect(2); this.render(hbs`
From 1b27d6a827e83d7c1d1434abedf0573ccde85b32 Mon Sep 17 00:00:00 2001 From: a15n Date: Mon, 29 Aug 2016 15:38:29 -0700 Subject: [PATCH 10/38] addressed feedback: no private API, shared -on-component util --- addon/components/popover-on-component.js | 16 ++-------------- addon/components/popover-on-element.js | 3 ++- addon/components/tooltip-on-component.js | 16 ++-------------- addon/utils.js | 15 +++++++++++++++ 4 files changed, 21 insertions(+), 29 deletions(-) create mode 100644 addon/utils.js diff --git a/addon/components/popover-on-component.js b/addon/components/popover-on-component.js index 3e184e61..8a0e8e55 100644 --- a/addon/components/popover-on-component.js +++ b/addon/components/popover-on-component.js @@ -1,20 +1,8 @@ -import Ember from 'ember'; import PopoverOnElementComponent from 'ember-tooltips/components/popover-on-element'; - -const { computed } = Ember; +import { onComponentTarget } from 'ember-tooltips/utils'; export default PopoverOnElementComponent.extend({ - target: computed(function() { - const parentView = this.get('parentView'); - - if (!parentView) { - console.warn('No parentView found'); - - return null; - } else { - return `#${parentView.get('elementId')}`; - } - }), + target: onComponentTarget, }); diff --git a/addon/components/popover-on-element.js b/addon/components/popover-on-element.js index 4e35cdd5..96fe0276 100644 --- a/addon/components/popover-on-element.js +++ b/addon/components/popover-on-element.js @@ -1,6 +1,7 @@ import Ember from 'ember'; import TooltipAndPopoverComponent from 'ember-tooltips/components/tooltip-and-popover'; + const { $, run } = Ember; export default TooltipAndPopoverComponent.extend({ @@ -16,7 +17,7 @@ export default TooltipAndPopoverComponent.extend({ const event = this.get('event'); const $target = $(this.get('target')); - const $popover = $(this.get('_tether.element')); + const $popover = this.$(); if (event === 'none') { return; diff --git a/addon/components/tooltip-on-component.js b/addon/components/tooltip-on-component.js index 27267dcf..101ac456 100644 --- a/addon/components/tooltip-on-component.js +++ b/addon/components/tooltip-on-component.js @@ -1,20 +1,8 @@ -import Ember from 'ember'; import TooltipOnElementComponent from 'ember-tooltips/components/tooltip-on-element'; - -const { computed } = Ember; +import { onComponentTarget } from 'ember-tooltips/utils'; export default TooltipOnElementComponent.extend({ - target: computed(function() { - const parentView = this.get('parentView'); - - if (!parentView) { - console.warn('No parentView found'); - - return null; - } else { - return `#${parentView.get('elementId')}`; - } - }), + target: onComponentTarget, }); diff --git a/addon/utils.js b/addon/utils.js new file mode 100644 index 00000000..81f72104 --- /dev/null +++ b/addon/utils.js @@ -0,0 +1,15 @@ +import Ember from 'ember'; + +const { computed } = Ember; + +export const onComponentTarget = computed(function() { + const parentView = this.get('parentView'); + + if (!parentView) { + console.warn('No parentView found'); + + return null; + } else { + return `#${parentView.get('elementId')}`; + } +}); From fa87fd4478b2b94c1e24a286c19151951a79dd8e Mon Sep 17 00:00:00 2001 From: a15n Date: Mon, 29 Aug 2016 15:56:11 -0700 Subject: [PATCH 11/38] first attempt at using (hash --- addon/components/popover-on-element.js | 8 +++++++- addon/templates/components/popover-on-element.hbs | 3 +++ package.json | 4 +++- 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 addon/templates/components/popover-on-element.hbs diff --git a/addon/components/popover-on-element.js b/addon/components/popover-on-element.js index 96fe0276..45987060 100644 --- a/addon/components/popover-on-element.js +++ b/addon/components/popover-on-element.js @@ -1,6 +1,6 @@ import Ember from 'ember'; import TooltipAndPopoverComponent from 'ember-tooltips/components/tooltip-and-popover'; - +import template from 'ember-tooltips/templates/components/popover-on-element'; const { $, run } = Ember; @@ -8,6 +8,7 @@ export default TooltipAndPopoverComponent.extend({ hideDelay: '250', + template, classNames: ['ember-popover'], _isMouseInside: false, _isMouseOutside: Ember.computed.not('_isMouseInside'), @@ -86,5 +87,10 @@ export default TooltipAndPopoverComponent.extend({ } }, + actions: { + hide() { + this.hide(); + } + } }); diff --git a/addon/templates/components/popover-on-element.hbs b/addon/templates/components/popover-on-element.hbs new file mode 100644 index 00000000..65c10403 --- /dev/null +++ b/addon/templates/components/popover-on-element.hbs @@ -0,0 +1,3 @@ +{{yield (hash + hide=(action "hide") +)}} diff --git a/package.json b/package.json index dd27f9f8..66ed834f 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,9 @@ ], "dependencies": { "ember-cli-babel": "^5.1.6", - "ember-cli-sass": "5.3.1" + "ember-cli-htmlbars": "^1.1.0", + "ember-cli-sass": "5.3.1", + "ember-hash-helper-polyfill": "^0.1.1" }, "peerDependencies": { "ember-tether": "0.3.1" From eec66118450d2ac440a0ea099144c3aa35e92935 Mon Sep 17 00:00:00 2001 From: a15n Date: Mon, 29 Aug 2016 16:20:26 -0700 Subject: [PATCH 12/38] second attempt: hash working, uses layout --- addon/components/popover-on-element.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addon/components/popover-on-element.js b/addon/components/popover-on-element.js index 45987060..38bb2733 100644 --- a/addon/components/popover-on-element.js +++ b/addon/components/popover-on-element.js @@ -1,6 +1,6 @@ import Ember from 'ember'; import TooltipAndPopoverComponent from 'ember-tooltips/components/tooltip-and-popover'; -import template from 'ember-tooltips/templates/components/popover-on-element'; +import layout from 'ember-tooltips/templates/components/popover-on-element'; const { $, run } = Ember; @@ -8,7 +8,7 @@ export default TooltipAndPopoverComponent.extend({ hideDelay: '250', - template, + layout, classNames: ['ember-popover'], _isMouseInside: false, _isMouseOutside: Ember.computed.not('_isMouseInside'), From 1b22b7c43cce67dc6c1d6d558861aca9565a93b7 Mon Sep 17 00:00:00 2001 From: a15n Date: Mon, 29 Aug 2016 16:21:01 -0700 Subject: [PATCH 13/38] clean up and fixing bugs --- addon/styles/addon.scss | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/addon/styles/addon.scss b/addon/styles/addon.scss index 6adbc28f..aa54fab4 100644 --- a/addon/styles/addon.scss +++ b/addon/styles/addon.scss @@ -28,52 +28,52 @@ } } -.ember-tooltip.tooltip-and-popover-target-attached-top:after, .ember-tooltip.tooltip-and-popover-target-attached-top-left:after, .ember-tooltip.tooltip-and-popover-target-attached-top-right:after { bottom: 0; } -.ember-tooltip.tooltip-and-popover-target-attached-bottom:after, .ember-tooltip.tooltip-and-popover-target-attached-bottom-left:after, .ember-tooltip.tooltip-and-popover-target-attached-bottom-right:after { top: 0; } -.ember-tooltip.tooltip-and-popover-target-attached-top:after, .ember-tooltip.tooltip-and-popover-target-attached-bottom:after { left: 50%; } -.ember-tooltip.tooltip-and-popover-target-attached-top-left:after, .ember-tooltip.tooltip-and-popover-target-attached-bottom-left:after { right: 15px; } -.ember-tooltip.tooltip-and-popover-target-attached-top-right:after, .ember-tooltip.tooltip-and-popover-target-attached-bottom-right:after { left: 15px; } +.ember-tooltip.ember-tooltip-target-attached-top:after, .ember-tooltip.ember-tooltip-target-attached-top-left:after, .ember-tooltip.ember-tooltip-target-attached-top-right:after { bottom: 0; } +.ember-tooltip.ember-tooltip-target-attached-bottom:after, .ember-tooltip.ember-tooltip-target-attached-bottom-left:after, .ember-tooltip.ember-tooltip-target-attached-bottom-right:after { top: 0; } +.ember-tooltip.ember-tooltip-target-attached-top:after, .ember-tooltip.ember-tooltip-target-attached-bottom:after { left: 50%; } +.ember-tooltip.ember-tooltip-target-attached-top-left:after, .ember-tooltip.ember-tooltip-target-attached-bottom-left:after { right: 15px; } +.ember-tooltip.ember-tooltip-target-attached-top-right:after, .ember-tooltip.ember-tooltip-target-attached-bottom-right:after { left: 15px; } -.ember-tooltip.tooltip-and-popover-target-attached-left:after, .ember-tooltip.tooltip-and-popover-target-attached-left-top:after, .ember-tooltip.tooltip-and-popover-target-attached-left-bottom:after { right: 0; } -.ember-tooltip.tooltip-and-popover-target-attached-right:after, .ember-tooltip.tooltip-and-popover-target-attached-right-top:after, .ember-tooltip.tooltip-and-popover-target-attached-right-bottom:after { left: 0; } -.ember-tooltip.tooltip-and-popover-target-attached-left:after, .ember-tooltip.tooltip-and-popover-target-attached-right:after { top: 50%; } -.ember-tooltip.tooltip-and-popover-target-attached-left-top:after, .ember-tooltip.tooltip-and-popover-target-attached-right-top:after { bottom: 15px; } -.ember-tooltip.tooltip-and-popover-target-attached-left-bottom:after, .ember-tooltip.tooltip-and-popover-target-attached-right-bottom:after { top: 15px; } +.ember-tooltip.ember-tooltip-target-attached-left:after, .ember-tooltip.ember-tooltip-target-attached-left-top:after, .ember-tooltip.ember-tooltip-target-attached-left-bottom:after { right: 0; } +.ember-tooltip.ember-tooltip-target-attached-right:after, .ember-tooltip.ember-tooltip-target-attached-right-top:after, .ember-tooltip.ember-tooltip-target-attached-right-bottom:after { left: 0; } +.ember-tooltip.ember-tooltip-target-attached-left:after, .ember-tooltip.ember-tooltip-target-attached-right:after { top: 50%; } +.ember-tooltip.ember-tooltip-target-attached-left-top:after, .ember-tooltip.ember-tooltip-target-attached-right-top:after { bottom: 15px; } +.ember-tooltip.ember-tooltip-target-attached-left-bottom:after, .ember-tooltip.ember-tooltip-target-attached-right-bottom:after { top: 15px; } /* Fade */ .tooltip-and-popover { opacity: 0; - &.tooltip-and-popover-slide, - &.tooltip-and-popover-fade { + &.ember-tooltip-slide, + &.ember-tooltip-fade { -webkit-transition: opacity 200ms ease-out, margin 200ms ease-out; transition: opacity 200ms ease-out, margin 200ms ease-out; } - &.tooltip-and-popover-slide { - &.tooltip-and-popover-target-attached-top { + &.ember-tooltip-slide { + &.ember-tooltip-target-attached-top { margin-top: 10px; } - &.tooltip-and-popover-target-attached-right { + &.ember-tooltip-target-attached-right { margin-left: -10px; } - &.tooltip-and-popover-target-attached-bottom { + &.ember-tooltip-target-attached-bottom { margin-top: -10px; } - &.tooltip-and-popover-target-attached-left { + &.ember-tooltip-target-attached-left { margin-left: 10px; } } &[aria-hidden="false"] { opacity: 1; - // transition-duration: 100ms; // Shorter hide duration + transition-duration: 100ms; // Shorter hide duration - &.tooltip-and-popover-slide { + &.ember-tooltip-slide { margin: 0px; } } From 9dc2fc76909c5560b9be16b24d97c4a2ce148cba Mon Sep 17 00:00:00 2001 From: a15n Date: Tue, 30 Aug 2016 10:34:36 -0700 Subject: [PATCH 14/38] use classPrefix instead of one-off strings --- addon/components/tooltip-and-popover.js | 16 +++++---- addon/styles/addon.scss | 36 +++++++++---------- .../components/tooltip-on-element-test.js | 2 +- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/addon/components/tooltip-and-popover.js b/addon/components/tooltip-and-popover.js index cc6b892f..f65d9f35 100644 --- a/addon/components/tooltip-and-popover.js +++ b/addon/components/tooltip-and-popover.js @@ -66,7 +66,7 @@ export default EmberTetherComponent.extend({ attributeBindings: ['aria-hidden', 'role', 'tabindex'], classNameBindings: ['effectClass'], - classPrefix: 'ember-tooltip', + classPrefix: 'tooltip-and-popover', classNames: ['tooltip-and-popover'], _didUpdateTimeoutLength: 1000, // 1000 ms or 0 ms, depending whether in test mode @@ -122,13 +122,14 @@ export default EmberTetherComponent.extend({ }), effectClass: computed(function() { - return `ember-tooltip-${this.get('effect')}`; + return `${this.get('classPrefix')}-${this.get('effect')}`; }), positionClass: computed(function() { const targetAttachment = this.get('targetAttachment'); + const classPrefix = this.get('classPrefix'); - return `ember-tooltip-${targetAttachment.replace(' ', ' ember-tooltip-')}`; + return `${classPrefix}-${targetAttachment.replace(' ', ` ${classPrefix}-`)}`; }), sideIsVertical: computed(function() { @@ -165,8 +166,9 @@ export default EmberTetherComponent.extend({ typeClass: computed(function() { const type = this.get('type'); + const classPrefix = this.get('classPrefix'); - return type ? `ember-tooltip-${type}` : null; + return type ? `${classPrefix}-${type}` : null; }), /* Private CPs */ @@ -246,8 +248,8 @@ export default EmberTetherComponent.extend({ let renderedSide; - ['top', 'right', 'bottom', 'left'].forEach(function(side) { - if ($_tether.hasClass(`ember-tooltip-target-attached-${side}`)) { + ['top', 'right', 'bottom', 'left'].forEach((side) => { + if ($_tether.hasClass(`${this.get('classPrefix')}-target-attached-${side}`)) { renderedSide = side; } }); @@ -346,7 +348,7 @@ export default EmberTetherComponent.extend({ already a tooltip visible in the DOM. Check that here and adjust the delay as needed. */ - let visibleTooltips = Ember.$('.ember-tooltip[aria-hidden="false"]').length; + let visibleTooltips = Ember.$(`.${this.get('classPrefix')}[aria-hidden="false"]`).length; if (visibleTooltips) { delay = 0; diff --git a/addon/styles/addon.scss b/addon/styles/addon.scss index aa54fab4..9d1b28e1 100644 --- a/addon/styles/addon.scss +++ b/addon/styles/addon.scss @@ -28,43 +28,43 @@ } } -.ember-tooltip.ember-tooltip-target-attached-top:after, .ember-tooltip.ember-tooltip-target-attached-top-left:after, .ember-tooltip.ember-tooltip-target-attached-top-right:after { bottom: 0; } -.ember-tooltip.ember-tooltip-target-attached-bottom:after, .ember-tooltip.ember-tooltip-target-attached-bottom-left:after, .ember-tooltip.ember-tooltip-target-attached-bottom-right:after { top: 0; } -.ember-tooltip.ember-tooltip-target-attached-top:after, .ember-tooltip.ember-tooltip-target-attached-bottom:after { left: 50%; } -.ember-tooltip.ember-tooltip-target-attached-top-left:after, .ember-tooltip.ember-tooltip-target-attached-bottom-left:after { right: 15px; } -.ember-tooltip.ember-tooltip-target-attached-top-right:after, .ember-tooltip.ember-tooltip-target-attached-bottom-right:after { left: 15px; } +.ember-tooltip.tooltip-and-popover-target-attached-top:after, .ember-tooltip.tooltip-and-popover-target-attached-top-left:after, .ember-tooltip.tooltip-and-popover-target-attached-top-right:after { bottom: 0; } +.ember-tooltip.tooltip-and-popover-target-attached-bottom:after, .ember-tooltip.tooltip-and-popover-target-attached-bottom-left:after, .ember-tooltip.tooltip-and-popover-target-attached-bottom-right:after { top: 0; } +.ember-tooltip.tooltip-and-popover-target-attached-top:after, .ember-tooltip.tooltip-and-popover-target-attached-bottom:after { left: 50%; } +.ember-tooltip.tooltip-and-popover-target-attached-top-left:after, .ember-tooltip.tooltip-and-popover-target-attached-bottom-left:after { right: 15px; } +.ember-tooltip.tooltip-and-popover-target-attached-top-right:after, .ember-tooltip.tooltip-and-popover-target-attached-bottom-right:after { left: 15px; } -.ember-tooltip.ember-tooltip-target-attached-left:after, .ember-tooltip.ember-tooltip-target-attached-left-top:after, .ember-tooltip.ember-tooltip-target-attached-left-bottom:after { right: 0; } -.ember-tooltip.ember-tooltip-target-attached-right:after, .ember-tooltip.ember-tooltip-target-attached-right-top:after, .ember-tooltip.ember-tooltip-target-attached-right-bottom:after { left: 0; } -.ember-tooltip.ember-tooltip-target-attached-left:after, .ember-tooltip.ember-tooltip-target-attached-right:after { top: 50%; } -.ember-tooltip.ember-tooltip-target-attached-left-top:after, .ember-tooltip.ember-tooltip-target-attached-right-top:after { bottom: 15px; } -.ember-tooltip.ember-tooltip-target-attached-left-bottom:after, .ember-tooltip.ember-tooltip-target-attached-right-bottom:after { top: 15px; } +.ember-tooltip.tooltip-and-popover-target-attached-left:after, .ember-tooltip.tooltip-and-popover-target-attached-left-top:after, .ember-tooltip.tooltip-and-popover-target-attached-left-bottom:after { right: 0; } +.ember-tooltip.tooltip-and-popover-target-attached-right:after, .ember-tooltip.tooltip-and-popover-target-attached-right-top:after, .ember-tooltip.tooltip-and-popover-target-attached-right-bottom:after { left: 0; } +.ember-tooltip.tooltip-and-popover-target-attached-left:after, .ember-tooltip.tooltip-and-popover-target-attached-right:after { top: 50%; } +.ember-tooltip.tooltip-and-popover-target-attached-left-top:after, .ember-tooltip.tooltip-and-popover-target-attached-right-top:after { bottom: 15px; } +.ember-tooltip.tooltip-and-popover-target-attached-left-bottom:after, .ember-tooltip.tooltip-and-popover-target-attached-right-bottom:after { top: 15px; } /* Fade */ .tooltip-and-popover { opacity: 0; - &.ember-tooltip-slide, - &.ember-tooltip-fade { + &.tooltip-and-popover-slide, + &.tooltip-and-popover-fade { -webkit-transition: opacity 200ms ease-out, margin 200ms ease-out; transition: opacity 200ms ease-out, margin 200ms ease-out; } - &.ember-tooltip-slide { - &.ember-tooltip-target-attached-top { + &.tooltip-and-popover-slide { + &.tooltip-and-popover-target-attached-top { margin-top: 10px; } - &.ember-tooltip-target-attached-right { + &.tooltip-and-popover-target-attached-right { margin-left: -10px; } - &.ember-tooltip-target-attached-bottom { + &.tooltip-and-popover-target-attached-bottom { margin-top: -10px; } - &.ember-tooltip-target-attached-left { + &.tooltip-and-popover-target-attached-left { margin-left: 10px; } } @@ -73,7 +73,7 @@ opacity: 1; transition-duration: 100ms; // Shorter hide duration - &.ember-tooltip-slide { + &.tooltip-and-popover-slide { margin: 0px; } } diff --git a/tests/integration/components/tooltip-on-element-test.js b/tests/integration/components/tooltip-on-element-test.js index 8775d555..1fc3eb1d 100644 --- a/tests/integration/components/tooltip-on-element-test.js +++ b/tests/integration/components/tooltip-on-element-test.js @@ -39,7 +39,7 @@ test('it has the proper aria-describedby tag', function(assert) {
`); - const $tooltipTarget = this.$('.ember-tooltip-target'); + const $tooltipTarget = this.$('.tooltip-and-popover-target'); const describedBy = $tooltipTarget.attr('aria-describedby'); assert.equal(this.$(`#${describedBy}`).text().trim(), 'Some info in a tooltip.'); From 4fc2c8e1802b26d85b7ef7e974720bb29d193296 Mon Sep 17 00:00:00 2001 From: a15n Date: Tue, 30 Aug 2016 17:59:02 -0700 Subject: [PATCH 15/38] better popover-on-element.didInsertElement code --- addon/components/popover-on-element.js | 73 +++++++---------- .../components/popover/click-test.js | 78 +++++++++++++++++-- 2 files changed, 102 insertions(+), 49 deletions(-) diff --git a/addon/components/popover-on-element.js b/addon/components/popover-on-element.js index 38bb2733..a11d6246 100644 --- a/addon/components/popover-on-element.js +++ b/addon/components/popover-on-element.js @@ -11,8 +11,6 @@ export default TooltipAndPopoverComponent.extend({ layout, classNames: ['ember-popover'], _isMouseInside: false, - _isMouseOutside: Ember.computed.not('_isMouseInside'), - _isMouseInsidePopover: false, /* see note below why this is necessary */ didInsertElement() { this._super(...arguments); @@ -24,73 +22,60 @@ export default TooltipAndPopoverComponent.extend({ return; } - const hideAfterDelayIfIsMouseOutside = () => { - run.later(() => { - if (this.get('_isMouseOutside')) { - this.hide(); - } - }, +this.get('hideDelay')); - }; - - /* record _isMouseInside events */ - $target.on('mouseenter', () => { - this.set('_isMouseInside', true); - }); - $popover.on('mouseenter', () => { - this.set('_isMouseInsidePopover', true); - this.set('_isMouseInside', true); - }); - - /* record !_isMouseInside events */ - $target.on('mouseleave', () => { - this.set('_isMouseInside', false); - }); - $popover.on('mouseleave', () => { - this.set('_isMouseInsidePopover', false); - this.set('_isMouseInside', false); - }); + // we must use mouseover, which correctly + // registers hover interactivity when spacing=0 + $target.add($popover).on('mouseover click', () => this.set('_isMouseInside', true)); + $target.add($popover).on('mouseout', () => this.set('_isMouseInside', false)); const _showOn = this.get('_showOn'); const _hideOn = this.get('_hideOn'); if (_showOn === 'mouseenter') { - /* handle the popover hover */ $target.on(_showOn, () => this.show()); - $target.on(_hideOn, () => hideAfterDelayIfIsMouseOutside()); + $target.on(_hideOn, () => this.hideAfterDelayIfIsMouseOutside()); - $popover.on(_hideOn, () => hideAfterDelayIfIsMouseOutside()); + $popover.on(_hideOn, () => this.hideAfterDelayIfIsMouseOutside()); } else if (_showOn === 'click') { - /* handle the popover click */ - $target.on(_showOn, () => { - /* $target.on('click'... ) gets called when the $popover is clicked - we need _isMouseInsidePopover to reject this event */ - if (this.get('_isMouseInsidePopover')) { + $target.on(_showOn, (event) => { + /* $target.on('click') is called when the $popover is clicked because the $popover + is contained within the $target. This will ignores those types of clicks. */ + if ($target[0] !== event.target) { return; } - if (this.get('tooltipIsVisible')) { - this.hide(); - } else { - this.show(); - } + this.toggle(); }); $target.on('focusout', () => { - if (this.get('_isMouseOutside')) { - this.hide(); - } + // this must be run.later(..., 1) because we must allow time + // for the all of the _isMouseInside events to be recorded + run.later(() => { + this.hideIfIsMouseOutside(); + }, 1); + }); } }, + hideIfIsMouseOutside() { + if (!this.get('_isMouseInside')) { + this.hide(); + } + }, + hideAfterDelayIfIsMouseOutside() { + run.later(() => { + this.hideIfIsMouseOutside(); + }, +this.get('hideDelay')); + }, actions: { hide() { this.hide(); + this.set('_isMouseInside', false); } - } + }, }); diff --git a/tests/integration/components/popover/click-test.js b/tests/integration/components/popover/click-test.js index 157f4162..faf07254 100644 --- a/tests/integration/components/popover/click-test.js +++ b/tests/integration/components/popover/click-test.js @@ -15,7 +15,30 @@ test('Popover with click toggle', function(assert) { this.render(hbs`{{popover-on-element event="click"}}`); + assertHide(assert, this); + + run(() => { + this.$().trigger('click'); + }); + + assertShow(assert, this); + + run(() => { + this.$().trigger('click'); + }); + + assertHide(assert, this); + +}); + +test('Popover: click target, click popover, click target', function(assert) { + + // assert.expect(4); + + this.render(hbs`{{popover-on-element event="click"}}`); + const $target = this.$(); + const $popover = $target.find('.ember-popover'); assertHide(assert, this); @@ -25,6 +48,12 @@ test('Popover with click toggle', function(assert) { assertShow(assert, this); + run(() => { + $popover.trigger('click'); + }); + + assertShow(assert, this); + run(() => { $target.trigger('click'); }); @@ -33,14 +62,49 @@ test('Popover with click toggle', function(assert) { }); -test('Popover with click on popover and focusout', function(assert) { +test('Popover: hover/click target, hover/click popover, hover/click target', function(assert) { + + // assert.expect(4); + + this.render(hbs`{{popover-on-element event="click"}}`); + + const $target = this.$(); + const $popover = $target.find('.ember-popover'); + + assertHide(assert, this); + + run(() => { + $target.trigger('mouseover'); + $target.trigger('click'); + }); + + assertShow(assert, this); + + run(() => { + $target.trigger('mouseout'); + $popover.trigger('mouseover'); + $popover.trigger('click'); + }); + + assertShow(assert, this); + + run(() => { + $popover.trigger('mouseout'); + $target.trigger('mouseover'); + $target.trigger('click'); + }); + + assertHide(assert, this); + +}); + +test('Popover: click target, click popover, click elsewhere', function(assert) { assert.expect(4); - this.render(hbs` - {{popover-on-element event="click"}} - `); + this.render(hbs`{{popover-on-element event="click"}}`); + const done = assert.async(); const $target = this.$(); const $popover = $target.find('.ember-popover'); @@ -65,6 +129,10 @@ test('Popover with click on popover and focusout', function(assert) { $popover.trigger('focusout'); }); - assertHide(assert, this); + run.later(() => { + assertHide(assert, this); + done(); + }, 50); + }); From cfc9ba81a07849ab0bd3ec53843c4fe193bd7251 Mon Sep 17 00:00:00 2001 From: a15n Date: Wed, 31 Aug 2016 07:37:29 -0700 Subject: [PATCH 16/38] use window.click to handle click outside events --- addon/components/popover-on-element.js | 35 ++++++++++++++------------ 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/addon/components/popover-on-element.js b/addon/components/popover-on-element.js index a11d6246..4be9e9d0 100644 --- a/addon/components/popover-on-element.js +++ b/addon/components/popover-on-element.js @@ -22,16 +22,16 @@ export default TooltipAndPopoverComponent.extend({ return; } - // we must use mouseover, which correctly - // registers hover interactivity when spacing=0 - $target.add($popover).on('mouseover click', () => this.set('_isMouseInside', true)); - $target.add($popover).on('mouseout', () => this.set('_isMouseInside', false)); - const _showOn = this.get('_showOn'); const _hideOn = this.get('_hideOn'); if (_showOn === 'mouseenter') { + // we must use mouseover, which correctly + // registers hover interactivity when spacing=0 + $target.add($popover).on('mouseover click', () => this.set('_isMouseInside', true)); + $target.add($popover).on('mouseout', () => this.set('_isMouseInside', false)); + $target.on(_showOn, () => this.show()); $target.on(_hideOn, () => this.hideAfterDelayIfIsMouseOutside()); @@ -40,25 +40,28 @@ export default TooltipAndPopoverComponent.extend({ } else if (_showOn === 'click') { - $target.on(_showOn, (event) => { + $(document).on('click', (event) => { + // this lightweight document.click handler is necessary to determine + // if a click is NOT on $target and NOT an ancestor of $target. + // If so then it must be a click elsewhere and should close the popover + // see... https://css-tricks.com/dangers-stopping-event-propagation/ + let $clickedElement = event.target; + if (this.get('tooltipIsVisible') && + $target[0] !== $clickedElement && + !$target.find($clickedElement).length) { + this.hide(); + } + }); + + $target.on('click', (event) => { /* $target.on('click') is called when the $popover is clicked because the $popover is contained within the $target. This will ignores those types of clicks. */ if ($target[0] !== event.target) { return; } - this.toggle(); }); - $target.on('focusout', () => { - // this must be run.later(..., 1) because we must allow time - // for the all of the _isMouseInside events to be recorded - run.later(() => { - this.hideIfIsMouseOutside(); - }, 1); - - }); - } }, hideIfIsMouseOutside() { From fedf79ca79f86df62c591c3b7137f6a4cbfef060 Mon Sep 17 00:00:00 2001 From: a15n Date: Wed, 31 Aug 2016 07:37:47 -0700 Subject: [PATCH 17/38] update tests --- .../components/popover/click-test.js | 66 +++++-------------- .../components/popover/none-test.js | 44 +++++++++++++ .../components/popover/toggle-test.js | 35 +--------- 3 files changed, 62 insertions(+), 83 deletions(-) create mode 100644 tests/integration/components/popover/none-test.js diff --git a/tests/integration/components/popover/click-test.js b/tests/integration/components/popover/click-test.js index faf07254..667b5069 100644 --- a/tests/integration/components/popover/click-test.js +++ b/tests/integration/components/popover/click-test.js @@ -9,36 +9,13 @@ moduleForComponent('popover-on-element', 'Integration | Option | event', { integration: true }); -test('Popover with click toggle', function(assert) { +test('Popover: click target toggle', function(assert) { assert.expect(3); this.render(hbs`{{popover-on-element event="click"}}`); - assertHide(assert, this); - - run(() => { - this.$().trigger('click'); - }); - - assertShow(assert, this); - - run(() => { - this.$().trigger('click'); - }); - - assertHide(assert, this); - -}); - -test('Popover: click target, click popover, click target', function(assert) { - - // assert.expect(4); - - this.render(hbs`{{popover-on-element event="click"}}`); - const $target = this.$(); - const $popover = $target.find('.ember-popover'); assertHide(assert, this); @@ -48,12 +25,6 @@ test('Popover: click target, click popover, click target', function(assert) { assertShow(assert, this); - run(() => { - $popover.trigger('click'); - }); - - assertShow(assert, this); - run(() => { $target.trigger('click'); }); @@ -62,9 +33,9 @@ test('Popover: click target, click popover, click target', function(assert) { }); -test('Popover: hover/click target, hover/click popover, hover/click target', function(assert) { +test('Popover: click target, click popover, click target', function(assert) { - // assert.expect(4); + assert.expect(4); this.render(hbs`{{popover-on-element event="click"}}`); @@ -74,23 +45,18 @@ test('Popover: hover/click target, hover/click popover, hover/click target', fun assertHide(assert, this); run(() => { - $target.trigger('mouseover'); $target.trigger('click'); }); assertShow(assert, this); run(() => { - $target.trigger('mouseout'); - $popover.trigger('mouseover'); $popover.trigger('click'); }); assertShow(assert, this); run(() => { - $popover.trigger('mouseout'); - $target.trigger('mouseover'); $target.trigger('click'); }); @@ -102,10 +68,17 @@ test('Popover: click target, click popover, click elsewhere', function(assert) { assert.expect(4); - this.render(hbs`{{popover-on-element event="click"}}`); - - const done = assert.async(); - const $target = this.$(); + this.render(hbs` +
+
+ click here for popover + {{popover-on-element event="click"}} +
+
+ `); + + const $elsewhere = this.$('.elsewhere'); + const $target = this.$('.target'); const $popover = $target.find('.ember-popover'); assertHide(assert, this); @@ -117,22 +90,15 @@ test('Popover: click target, click popover, click elsewhere', function(assert) { assertShow(assert, this); run(() => { - $popover.trigger('mouseenter'); $popover.trigger('click'); }); assertShow(assert, this); run(() => { - /* mouseleave the popover and focusout */ - $popover.trigger('mouseleave'); - $popover.trigger('focusout'); + $elsewhere.trigger('click'); }); - run.later(() => { - assertHide(assert, this); - done(); - }, 50); - + assertHide(assert, this); }); diff --git a/tests/integration/components/popover/none-test.js b/tests/integration/components/popover/none-test.js new file mode 100644 index 00000000..9d75a23a --- /dev/null +++ b/tests/integration/components/popover/none-test.js @@ -0,0 +1,44 @@ +import Ember from 'ember'; +import { moduleForComponent, test } from 'ember-qunit'; +import { assertHide } from '../../../helpers/sync/assert-visibility'; +import hbs from 'htmlbars-inline-precompile'; + +const { run } = Ember; + +moduleForComponent('popover-on-component', 'Integration | Option | event', { + integration: true +}); + +test('Popover does not show with none', function(assert) { + + assert.expect(4); + + this.render(hbs`{{popover-on-component event='none'}}`); + + assertHide(assert, this); + + /* Check focus */ + + run(() => { + this.$().trigger('focus'); + }); + + assertHide(assert, this); + + /* Check hover */ + + run(this, () => { + this.$().trigger('mouseover'); + }); + + assertHide(assert, this); + + /* Check click */ + + run(this, () => { + this.$().click(); + }); + + assertHide(assert, this); + +}); diff --git a/tests/integration/components/popover/toggle-test.js b/tests/integration/components/popover/toggle-test.js index 367564ce..efe00abd 100644 --- a/tests/integration/components/popover/toggle-test.js +++ b/tests/integration/components/popover/toggle-test.js @@ -57,36 +57,5 @@ test('Popover toggles with click', function(assert) { }); -test('Popover does not show with none', function(assert) { - - assert.expect(4); - - this.render(hbs`{{popover-on-component event='none'}}`); - - assertHide(assert, this); - - /* Check focus */ - - run(() => { - this.$().trigger('focus'); - }); - - assertHide(assert, this); - - /* Check hover */ - - run(this, () => { - this.$().trigger('mouseover'); - }); - - assertHide(assert, this); - - /* Check click */ - - run(this, () => { - this.$().click(); - }); - - assertHide(assert, this); - -}); +// TODO Popover toggles with focus +// TODO create a focus-test.js and focus interactivity From 4afd37e470423c2e3ea243433721723870cff4c6 Mon Sep 17 00:00:00 2001 From: a15n Date: Wed, 31 Aug 2016 10:32:27 -0700 Subject: [PATCH 18/38] unsubscribe from all events on willDestroyElement --- addon/components/popover-on-element.js | 49 ++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/addon/components/popover-on-element.js b/addon/components/popover-on-element.js index 4be9e9d0..0b797a37 100644 --- a/addon/components/popover-on-element.js +++ b/addon/components/popover-on-element.js @@ -15,18 +15,19 @@ export default TooltipAndPopoverComponent.extend({ this._super(...arguments); const event = this.get('event'); - const $target = $(this.get('target')); + const target = this.get('target'); + const $target = $(target); const $popover = this.$(); + const _showOn = this.get('_showOn'); if (event === 'none') { return; } - const _showOn = this.get('_showOn'); - const _hideOn = this.get('_hideOn'); - if (_showOn === 'mouseenter') { + const _hideOn = this.get('_hideOn'); + // we must use mouseover, which correctly // registers hover interactivity when spacing=0 $target.add($popover).on('mouseover click', () => this.set('_isMouseInside', true)); @@ -40,8 +41,8 @@ export default TooltipAndPopoverComponent.extend({ } else if (_showOn === 'click') { - $(document).on('click', (event) => { - // this lightweight document.click handler is necessary to determine + $(document).on(`click.${target}`, (event) => { + // this lightweight, namespaced click handler is necessary to determine // if a click is NOT on $target and NOT an ancestor of $target. // If so then it must be a click elsewhere and should close the popover // see... https://css-tricks.com/dangers-stopping-event-propagation/ @@ -64,6 +65,42 @@ export default TooltipAndPopoverComponent.extend({ } }, + willDestroyElement() { + this._super(...arguments); + + const event = this.get('event'); + const target = this.get('target'); + const $target = $(target); + const $popover = this.$(); + const _showOn = this.get('_showOn'); + + if (event === 'none') { + return; + } + + if (_showOn === 'mouseenter') { + + const _hideOn = this.get('_hideOn'); + + $target.add($popover).off('mouseover click'); + + $target.add($popover).off('mouseout'); + + $target.off(_showOn); + + $target.off(_hideOn); + + $popover.off(_hideOn); + + } else if (_showOn === 'click') { + + $(document).off(`click.${target}`); + + $target.off('click'); + + } + + }, hideIfIsMouseOutside() { if (!this.get('_isMouseInside')) { this.hide(); From 741ab038cd9590a70c1233a5c8b1d3b6c23d1aa1 Mon Sep 17 00:00:00 2001 From: a15n Date: Wed, 31 Aug 2016 11:11:39 -0700 Subject: [PATCH 19/38] unsubscribe from all events. simpler hover event delegation --- addon/components/popover-on-element.js | 57 +++++++------------------- 1 file changed, 15 insertions(+), 42 deletions(-) diff --git a/addon/components/popover-on-element.js b/addon/components/popover-on-element.js index 0b797a37..6af66c44 100644 --- a/addon/components/popover-on-element.js +++ b/addon/components/popover-on-element.js @@ -28,16 +28,22 @@ export default TooltipAndPopoverComponent.extend({ const _hideOn = this.get('_hideOn'); - // we must use mouseover, which correctly - // registers hover interactivity when spacing=0 - $target.add($popover).on('mouseover click', () => this.set('_isMouseInside', true)); + // we must use mouseover/mouseout because they correctly + // register hover interactivity when spacing='0' + $target.add($popover).on('mouseover', () => this.set('_isMouseInside', true)); $target.add($popover).on('mouseout', () => this.set('_isMouseInside', false)); + // _showOn == 'mouseenter' $target.on(_showOn, () => this.show()); - $target.on(_hideOn, () => this.hideAfterDelayIfIsMouseOutside()); - - $popover.on(_hideOn, () => this.hideAfterDelayIfIsMouseOutside()); + // _hideOn == 'mouseleave' + $target.add($popover).on(_hideOn, () => { + run.later(() => { + if (!this.get('_isMouseInside')) { + this.hide(); + } + }, +this.get('hideDelay')); + }); } else if (_showOn === 'click') { @@ -68,53 +74,20 @@ export default TooltipAndPopoverComponent.extend({ willDestroyElement() { this._super(...arguments); - const event = this.get('event'); const target = this.get('target'); const $target = $(target); const $popover = this.$(); const _showOn = this.get('_showOn'); + const _hideOn = this.get('_hideOn'); - if (event === 'none') { - return; - } - - if (_showOn === 'mouseenter') { - - const _hideOn = this.get('_hideOn'); - - $target.add($popover).off('mouseover click'); - - $target.add($popover).off('mouseout'); + $target.add($popover).off(`${_showOn} mouseover ${_hideOn} mouseout click`); - $target.off(_showOn); + $(document).off(`click.${target}`); - $target.off(_hideOn); - - $popover.off(_hideOn); - - } else if (_showOn === 'click') { - - $(document).off(`click.${target}`); - - $target.off('click'); - - } - - }, - hideIfIsMouseOutside() { - if (!this.get('_isMouseInside')) { - this.hide(); - } - }, - hideAfterDelayIfIsMouseOutside() { - run.later(() => { - this.hideIfIsMouseOutside(); - }, +this.get('hideDelay')); }, actions: { hide() { this.hide(); - this.set('_isMouseInside', false); } }, From ea8e119725409a46ad670d24640442a8d49e0787 Mon Sep 17 00:00:00 2001 From: a15n Date: Wed, 31 Aug 2016 12:05:44 -0700 Subject: [PATCH 20/38] focus event added --- addon/components/popover-on-element.js | 37 ++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/addon/components/popover-on-element.js b/addon/components/popover-on-element.js index 6af66c44..a0ed8eb2 100644 --- a/addon/components/popover-on-element.js +++ b/addon/components/popover-on-element.js @@ -24,7 +24,7 @@ export default TooltipAndPopoverComponent.extend({ return; } - if (_showOn === 'mouseenter') { + if (event === 'hover') { const _hideOn = this.get('_hideOn'); @@ -45,7 +45,7 @@ export default TooltipAndPopoverComponent.extend({ }, +this.get('hideDelay')); }); - } else if (_showOn === 'click') { + } else if (event === 'click') { $(document).on(`click.${target}`, (event) => { // this lightweight, namespaced click handler is necessary to determine @@ -61,15 +61,42 @@ export default TooltipAndPopoverComponent.extend({ }); $target.on('click', (event) => { - /* $target.on('click') is called when the $popover is clicked because the $popover - is contained within the $target. This will ignores those types of clicks. */ - if ($target[0] !== event.target) { + // $target.on('click') is called when the $popover is clicked because the $popover + // is contained within the $target. This will ignores those types of clicks. + // It will also ignore if the lastEventType was focus. + // This is necessary because focus occurs before click, thereby flickering the popover + if ($target[0] !== event.target || lastEventType === 'focus') { + lastEventType = event.type; return; } this.toggle(); }); } + + // always allow focus and blur to show/hide the popover + // for accessibility and mobile interaction + + let lastEventType; + + $target.on('focus', (event) => { + lastEventType = event.type; + this.show(); + }); + + // only hide() if the new focused element is outside of the popover + // this adds a blur listener to each descendant within $target + $target.on('blur', '*', () => { + + run.later(() => { + const isFocusedElementElsewhere = !$popover.find(':focus').length; + if (isFocusedElementElsewhere) { + this.hide(); + } + }, 1); + + }); + }, willDestroyElement() { this._super(...arguments); From 129181b1c6d22414c884dc9f8d3d65e64c6a4329 Mon Sep 17 00:00:00 2001 From: a15n Date: Wed, 31 Aug 2016 16:09:42 -0700 Subject: [PATCH 21/38] improve the hover/click interaction. improve tests. revert focus event (unstable) --- addon/components/popover-on-element.js | 40 +----- tests/helpers/sync/assert-visibility.js | 4 +- .../components/popover/api-test.js | 131 ++++++++++++++++++ .../components/popover/click-test.js | 34 ++++- .../components/popover/hover-test.js | 76 +++++++--- .../components/popover/none-test.js | 14 +- .../components/popover/toggle-test.js | 61 -------- 7 files changed, 234 insertions(+), 126 deletions(-) create mode 100644 tests/integration/components/popover/api-test.js delete mode 100644 tests/integration/components/popover/toggle-test.js diff --git a/addon/components/popover-on-element.js b/addon/components/popover-on-element.js index a0ed8eb2..9d675dc8 100644 --- a/addon/components/popover-on-element.js +++ b/addon/components/popover-on-element.js @@ -52,10 +52,12 @@ export default TooltipAndPopoverComponent.extend({ // if a click is NOT on $target and NOT an ancestor of $target. // If so then it must be a click elsewhere and should close the popover // see... https://css-tricks.com/dangers-stopping-event-propagation/ - let $clickedElement = event.target; - if (this.get('tooltipIsVisible') && - $target[0] !== $clickedElement && - !$target.find($clickedElement).length) { + const $clickedElement = event.target; + const isClickOutsideTarget = $target[0] !== $clickedElement; + const isClickOutsidePopover = !$target.find($clickedElement).length; + const tooltipIsVisible = this.get('tooltipIsVisible'); + + if (isClickOutsideTarget && isClickOutsidePopover && tooltipIsVisible) { this.hide(); } }); @@ -63,40 +65,12 @@ export default TooltipAndPopoverComponent.extend({ $target.on('click', (event) => { // $target.on('click') is called when the $popover is clicked because the $popover // is contained within the $target. This will ignores those types of clicks. - // It will also ignore if the lastEventType was focus. - // This is necessary because focus occurs before click, thereby flickering the popover - if ($target[0] !== event.target || lastEventType === 'focus') { - lastEventType = event.type; + if ($target[0] !== event.target) { return; } this.toggle(); }); - } - - // always allow focus and blur to show/hide the popover - // for accessibility and mobile interaction - - let lastEventType; - - $target.on('focus', (event) => { - lastEventType = event.type; - this.show(); - }); - - // only hide() if the new focused element is outside of the popover - // this adds a blur listener to each descendant within $target - $target.on('blur', '*', () => { - - run.later(() => { - const isFocusedElementElsewhere = !$popover.find(':focus').length; - if (isFocusedElementElsewhere) { - this.hide(); - } - }, 1); - - }); - }, willDestroyElement() { this._super(...arguments); diff --git a/tests/helpers/sync/assert-visibility.js b/tests/helpers/sync/assert-visibility.js index 28c04d74..d8432fe1 100644 --- a/tests/helpers/sync/assert-visibility.js +++ b/tests/helpers/sync/assert-visibility.js @@ -1,13 +1,13 @@ export function assertShow(assert, context) { assert.equal(context.$().find('.tooltip-and-popover').attr('aria-hidden'), 'false', - 'Should show tooltip'); + 'Should show tooltip or popover'); } export function assertHide(assert, context) { assert.equal(context.$().find('.tooltip-and-popover').attr('aria-hidden'), 'true', - 'Should hide tooltip'); + 'Should hide tooltip or popover'); } diff --git a/tests/integration/components/popover/api-test.js b/tests/integration/components/popover/api-test.js new file mode 100644 index 00000000..c744c00a --- /dev/null +++ b/tests/integration/components/popover/api-test.js @@ -0,0 +1,131 @@ +import Ember from 'ember'; +import { moduleForComponent, test } from 'ember-qunit'; +import { assertHide, assertShow } from '../../../helpers/sync/assert-visibility'; +import hbs from 'htmlbars-inline-precompile'; + +const { run } = Ember; + +moduleForComponent('popover-on-element', 'Integration | Option | event', { + integration: true +}); + +test('Popover: click target, click hideSpan', function(assert) { + + this.render(hbs` + {{#popover-on-element event="click" as |popover|}} + + {{/popover-on-element}} + `); + + const $target = this.$(); + const $hideSpan = $target.find('.hideSpan'); + + assertHide(assert, this); + + run(() => { + $target.trigger('click'); + }); + + assertShow(assert, this); + + run(() => { + $hideSpan.trigger('click'); + }); + + assertHide(assert, this); + + assert.expect(3); + +}); + +test('Popover: click target, click hideSpan, click target', function(assert) { + + this.render(hbs` + {{#popover-on-element event="click" as |popover|}} + + {{/popover-on-element}} + `); + + const done = assert.async(); + const $target = this.$(); + const $hideSpan = $target.find('.hideSpan'); + + assertHide(assert, this); + + run(() => { + $target.trigger('click'); + }); + + assertShow(assert, this); + + run(() => { + $hideSpan.trigger('click'); + $hideSpan.trigger('blur'); + // it is necessary to trigger a blur on the span + // because the span/popover is automatically hidden + }); + + assertHide(assert, this); + + run(() => { + $target.trigger('click'); + }); + + run.later(() => { + assertShow(assert, this); + done(); + }, 10); + + assert.expect(4); + +}); + +test('Popover: click target, click popover, click hideSpan, click target', function(assert) { + + this.render(hbs` + {{#popover-on-element event="click" as |popover|}} + + {{/popover-on-element}} + `); + + const done = assert.async(); + const $target = this.$(); + const $popover = $target.find('.ember-popover'); + const $hideSpan = $target.find('.hideSpan'); + + assertHide(assert, this); + + run(() => { + $target.trigger('click'); + }); + + assertShow(assert, this); + + run(() => { + $popover.trigger('click'); + }); + + assertShow(assert, this); + + run(() => { + $hideSpan.trigger('click'); + $hideSpan.trigger('blur'); + // it is necessary to trigger a blur on the span + // because the span/popover is automatically hidden + }); + + assertHide(assert, this); + + run(() => { + $target.trigger('click'); + }); + + run.later(() => { + assertShow(assert, this); + done(); + // we run.later to future proof against TBD blur accessibility events + }, 10); + + assert.expect(5); + +}); diff --git a/tests/integration/components/popover/click-test.js b/tests/integration/components/popover/click-test.js index 667b5069..bde128ef 100644 --- a/tests/integration/components/popover/click-test.js +++ b/tests/integration/components/popover/click-test.js @@ -9,7 +9,7 @@ moduleForComponent('popover-on-element', 'Integration | Option | event', { integration: true }); -test('Popover: click target toggle', function(assert) { +test('Popover: click target, click target', function(assert) { assert.expect(3); @@ -64,6 +64,38 @@ test('Popover: click target, click popover, click target', function(assert) { }); +test('Popover: click target, click elsewhere', function(assert) { + + assert.expect(3); + + this.render(hbs` +
+
+ click here for popover + {{popover-on-element event="click"}} +
+
+ `); + + const $elsewhere = this.$('.elsewhere'); + const $target = this.$('.target'); + + assertHide(assert, this); + + run(() => { + $target.trigger('click'); + }); + + assertShow(assert, this); + + run(() => { + $elsewhere.trigger('click'); + }); + + assertHide(assert, this); + +}); + test('Popover: click target, click popover, click elsewhere', function(assert) { assert.expect(4); diff --git a/tests/integration/components/popover/hover-test.js b/tests/integration/components/popover/hover-test.js index 075fb8df..0cccd911 100644 --- a/tests/integration/components/popover/hover-test.js +++ b/tests/integration/components/popover/hover-test.js @@ -9,14 +9,43 @@ moduleForComponent('popover-on-element', 'Integration | Option | event', { integration: true }); -test('Popover with hover visible with long delay', function(assert) { +test('Popover: hover target, hover elsewhere', function(assert) { + + this.render(hbs`{{popover-on-element event="hover" hideDelay="250"}}`); const done = assert.async(); - const HIDE_DELAY = 500; + const $target = this.$(); - this.render(hbs`{{popover-on-element hideDelay="500"}}`); + assertHide(assert, this); + run(() => { + $target.trigger('mouseover'); + }); + + assertShow(assert, this); + + run(() => { + $target.trigger('mouseleave'); + }); + + assertShow(assert, this); + + run.later(() => { + assertHide(assert, this); + done(); + }, 300); + + assert.expect(4); + +}); + +test('Popover: hover target, hover popover (too slow)', function(assert) { + + this.render(hbs`{{popover-on-element event="hover" hideDelay="250"}}`); + + const done = assert.async(); const $target = this.$(); + const $popover = $target.find('.ember-popover'); assertHide(assert, this); @@ -31,39 +60,38 @@ test('Popover with hover visible with long delay', function(assert) { }); run.later(() => { - assertShow(assert, this); - }, HIDE_DELAY - 100); + $popover.trigger('mouseover'); + // hideDelay is 250, it took the 'user' 300 to mouseover the popover + }, 300); run.later(() => { assertHide(assert, this); done(); - }, HIDE_DELAY + 100); + }, 300); + + assert.expect(3); }); -test('Popover with hover shown with interaction', function(assert) { +test('Popover: hover target, hover inbetween, hover popover, hover elsewhere', function(assert) { /* Timeline: the popover should only hide if neither elements - have been moused-over within the 250ms default hideDelay + have been moused-over within the 250ms hideDelay 0 hidden 0 target.mouseover 0 shown 0 target.mouseleave 0 shown 100 popover.mouseover - 100 shown - 350 shown - 400 popover.mouseleave + 200 shown + 300 popover.mouseleave 400 shown - 750 hidden + 1000 hidden */ - const done = assert.async(); - - assert.expect(7); - - this.render(hbs`{{popover-on-element}}`); + this.render(hbs`{{popover-on-element event="hover" hideDelay="250"}}`); + const done = assert.async(); const $target = this.$(); const $popover = $target.find('.ember-popover'); @@ -85,21 +113,23 @@ test('Popover with hover shown with interaction', function(assert) { $popover.trigger('mouseover'); }, 100); - assertShow(assert, this); - run.later(() => { assertShow(assert, this); - }, 350); + }, 200); run.later(() => { $popover.trigger('mouseleave'); - }, 400); + }, 300); - assertShow(assert, this); + run.later(() => { + assertShow(assert, this); + }, 400); run.later(() => { assertHide(assert, this); done(); - }, 750); + }, 1000); + + assert.expect(6); }); diff --git a/tests/integration/components/popover/none-test.js b/tests/integration/components/popover/none-test.js index 9d75a23a..51c2a1ed 100644 --- a/tests/integration/components/popover/none-test.js +++ b/tests/integration/components/popover/none-test.js @@ -9,18 +9,18 @@ moduleForComponent('popover-on-component', 'Integration | Option | event', { integration: true }); -test('Popover does not show with none', function(assert) { - - assert.expect(4); +test('Popover: never shows with none', function(assert) { this.render(hbs`{{popover-on-component event='none'}}`); + const $target = this.$(); + assertHide(assert, this); /* Check focus */ run(() => { - this.$().trigger('focus'); + $target.trigger('focus'); }); assertHide(assert, this); @@ -28,7 +28,7 @@ test('Popover does not show with none', function(assert) { /* Check hover */ run(this, () => { - this.$().trigger('mouseover'); + $target.trigger('mouseover'); }); assertHide(assert, this); @@ -36,9 +36,11 @@ test('Popover does not show with none', function(assert) { /* Check click */ run(this, () => { - this.$().click(); + $target.click(); }); assertHide(assert, this); + assert.expect(4); + }); diff --git a/tests/integration/components/popover/toggle-test.js b/tests/integration/components/popover/toggle-test.js deleted file mode 100644 index efe00abd..00000000 --- a/tests/integration/components/popover/toggle-test.js +++ /dev/null @@ -1,61 +0,0 @@ -import Ember from 'ember'; -import { moduleForComponent, test } from 'ember-qunit'; -import { assertHide, assertShow } from '../../../helpers/sync/assert-visibility'; -import hbs from 'htmlbars-inline-precompile'; - -const { run } = Ember; - -moduleForComponent('popover-on-component', 'Integration | Option | event', { - integration: true -}); - -test('Popover toggles with hover', function(assert) { - - const done = assert.async(); - - assert.expect(3); - - this.render(hbs`{{popover-on-component}}`); - - assertHide(assert, this); - - run(() => { - this.$().trigger('mouseover'); - }); - - assertShow(assert, this); - - run(() => { - this.$().trigger('mouseleave'); - }); - - run.later(() => { - assertHide(assert, this); - done(); - }, 350); -}); - -test('Popover toggles with click', function(assert) { - - assert.expect(3); - - this.render(hbs`{{popover-on-component event='click'}}`); - - assertHide(assert, this); - - run(() => { - this.$().click(); - }); - - assertShow(assert, this); - - run(this, () => { - this.$().click(); - }); - - assertHide(assert, this); - -}); - -// TODO Popover toggles with focus -// TODO create a focus-test.js and focus interactivity From 54e265d9ac71b402be205aabb5a45e825ae9d0ca Mon Sep 17 00:00:00 2001 From: a15n Date: Wed, 31 Aug 2016 19:42:13 -0700 Subject: [PATCH 22/38] improved tests --- tests/integration/components/popover/click-test.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/integration/components/popover/click-test.js b/tests/integration/components/popover/click-test.js index bde128ef..a50db0c9 100644 --- a/tests/integration/components/popover/click-test.js +++ b/tests/integration/components/popover/click-test.js @@ -77,8 +77,9 @@ test('Popover: click target, click elsewhere', function(assert) {
`); - const $elsewhere = this.$('.elsewhere'); + const done = assert.async(); const $target = this.$('.target'); + const $elsewhere = $('.elsewhere'); assertHide(assert, this); @@ -92,7 +93,11 @@ test('Popover: click target, click elsewhere', function(assert) { $elsewhere.trigger('click'); }); - assertHide(assert, this); + run.later(() => { + assertHide(assert, this); + done(); + }, 100); + }); @@ -132,5 +137,4 @@ test('Popover: click target, click popover, click elsewhere', function(assert) { }); assertHide(assert, this); - }); From 0121663d4cc4a1d6b15b58994017ce8ec7b65959 Mon Sep 17 00:00:00 2001 From: a15n Date: Thu, 1 Sep 2016 10:32:54 -0700 Subject: [PATCH 23/38] cleaned up --- addon/components/popover-on-element.js | 18 ++++---- .../components/popover/api-test.js | 42 ++++++++----------- .../components/popover/click-test.js | 26 +++++------- .../components/popover/hover-test.js | 8 ++-- 4 files changed, 40 insertions(+), 54 deletions(-) diff --git a/addon/components/popover-on-element.js b/addon/components/popover-on-element.js index 9d675dc8..27186b38 100644 --- a/addon/components/popover-on-element.js +++ b/addon/components/popover-on-element.js @@ -18,21 +18,16 @@ export default TooltipAndPopoverComponent.extend({ const target = this.get('target'); const $target = $(target); const $popover = this.$(); - const _showOn = this.get('_showOn'); if (event === 'none') { + return; - } - if (event === 'hover') { + } else if (event === 'hover') { + const _showOn = this.get('_showOn'); const _hideOn = this.get('_hideOn'); - // we must use mouseover/mouseout because they correctly - // register hover interactivity when spacing='0' - $target.add($popover).on('mouseover', () => this.set('_isMouseInside', true)); - $target.add($popover).on('mouseout', () => this.set('_isMouseInside', false)); - // _showOn == 'mouseenter' $target.on(_showOn, () => this.show()); @@ -45,10 +40,15 @@ export default TooltipAndPopoverComponent.extend({ }, +this.get('hideDelay')); }); + // we must use mouseover/mouseout because they correctly + // register hover interactivity when spacing='0' + $target.add($popover).on('mouseover', () => this.set('_isMouseInside', true)); + $target.add($popover).on('mouseout', () => this.set('_isMouseInside', false)); + } else if (event === 'click') { $(document).on(`click.${target}`, (event) => { - // this lightweight, namespaced click handler is necessary to determine + // this lightweight, name-spaced click handler is necessary to determine // if a click is NOT on $target and NOT an ancestor of $target. // If so then it must be a click elsewhere and should close the popover // see... https://css-tricks.com/dangers-stopping-event-propagation/ diff --git a/tests/integration/components/popover/api-test.js b/tests/integration/components/popover/api-test.js index c744c00a..05ec3f75 100644 --- a/tests/integration/components/popover/api-test.js +++ b/tests/integration/components/popover/api-test.js @@ -5,20 +5,20 @@ import hbs from 'htmlbars-inline-precompile'; const { run } = Ember; -moduleForComponent('popover-on-element', 'Integration | Option | event', { +moduleForComponent('popover-on-element', 'Integration | Option | API', { integration: true }); -test('Popover: click target, click hideSpan', function(assert) { +test('Popover: click target, click hideAction', function(assert) { this.render(hbs` {{#popover-on-element event="click" as |popover|}} - + {{/popover-on-element}} `); const $target = this.$(); - const $hideSpan = $target.find('.hideSpan'); + const $hideAction = $target.find('.hideAction'); assertHide(assert, this); @@ -29,7 +29,7 @@ test('Popover: click target, click hideSpan', function(assert) { assertShow(assert, this); run(() => { - $hideSpan.trigger('click'); + $hideAction.trigger('click'); }); assertHide(assert, this); @@ -38,17 +38,16 @@ test('Popover: click target, click hideSpan', function(assert) { }); -test('Popover: click target, click hideSpan, click target', function(assert) { +test('Popover: click target, click hideAction, click target', function(assert) { this.render(hbs` {{#popover-on-element event="click" as |popover|}} - + {{/popover-on-element}} `); - const done = assert.async(); const $target = this.$(); - const $hideSpan = $target.find('.hideSpan'); + const $hideAction = $target.find('.hideAction'); assertHide(assert, this); @@ -59,10 +58,7 @@ test('Popover: click target, click hideSpan, click target', function(assert) { assertShow(assert, this); run(() => { - $hideSpan.trigger('click'); - $hideSpan.trigger('blur'); - // it is necessary to trigger a blur on the span - // because the span/popover is automatically hidden + $hideAction.trigger('click'); }); assertHide(assert, this); @@ -71,27 +67,26 @@ test('Popover: click target, click hideSpan, click target', function(assert) { $target.trigger('click'); }); - run.later(() => { - assertShow(assert, this); - done(); - }, 10); + assertShow(assert, this); assert.expect(4); }); -test('Popover: click target, click popover, click hideSpan, click target', function(assert) { +test('Popover: click target, click popover, click hideAction, click target', function(assert) { + // we should keep the run.later and .blur to future proof + // against to-be-developed 'focus' accessibility events production this.render(hbs` {{#popover-on-element event="click" as |popover|}} - + {{/popover-on-element}} `); const done = assert.async(); const $target = this.$(); const $popover = $target.find('.ember-popover'); - const $hideSpan = $target.find('.hideSpan'); + const $hideAction = $target.find('.hideAction'); assertHide(assert, this); @@ -108,10 +103,8 @@ test('Popover: click target, click popover, click hideSpan, click target', funct assertShow(assert, this); run(() => { - $hideSpan.trigger('click'); - $hideSpan.trigger('blur'); - // it is necessary to trigger a blur on the span - // because the span/popover is automatically hidden + $hideAction.trigger('click'); + $hideAction.trigger('blur'); }); assertHide(assert, this); @@ -123,7 +116,6 @@ test('Popover: click target, click popover, click hideSpan, click target', funct run.later(() => { assertShow(assert, this); done(); - // we run.later to future proof against TBD blur accessibility events }, 10); assert.expect(5); diff --git a/tests/integration/components/popover/click-test.js b/tests/integration/components/popover/click-test.js index a50db0c9..6243a577 100644 --- a/tests/integration/components/popover/click-test.js +++ b/tests/integration/components/popover/click-test.js @@ -5,14 +5,12 @@ import hbs from 'htmlbars-inline-precompile'; const { run } = Ember; -moduleForComponent('popover-on-element', 'Integration | Option | event', { +moduleForComponent('popover-on-element', 'Integration | Option | click', { integration: true }); test('Popover: click target, click target', function(assert) { - assert.expect(3); - this.render(hbs`{{popover-on-element event="click"}}`); const $target = this.$(); @@ -31,12 +29,12 @@ test('Popover: click target, click target', function(assert) { assertHide(assert, this); + assert.expect(3); + }); test('Popover: click target, click popover, click target', function(assert) { - assert.expect(4); - this.render(hbs`{{popover-on-element event="click"}}`); const $target = this.$(); @@ -62,22 +60,20 @@ test('Popover: click target, click popover, click target', function(assert) { assertHide(assert, this); + assert.expect(4); + }); test('Popover: click target, click elsewhere', function(assert) { - assert.expect(3); - this.render(hbs`
- click here for popover {{popover-on-element event="click"}}
`); - const done = assert.async(); const $target = this.$('.target'); const $elsewhere = $('.elsewhere'); @@ -93,22 +89,17 @@ test('Popover: click target, click elsewhere', function(assert) { $elsewhere.trigger('click'); }); - run.later(() => { - assertHide(assert, this); - done(); - }, 100); + assertHide(assert, this); + assert.expect(3); }); test('Popover: click target, click popover, click elsewhere', function(assert) { - assert.expect(4); - this.render(hbs`
- click here for popover {{popover-on-element event="click"}}
@@ -137,4 +128,7 @@ test('Popover: click target, click popover, click elsewhere', function(assert) { }); assertHide(assert, this); + + assert.expect(4); + }); diff --git a/tests/integration/components/popover/hover-test.js b/tests/integration/components/popover/hover-test.js index 0cccd911..6686078e 100644 --- a/tests/integration/components/popover/hover-test.js +++ b/tests/integration/components/popover/hover-test.js @@ -5,7 +5,7 @@ import hbs from 'htmlbars-inline-precompile'; const { run } = Ember; -moduleForComponent('popover-on-element', 'Integration | Option | event', { +moduleForComponent('popover-on-element', 'Integration | Option | hover', { integration: true }); @@ -61,13 +61,13 @@ test('Popover: hover target, hover popover (too slow)', function(assert) { run.later(() => { $popover.trigger('mouseover'); - // hideDelay is 250, it took the 'user' 300 to mouseover the popover - }, 300); + // hideDelay is 250ms, it took the 'user' 500ms to mouseover the popover + }, 500); run.later(() => { assertHide(assert, this); done(); - }, 300); + }, 500); assert.expect(3); From 8653877b56ab3c5e8da6e1b6c7e23f607c15c33f Mon Sep 17 00:00:00 2001 From: a15n Date: Fri, 2 Sep 2016 12:19:39 -0700 Subject: [PATCH 24/38] add focus event handling, click tests become mousedown. add focus tests --- addon/components/popover-on-element.js | 28 +++- .../components/popover/api-test.js | 24 ++- .../components/popover/click-test.js | 18 ++- .../components/popover/focus-test.js | 152 ++++++++++++++++++ .../components/popover/none-test.js | 3 +- 5 files changed, 200 insertions(+), 25 deletions(-) create mode 100644 tests/integration/components/popover/focus-test.js diff --git a/addon/components/popover-on-element.js b/addon/components/popover-on-element.js index 27186b38..092bf485 100644 --- a/addon/components/popover-on-element.js +++ b/addon/components/popover-on-element.js @@ -62,15 +62,33 @@ export default TooltipAndPopoverComponent.extend({ } }); - $target.on('click', (event) => { - // $target.on('click') is called when the $popover is clicked because the $popover - // is contained within the $target. This will ignores those types of clicks. - if ($target[0] !== event.target) { + // we use mousedown because it occurs before the focus event + $target.on('mousedown', (event) => { + // $target.on('mousedown') is called when the $popover is + // clicked because the $popover is contained within the $target. + // This will ignores those types of clicks. + if ($popover[0] === event.target || $popover.find(event.target).length) { return; } this.toggle(); }); } + + $target.on('focus', () => this.show()); + + $popover.on('focusout', () => { + // use a run.later() to allow the 'focusout' event to finish handling + run.later(() => { + const focusedElement = document.activeElement; + const isFocusedElementTarget = focusedElement === $target[0]; + const isFocusedElementPopover = focusedElement === $popover[0]; + const isFocusedElementInPopover = !!$popover.find(focusedElement).length; + if (isFocusedElementTarget || isFocusedElementPopover || isFocusedElementInPopover) { + return; + } + this.hide(); + }); + }); }, willDestroyElement() { this._super(...arguments); @@ -81,7 +99,7 @@ export default TooltipAndPopoverComponent.extend({ const _showOn = this.get('_showOn'); const _hideOn = this.get('_hideOn'); - $target.add($popover).off(`${_showOn} mouseover ${_hideOn} mouseout click`); + $target.add($popover).off(`${_showOn} mouseover ${_hideOn} mouseout mousedown focus focusout`); $(document).off(`click.${target}`); diff --git a/tests/integration/components/popover/api-test.js b/tests/integration/components/popover/api-test.js index 05ec3f75..fc98d04c 100644 --- a/tests/integration/components/popover/api-test.js +++ b/tests/integration/components/popover/api-test.js @@ -23,7 +23,8 @@ test('Popover: click target, click hideAction', function(assert) { assertHide(assert, this); run(() => { - $target.trigger('click'); + $target.trigger('mousedown'); + $target.trigger('mouseup'); }); assertShow(assert, this); @@ -52,7 +53,8 @@ test('Popover: click target, click hideAction, click target', function(assert) { assertHide(assert, this); run(() => { - $target.trigger('click'); + $target.trigger('mousedown'); + $target.trigger('mouseup'); }); assertShow(assert, this); @@ -64,7 +66,8 @@ test('Popover: click target, click hideAction, click target', function(assert) { assertHide(assert, this); run(() => { - $target.trigger('click'); + $target.trigger('mousedown'); + $target.trigger('mouseup'); }); assertShow(assert, this); @@ -74,8 +77,6 @@ test('Popover: click target, click hideAction, click target', function(assert) { }); test('Popover: click target, click popover, click hideAction, click target', function(assert) { - // we should keep the run.later and .blur to future proof - // against to-be-developed 'focus' accessibility events production this.render(hbs` {{#popover-on-element event="click" as |popover|}} @@ -83,7 +84,6 @@ test('Popover: click target, click popover, click hideAction, click target', fun {{/popover-on-element}} `); - const done = assert.async(); const $target = this.$(); const $popover = $target.find('.ember-popover'); const $hideAction = $target.find('.hideAction'); @@ -91,7 +91,8 @@ test('Popover: click target, click popover, click hideAction, click target', fun assertHide(assert, this); run(() => { - $target.trigger('click'); + $target.trigger('mousedown'); + $target.trigger('mouseup'); }); assertShow(assert, this); @@ -104,19 +105,16 @@ test('Popover: click target, click popover, click hideAction, click target', fun run(() => { $hideAction.trigger('click'); - $hideAction.trigger('blur'); }); assertHide(assert, this); run(() => { - $target.trigger('click'); + $target.trigger('mousedown'); + $target.trigger('mouseup'); }); - run.later(() => { - assertShow(assert, this); - done(); - }, 10); + assertShow(assert, this); assert.expect(5); diff --git a/tests/integration/components/popover/click-test.js b/tests/integration/components/popover/click-test.js index 6243a577..151a3d14 100644 --- a/tests/integration/components/popover/click-test.js +++ b/tests/integration/components/popover/click-test.js @@ -18,13 +18,15 @@ test('Popover: click target, click target', function(assert) { assertHide(assert, this); run(() => { - $target.trigger('click'); + $target.trigger('mousedown'); + $target.trigger('mouseup'); }); assertShow(assert, this); run(() => { - $target.trigger('click'); + $target.trigger('mousedown'); + $target.trigger('mouseup'); }); assertHide(assert, this); @@ -43,7 +45,8 @@ test('Popover: click target, click popover, click target', function(assert) { assertHide(assert, this); run(() => { - $target.trigger('click'); + $target.trigger('mousedown'); + $target.trigger('mouseup'); }); assertShow(assert, this); @@ -55,7 +58,8 @@ test('Popover: click target, click popover, click target', function(assert) { assertShow(assert, this); run(() => { - $target.trigger('click'); + $target.trigger('mousedown'); + $target.trigger('mouseup'); }); assertHide(assert, this); @@ -80,7 +84,8 @@ test('Popover: click target, click elsewhere', function(assert) { assertHide(assert, this); run(() => { - $target.trigger('click'); + $target.trigger('mousedown'); + $target.trigger('mouseup'); }); assertShow(assert, this); @@ -112,7 +117,8 @@ test('Popover: click target, click popover, click elsewhere', function(assert) { assertHide(assert, this); run(() => { - $target.trigger('click'); + $target.trigger('mousedown'); + $target.trigger('mouseup'); }); assertShow(assert, this); diff --git a/tests/integration/components/popover/focus-test.js b/tests/integration/components/popover/focus-test.js new file mode 100644 index 00000000..b1165053 --- /dev/null +++ b/tests/integration/components/popover/focus-test.js @@ -0,0 +1,152 @@ +import Ember from 'ember'; +import { moduleForComponent, test } from 'ember-qunit'; +import { assertHide, assertShow } from '../../../helpers/sync/assert-visibility'; +import hbs from 'htmlbars-inline-precompile'; + +const { run } = Ember; + +moduleForComponent('popover-on-element', 'Integration | Option | hover', { + integration: true +}); + +test('Popover: target focus, popover focus, elsewhere focus', function(assert) { + + this.render(hbs` +
+ {{popover-on-element event="focus"}} +
+ + `); + + const done = assert.async(); + const $target = $('.target'); + const $popover = $target.find('.ember-popover'); + const $elsewhere = $('.elsewhere'); + + assertHide(assert, this); + + run(() => { + $target.trigger('focus'); + }); + + assertShow(assert, this); + + run(() => { + $popover.trigger('focus'); + }); + + assertShow(assert, this); + + run(() => { + $popover.trigger('focusout'); + $elsewhere.trigger('focus'); + }); + + run.later(() => { + assertHide(assert, this); + done(); + }, 10); + + assert.expect(4); + +}); + +test('Popover: target focus, targetInterior focus, popover focus, elsewhere focus', function(assert) { + + this.render(hbs` +
+ + {{popover-on-element event="focus"}} +
+ + `); + + const done = assert.async(); + const $target = $('.target'); + const $targetInterior = $target.find('.target-interior'); + const $popover = $target.find('.ember-popover'); + const $elsewhere = $('.elsewhere'); + + assertHide(assert, this); + + run(() => { + $target.trigger('focus'); + }); + + assertShow(assert, this); + + run(() => { + $targetInterior.trigger('focus'); + }); + + assertShow(assert, this); + + run(() => { + $popover.trigger('focus'); + }); + + assertShow(assert, this); + + run(() => { + $popover.trigger('focusout'); + $elsewhere.trigger('focus'); + }); + + run.later(() => { + assertHide(assert, this); + done(); + }, 10); + + assert.expect(5); + +}); + +test('Popover: target focus, popover focus, popoverInterior focus, elsewhere focus', function(assert) { + + this.render(hbs` +
+ {{#popover-on-element event="focus"}} + + {{/popover-on-element}} +
+ + `); + + const done = assert.async(); + const $target = $('.target'); + const $popover = $target.find('.ember-popover'); + const $popoverInterior = $target.find('.popover-interior'); + const $elsewhere = $('.elsewhere'); + + assertHide(assert, this); + + run(() => { + $target.trigger('focus'); + }); + + assertShow(assert, this); + + run(() => { + $popover.trigger('focus'); + }); + + assertShow(assert, this); + + run(() => { + $popoverInterior.trigger('focus'); + }); + + assertShow(assert, this); + + run(() => { + $elsewhere.trigger('focus'); + }); + + run.later(() => { + assertHide(assert, this); + done(); + }, 10); + + assert.expect(5); + +}); diff --git a/tests/integration/components/popover/none-test.js b/tests/integration/components/popover/none-test.js index 51c2a1ed..81ba96a1 100644 --- a/tests/integration/components/popover/none-test.js +++ b/tests/integration/components/popover/none-test.js @@ -36,7 +36,8 @@ test('Popover: never shows with none', function(assert) { /* Check click */ run(this, () => { - $target.click(); + $target.trigger('mousedown'); + $target.trigger('mouseup'); }); assertHide(assert, this); From f08c0b2c68542dd1dd33cd8e75b645768bbaced8 Mon Sep 17 00:00:00 2001 From: a15n Date: Fri, 2 Sep 2016 12:51:20 -0700 Subject: [PATCH 25/38] -> clickedElement --- addon/components/popover-on-element.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addon/components/popover-on-element.js b/addon/components/popover-on-element.js index 092bf485..f5674449 100644 --- a/addon/components/popover-on-element.js +++ b/addon/components/popover-on-element.js @@ -52,9 +52,9 @@ export default TooltipAndPopoverComponent.extend({ // if a click is NOT on $target and NOT an ancestor of $target. // If so then it must be a click elsewhere and should close the popover // see... https://css-tricks.com/dangers-stopping-event-propagation/ - const $clickedElement = event.target; - const isClickOutsideTarget = $target[0] !== $clickedElement; - const isClickOutsidePopover = !$target.find($clickedElement).length; + const clickedElement = event.target; + const isClickOutsideTarget = $target[0] !== clickedElement; + const isClickOutsidePopover = !$target.find(clickedElement).length; const tooltipIsVisible = this.get('tooltipIsVisible'); if (isClickOutsideTarget && isClickOutsidePopover && tooltipIsVisible) { From f6a4632b3d61be408ef305fa1623f1b19822c44b Mon Sep 17 00:00:00 2001 From: a15n Date: Fri, 2 Sep 2016 13:52:29 -0700 Subject: [PATCH 26/38] use only one this.render per test --- tests/integration/components/side-test.js | 26 ++++++++++++++++--- tests/integration/components/spacing-test.js | 10 +++++-- .../components/tooltip-on-component-test.js | 6 ++++- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/tests/integration/components/side-test.js b/tests/integration/components/side-test.js index a9f6bff9..c75c7b62 100644 --- a/tests/integration/components/side-test.js +++ b/tests/integration/components/side-test.js @@ -25,25 +25,43 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | side and keep integration: true }); -test('It shows with showOn', function(assert) { +/* Test the positions without forcing the tooltip +to stay in the window. */ - assert.expect(4); +test('It shows with showOn top', function(assert) { - /* Test the positions without forcing the tooltip - to stay in the window. */ + assert.expect(1); this.render(hbs`{{tooltip-on-component side='top' keepInWindow=false}}`); assertPosition(assert, this, 'top'); +}); + +test('It shows with showOn right', function(assert) { + + assert.expect(1); + this.render(hbs`{{tooltip-on-component side='right' keepInWindow=false}}`); assertPosition(assert, this, 'right'); +}); + +test('It shows with showOn bottom', function(assert) { + + assert.expect(1); + this.render(hbs`{{tooltip-on-component side='bottom' keepInWindow=false}}`); assertPosition(assert, this, 'bottom'); +}); + +test('It shows with showOn left', function(assert) { + + assert.expect(1); + this.render(hbs`{{tooltip-on-component side='left' keepInWindow=false}}`); assertPosition(assert, this, 'left'); diff --git a/tests/integration/components/spacing-test.js b/tests/integration/components/spacing-test.js index 983d3621..a30fcc72 100644 --- a/tests/integration/components/spacing-test.js +++ b/tests/integration/components/spacing-test.js @@ -24,9 +24,9 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | spacing', { integration: true }); -test('It shows with showOn', function(assert) { +test('It shows with showOn spacing=default', function(assert) { - assert.expect(2); + assert.expect(1); /* Check the default spacing */ @@ -34,6 +34,12 @@ test('It shows with showOn', function(assert) { assertSpacing(assert, this, 10); +}); + +test('It shows with showOn spacing=default', function(assert) { + + assert.expect(1); + /* Check custom spacing */ this.render(hbs`{{tooltip-on-component spacing=20}}`); diff --git a/tests/integration/components/tooltip-on-component-test.js b/tests/integration/components/tooltip-on-component-test.js index ba368273..b47729df 100644 --- a/tests/integration/components/tooltip-on-component-test.js +++ b/tests/integration/components/tooltip-on-component-test.js @@ -5,13 +5,17 @@ moduleForComponent('tooltip-on-component', 'Integration | Component | tooltip on integration: true }); -test('It renders', function(assert) { +test('It renders with no content', function(assert) { this.render(hbs`{{tooltip-on-component}}`); assert.equal(this.$().text().trim(), '', 'Should render with no content'); +}); + +test('It renders with content', function(assert) { + this.render(hbs` {{#tooltip-on-component}} template block text From 1e9cef210184256810ee37795292f45af1ffa956 Mon Sep 17 00:00:00 2001 From: a15n Date: Sat, 3 Sep 2016 09:48:40 -0700 Subject: [PATCH 27/38] re-structure some CSS. use ember-tooltip-or-popover class --- addon/components/tooltip-and-popover.js | 4 +- addon/styles/addon.scss | 94 +++++++++++++------ tests/helpers/sync/assert-visibility.js | 23 ++++- .../components/popover/api-test.js | 26 ++--- .../components/popover/click-test.js | 30 +++--- .../components/popover/focus-test.js | 30 +++--- .../components/popover/hover-test.js | 28 +++--- .../components/popover/none-test.js | 10 +- .../components/tooltip-on-element-test.js | 4 +- 9 files changed, 150 insertions(+), 99 deletions(-) diff --git a/addon/components/tooltip-and-popover.js b/addon/components/tooltip-and-popover.js index f65d9f35..992bfcf6 100644 --- a/addon/components/tooltip-and-popover.js +++ b/addon/components/tooltip-and-popover.js @@ -66,8 +66,8 @@ export default EmberTetherComponent.extend({ attributeBindings: ['aria-hidden', 'role', 'tabindex'], classNameBindings: ['effectClass'], - classPrefix: 'tooltip-and-popover', - classNames: ['tooltip-and-popover'], + classPrefix: 'ember-tooltip-or-popover', + classNames: ['ember-tooltip-or-popover'], _didUpdateTimeoutLength: 1000, // 1000 ms or 0 ms, depending whether in test mode _hideTimer: null, diff --git a/addon/styles/addon.scss b/addon/styles/addon.scss index 9d1b28e1..9ea30c0c 100644 --- a/addon/styles/addon.scss +++ b/addon/styles/addon.scss @@ -1,10 +1,19 @@ -.tooltip-and-popover { +.ember-tooltip, +.ember-popover { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; pointer-events: none; } +.ember-popover[aria-hidden="false"] { + pointer-events: initial; + cursor: initial; + -webkit-touch-callout: auto; + -webkit-user-select: auto; + user-select: auto; +} + .ember-tooltip { max-width: 200px; color: #fff; @@ -26,45 +35,80 @@ -ms-transform: rotate(45deg); transform: rotate(45deg); } -} -.ember-tooltip.tooltip-and-popover-target-attached-top:after, .ember-tooltip.tooltip-and-popover-target-attached-top-left:after, .ember-tooltip.tooltip-and-popover-target-attached-top-right:after { bottom: 0; } -.ember-tooltip.tooltip-and-popover-target-attached-bottom:after, .ember-tooltip.tooltip-and-popover-target-attached-bottom-left:after, .ember-tooltip.tooltip-and-popover-target-attached-bottom-right:after { top: 0; } -.ember-tooltip.tooltip-and-popover-target-attached-top:after, .ember-tooltip.tooltip-and-popover-target-attached-bottom:after { left: 50%; } -.ember-tooltip.tooltip-and-popover-target-attached-top-left:after, .ember-tooltip.tooltip-and-popover-target-attached-bottom-left:after { right: 15px; } -.ember-tooltip.tooltip-and-popover-target-attached-top-right:after, .ember-tooltip.tooltip-and-popover-target-attached-bottom-right:after { left: 15px; } + &.ember-tooltip-or-popover-target-attached-top:after, + &.ember-tooltip-or-popover-target-attached-top-left:after, + &.ember-tooltip-or-popover-target-attached-top-right:after { + bottom: 0; + } + &.ember-tooltip-or-popover-target-attached-bottom:after, + &.ember-tooltip-or-popover-target-attached-bottom-left:after, + &.ember-tooltip-or-popover-target-attached-bottom-right:after { + top: 0; + } + &.ember-tooltip-or-popover-target-attached-top:after, + &.ember-tooltip-or-popover-target-attached-bottom:after { + left: 50%; + } + &.ember-tooltip-or-popover-target-attached-top-left:after, + &.ember-tooltip-or-popover-target-attached-bottom-left:after { + right: 15px; + } + &.ember-tooltip-or-popover-target-attached-top-right:after, + &.ember-tooltip-or-popover-target-attached-bottom-right:after { + left: 15px; + } -.ember-tooltip.tooltip-and-popover-target-attached-left:after, .ember-tooltip.tooltip-and-popover-target-attached-left-top:after, .ember-tooltip.tooltip-and-popover-target-attached-left-bottom:after { right: 0; } -.ember-tooltip.tooltip-and-popover-target-attached-right:after, .ember-tooltip.tooltip-and-popover-target-attached-right-top:after, .ember-tooltip.tooltip-and-popover-target-attached-right-bottom:after { left: 0; } -.ember-tooltip.tooltip-and-popover-target-attached-left:after, .ember-tooltip.tooltip-and-popover-target-attached-right:after { top: 50%; } -.ember-tooltip.tooltip-and-popover-target-attached-left-top:after, .ember-tooltip.tooltip-and-popover-target-attached-right-top:after { bottom: 15px; } -.ember-tooltip.tooltip-and-popover-target-attached-left-bottom:after, .ember-tooltip.tooltip-and-popover-target-attached-right-bottom:after { top: 15px; } + &.ember-tooltip-or-popover-target-attached-left:after, + &.ember-tooltip-or-popover-target-attached-left-top:after, + &.ember-tooltip-or-popover-target-attached-left-bottom:after { + right: 0; + } + &.ember-tooltip-or-popover-target-attached-right:after, + &.ember-tooltip-or-popover-target-attached-right-top:after, + &.ember-tooltip-or-popover-target-attached-right-bottom:after { + left: 0; + } + &.ember-tooltip-or-popover-target-attached-left:after, + &.ember-tooltip-or-popover-target-attached-right:after { + top: 50%; + } + &.ember-tooltip-or-popover-target-attached-left-top:after, + &.ember-tooltip-or-popover-target-attached-right-top:after { + bottom: 15px; + } + &.ember-tooltip-or-popover-target-attached-left-bottom:after, + &.ember-tooltip-or-popover-target-attached-right-bottom:after { + top: 15px; + } +} /* Fade */ -.tooltip-and-popover { +.ember-tooltip, +.ember-popover { opacity: 0; - &.tooltip-and-popover-slide, - &.tooltip-and-popover-fade { + &.ember-tooltip-or-popover-slide, + &.ember-tooltip-or-popover-fade { -webkit-transition: opacity 200ms ease-out, margin 200ms ease-out; transition: opacity 200ms ease-out, margin 200ms ease-out; } - &.tooltip-and-popover-slide { - &.tooltip-and-popover-target-attached-top { + &.ember-tooltip-or-popover-slide { + &.ember-tooltip-or-popover-target-attached-top { margin-top: 10px; } - &.tooltip-and-popover-target-attached-right { + &.ember-tooltip-or-popover-target-attached-right { margin-left: -10px; } - &.tooltip-and-popover-target-attached-bottom { + &.ember-tooltip-or-popover-target-attached-bottom { margin-top: -10px; } - &.tooltip-and-popover-target-attached-left { + &.ember-tooltip-or-popover-target-attached-left { margin-left: 10px; } } @@ -73,16 +117,8 @@ opacity: 1; transition-duration: 100ms; // Shorter hide duration - &.tooltip-and-popover-slide { + &.ember-tooltip-or-popover-slide { margin: 0px; } } } - -.ember-popover[aria-hidden="false"] { - pointer-events: initial; - cursor: initial; - -webkit-touch-callout: auto; - -webkit-user-select: auto; - user-select: auto; -} diff --git a/tests/helpers/sync/assert-visibility.js b/tests/helpers/sync/assert-visibility.js index d8432fe1..ceeff846 100644 --- a/tests/helpers/sync/assert-visibility.js +++ b/tests/helpers/sync/assert-visibility.js @@ -1,13 +1,28 @@ export function assertShow(assert, context) { - assert.equal(context.$().find('.tooltip-and-popover').attr('aria-hidden'), 'false', - 'Should show tooltip or popover'); + assert.equal(context.$().find('.ember-tooltip').attr('aria-hidden'), 'false', + 'Should show tooltip'); } export function assertHide(assert, context) { - assert.equal(context.$().find('.tooltip-and-popover').attr('aria-hidden'), 'true', - 'Should hide tooltip or popover'); + assert.equal(context.$().find('.ember-tooltip').attr('aria-hidden'), 'true', + 'Should hide tooltip'); + +} + + +export function assertPopoverShow(assert, context) { + + assert.equal(context.$().find('.ember-popover').attr('aria-hidden'), 'false', + 'Should show popover'); + +} + +export function assertPopoverHide(assert, context) { + + assert.equal(context.$().find('.ember-popover').attr('aria-hidden'), 'true', + 'Should hide popover'); } diff --git a/tests/integration/components/popover/api-test.js b/tests/integration/components/popover/api-test.js index fc98d04c..467b7436 100644 --- a/tests/integration/components/popover/api-test.js +++ b/tests/integration/components/popover/api-test.js @@ -1,6 +1,6 @@ import Ember from 'ember'; import { moduleForComponent, test } from 'ember-qunit'; -import { assertHide, assertShow } from '../../../helpers/sync/assert-visibility'; +import { assertPopoverHide, assertPopoverShow } from '../../../helpers/sync/assert-visibility'; import hbs from 'htmlbars-inline-precompile'; const { run } = Ember; @@ -20,20 +20,20 @@ test('Popover: click target, click hideAction', function(assert) { const $target = this.$(); const $hideAction = $target.find('.hideAction'); - assertHide(assert, this); + assertPopoverHide(assert, this); run(() => { $target.trigger('mousedown'); $target.trigger('mouseup'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run(() => { $hideAction.trigger('click'); }); - assertHide(assert, this); + assertPopoverHide(assert, this); assert.expect(3); @@ -50,27 +50,27 @@ test('Popover: click target, click hideAction, click target', function(assert) { const $target = this.$(); const $hideAction = $target.find('.hideAction'); - assertHide(assert, this); + assertPopoverHide(assert, this); run(() => { $target.trigger('mousedown'); $target.trigger('mouseup'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run(() => { $hideAction.trigger('click'); }); - assertHide(assert, this); + assertPopoverHide(assert, this); run(() => { $target.trigger('mousedown'); $target.trigger('mouseup'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); assert.expect(4); @@ -88,33 +88,33 @@ test('Popover: click target, click popover, click hideAction, click target', fun const $popover = $target.find('.ember-popover'); const $hideAction = $target.find('.hideAction'); - assertHide(assert, this); + assertPopoverHide(assert, this); run(() => { $target.trigger('mousedown'); $target.trigger('mouseup'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run(() => { $popover.trigger('click'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run(() => { $hideAction.trigger('click'); }); - assertHide(assert, this); + assertPopoverHide(assert, this); run(() => { $target.trigger('mousedown'); $target.trigger('mouseup'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); assert.expect(5); diff --git a/tests/integration/components/popover/click-test.js b/tests/integration/components/popover/click-test.js index 151a3d14..5449acf7 100644 --- a/tests/integration/components/popover/click-test.js +++ b/tests/integration/components/popover/click-test.js @@ -1,6 +1,6 @@ import Ember from 'ember'; import { moduleForComponent, test } from 'ember-qunit'; -import { assertHide, assertShow } from '../../../helpers/sync/assert-visibility'; +import { assertPopoverHide, assertPopoverShow } from '../../../helpers/sync/assert-visibility'; import hbs from 'htmlbars-inline-precompile'; const { run } = Ember; @@ -15,21 +15,21 @@ test('Popover: click target, click target', function(assert) { const $target = this.$(); - assertHide(assert, this); + assertPopoverHide(assert, this); run(() => { $target.trigger('mousedown'); $target.trigger('mouseup'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run(() => { $target.trigger('mousedown'); $target.trigger('mouseup'); }); - assertHide(assert, this); + assertPopoverHide(assert, this); assert.expect(3); @@ -42,27 +42,27 @@ test('Popover: click target, click popover, click target', function(assert) { const $target = this.$(); const $popover = $target.find('.ember-popover'); - assertHide(assert, this); + assertPopoverHide(assert, this); run(() => { $target.trigger('mousedown'); $target.trigger('mouseup'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run(() => { $popover.trigger('click'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run(() => { $target.trigger('mousedown'); $target.trigger('mouseup'); }); - assertHide(assert, this); + assertPopoverHide(assert, this); assert.expect(4); @@ -81,20 +81,20 @@ test('Popover: click target, click elsewhere', function(assert) { const $target = this.$('.target'); const $elsewhere = $('.elsewhere'); - assertHide(assert, this); + assertPopoverHide(assert, this); run(() => { $target.trigger('mousedown'); $target.trigger('mouseup'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run(() => { $elsewhere.trigger('click'); }); - assertHide(assert, this); + assertPopoverHide(assert, this); assert.expect(3); @@ -114,26 +114,26 @@ test('Popover: click target, click popover, click elsewhere', function(assert) { const $target = this.$('.target'); const $popover = $target.find('.ember-popover'); - assertHide(assert, this); + assertPopoverHide(assert, this); run(() => { $target.trigger('mousedown'); $target.trigger('mouseup'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run(() => { $popover.trigger('click'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run(() => { $elsewhere.trigger('click'); }); - assertHide(assert, this); + assertPopoverHide(assert, this); assert.expect(4); diff --git a/tests/integration/components/popover/focus-test.js b/tests/integration/components/popover/focus-test.js index b1165053..cb7bcaa3 100644 --- a/tests/integration/components/popover/focus-test.js +++ b/tests/integration/components/popover/focus-test.js @@ -1,6 +1,6 @@ import Ember from 'ember'; import { moduleForComponent, test } from 'ember-qunit'; -import { assertHide, assertShow } from '../../../helpers/sync/assert-visibility'; +import { assertPopoverHide, assertPopoverShow } from '../../../helpers/sync/assert-visibility'; import hbs from 'htmlbars-inline-precompile'; const { run } = Ember; @@ -23,19 +23,19 @@ test('Popover: target focus, popover focus, elsewhere focus', function(assert) { const $popover = $target.find('.ember-popover'); const $elsewhere = $('.elsewhere'); - assertHide(assert, this); + assertPopoverHide(assert, this); run(() => { $target.trigger('focus'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run(() => { $popover.trigger('focus'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run(() => { $popover.trigger('focusout'); @@ -43,7 +43,7 @@ test('Popover: target focus, popover focus, elsewhere focus', function(assert) { }); run.later(() => { - assertHide(assert, this); + assertPopoverHide(assert, this); done(); }, 10); @@ -67,25 +67,25 @@ test('Popover: target focus, targetInterior focus, popover focus, elsewhere focu const $popover = $target.find('.ember-popover'); const $elsewhere = $('.elsewhere'); - assertHide(assert, this); + assertPopoverHide(assert, this); run(() => { $target.trigger('focus'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run(() => { $targetInterior.trigger('focus'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run(() => { $popover.trigger('focus'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run(() => { $popover.trigger('focusout'); @@ -93,7 +93,7 @@ test('Popover: target focus, targetInterior focus, popover focus, elsewhere focu }); run.later(() => { - assertHide(assert, this); + assertPopoverHide(assert, this); done(); }, 10); @@ -118,32 +118,32 @@ test('Popover: target focus, popover focus, popoverInterior focus, elsewhere foc const $popoverInterior = $target.find('.popover-interior'); const $elsewhere = $('.elsewhere'); - assertHide(assert, this); + assertPopoverHide(assert, this); run(() => { $target.trigger('focus'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run(() => { $popover.trigger('focus'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run(() => { $popoverInterior.trigger('focus'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run(() => { $elsewhere.trigger('focus'); }); run.later(() => { - assertHide(assert, this); + assertPopoverHide(assert, this); done(); }, 10); diff --git a/tests/integration/components/popover/hover-test.js b/tests/integration/components/popover/hover-test.js index 6686078e..a18b1146 100644 --- a/tests/integration/components/popover/hover-test.js +++ b/tests/integration/components/popover/hover-test.js @@ -1,6 +1,6 @@ import Ember from 'ember'; import { moduleForComponent, test } from 'ember-qunit'; -import { assertHide, assertShow } from '../../../helpers/sync/assert-visibility'; +import { assertPopoverHide, assertPopoverShow } from '../../../helpers/sync/assert-visibility'; import hbs from 'htmlbars-inline-precompile'; const { run } = Ember; @@ -16,22 +16,22 @@ test('Popover: hover target, hover elsewhere', function(assert) { const done = assert.async(); const $target = this.$(); - assertHide(assert, this); + assertPopoverHide(assert, this); run(() => { $target.trigger('mouseover'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run(() => { $target.trigger('mouseleave'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run.later(() => { - assertHide(assert, this); + assertPopoverHide(assert, this); done(); }, 300); @@ -47,13 +47,13 @@ test('Popover: hover target, hover popover (too slow)', function(assert) { const $target = this.$(); const $popover = $target.find('.ember-popover'); - assertHide(assert, this); + assertPopoverHide(assert, this); run(() => { $target.trigger('mouseover'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run(() => { $target.trigger('mouseleave'); @@ -65,7 +65,7 @@ test('Popover: hover target, hover popover (too slow)', function(assert) { }, 500); run.later(() => { - assertHide(assert, this); + assertPopoverHide(assert, this); done(); }, 500); @@ -95,26 +95,26 @@ test('Popover: hover target, hover inbetween, hover popover, hover elsewhere', f const $target = this.$(); const $popover = $target.find('.ember-popover'); - assertHide(assert, this); + assertPopoverHide(assert, this); run(() => { $target.trigger('mouseover'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run(() => { $target.trigger('mouseleave'); }); - assertShow(assert, this); + assertPopoverShow(assert, this); run.later(() => { $popover.trigger('mouseover'); }, 100); run.later(() => { - assertShow(assert, this); + assertPopoverShow(assert, this); }, 200); run.later(() => { @@ -122,11 +122,11 @@ test('Popover: hover target, hover inbetween, hover popover, hover elsewhere', f }, 300); run.later(() => { - assertShow(assert, this); + assertPopoverShow(assert, this); }, 400); run.later(() => { - assertHide(assert, this); + assertPopoverHide(assert, this); done(); }, 1000); diff --git a/tests/integration/components/popover/none-test.js b/tests/integration/components/popover/none-test.js index 81ba96a1..174a230f 100644 --- a/tests/integration/components/popover/none-test.js +++ b/tests/integration/components/popover/none-test.js @@ -1,6 +1,6 @@ import Ember from 'ember'; import { moduleForComponent, test } from 'ember-qunit'; -import { assertHide } from '../../../helpers/sync/assert-visibility'; +import { assertPopoverHide } from '../../../helpers/sync/assert-visibility'; import hbs from 'htmlbars-inline-precompile'; const { run } = Ember; @@ -15,7 +15,7 @@ test('Popover: never shows with none', function(assert) { const $target = this.$(); - assertHide(assert, this); + assertPopoverHide(assert, this); /* Check focus */ @@ -23,7 +23,7 @@ test('Popover: never shows with none', function(assert) { $target.trigger('focus'); }); - assertHide(assert, this); + assertPopoverHide(assert, this); /* Check hover */ @@ -31,7 +31,7 @@ test('Popover: never shows with none', function(assert) { $target.trigger('mouseover'); }); - assertHide(assert, this); + assertPopoverHide(assert, this); /* Check click */ @@ -40,7 +40,7 @@ test('Popover: never shows with none', function(assert) { $target.trigger('mouseup'); }); - assertHide(assert, this); + assertPopoverHide(assert, this); assert.expect(4); diff --git a/tests/integration/components/tooltip-on-element-test.js b/tests/integration/components/tooltip-on-element-test.js index 1fc3eb1d..8ed24fec 100644 --- a/tests/integration/components/tooltip-on-element-test.js +++ b/tests/integration/components/tooltip-on-element-test.js @@ -30,7 +30,7 @@ test('it has the proper aria-describedby tag', function(assert) { assert.expect(2); this.render(hbs` -
+
Hover here! {{#tooltip-on-element}} @@ -39,7 +39,7 @@ test('it has the proper aria-describedby tag', function(assert) {
`); - const $tooltipTarget = this.$('.tooltip-and-popover-target'); + const $tooltipTarget = this.$('.target'); const describedBy = $tooltipTarget.attr('aria-describedby'); assert.equal(this.$(`#${describedBy}`).text().trim(), 'Some info in a tooltip.'); From 9daa4e55c069893b3ad1c0f1eb76e75d6caa6bb9 Mon Sep 17 00:00:00 2001 From: a15n Date: Sat, 3 Sep 2016 10:38:12 -0700 Subject: [PATCH 28/38] replace tooltipIsVisible with isShown --- README.md | 8 ++--- addon/components/popover-on-element.js | 4 +-- addon/components/tooltip-and-popover.js | 30 +++++++++---------- .../components/delay-on-change-test.js | 2 +- .../components/tooltip-is-visible-test.js | 6 ++-- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 6d7b9f88..c67a0f67 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ Options are set as attributes on the tooltip components. Current tooltip propert - [side](#side) - [showOn](#show-on) - [spacing](#spacing) -- [tooltipIsVisible](#tooltip-is-visible) +- [isShown](#is-shown) #### Class @@ -202,7 +202,7 @@ The event that the tooltip will hide and show for. Possible options are: This event is overwritten by the individual [`hideOn`](#hide-on) and [`showOn`](#show-on) properties. In effect, setting `event` sets `hideOn` and `shownOn` for you. -The tooltip can also be shown programatically by passing in the `tooltipIsVisible` property, [documented here](#tooltip-is-visible). +The tooltip can also be shown programatically by passing in the `isShown` property, [documented here](#is-shown). #### Hide on @@ -301,7 +301,7 @@ Sets the number of pixels the tooltip will render from the target element. A hig {{tooltip-on-component spacing=20}} ``` -#### Tooltip is visible +#### Tooltip is shown | Type | Boolean | |---------|---------| @@ -313,7 +313,7 @@ This can be useful alongside `event='none'` when you only want to toolip to show ```hbs {{!--Binds the tooltip visibility to the showTooltip property--}} -{{tooltip-on-component tooltipIsVisible=showTooltip}} +{{tooltip-on-component isShown=showTooltip}} ``` ### Setting Defaults diff --git a/addon/components/popover-on-element.js b/addon/components/popover-on-element.js index f5674449..fc14ef2a 100644 --- a/addon/components/popover-on-element.js +++ b/addon/components/popover-on-element.js @@ -55,9 +55,9 @@ export default TooltipAndPopoverComponent.extend({ const clickedElement = event.target; const isClickOutsideTarget = $target[0] !== clickedElement; const isClickOutsidePopover = !$target.find(clickedElement).length; - const tooltipIsVisible = this.get('tooltipIsVisible'); + const isShown = this.get('isShown'); - if (isClickOutsideTarget && isClickOutsidePopover && tooltipIsVisible) { + if (isClickOutsideTarget && isClickOutsidePopover && isShown) { this.hide(); } }); diff --git a/addon/components/tooltip-and-popover.js b/addon/components/tooltip-and-popover.js index 992bfcf6..4204d475 100644 --- a/addon/components/tooltip-and-popover.js +++ b/addon/components/tooltip-and-popover.js @@ -40,7 +40,7 @@ export default EmberTetherComponent.extend({ showOn: null, spacing: 10, tabindex: '0', // A positive integer (to enable) or -1 (to disable) - tooltipIsVisible: false, + isShown: false, keepInWindow: true, /* @@ -75,8 +75,8 @@ export default EmberTetherComponent.extend({ /* CPs */ - 'aria-hidden': computed('tooltipIsVisible', function() { - return this.get('tooltipIsVisible') ? 'false' : 'true'; + 'aria-hidden': computed('isShown', function() { + return this.get('isShown') ? 'false' : 'true'; }), attachment: computed(function() { @@ -218,7 +218,7 @@ export default EmberTetherComponent.extend({ run.cancel(this.get('_showTimer')); - this.set('tooltipIsVisible', false); + this.set('isShown', false); this.sendAction('onTooltipHide', this); }, @@ -297,16 +297,16 @@ export default EmberTetherComponent.extend({ }, /* - We use an observer so the user can set tooltipIsVisible + We use an observer so the user can set isShown as a attribute. @method setTimer */ - setTimer: Ember.observer('tooltipIsVisible', function() { - const tooltipIsVisible = this.get('tooltipIsVisible'); + setTimer: Ember.observer('isShown', function() { + const isShown = this.get('isShown'); - if (tooltipIsVisible) { + if (isShown) { const duration = cleanNumber(this.get('duration')); run.cancel(this.get('_hideTimer')); @@ -344,20 +344,20 @@ export default EmberTetherComponent.extend({ if (!this.get('delayOnChange')) { /* If the `delayOnChange` property is set to false, we - don't want to delay opening this tooltip if there is - already a tooltip visible in the DOM. Check that here + don't want to delay opening this tooltip/popover if there is + already a tooltip/popover shown in the DOM. Check that here and adjust the delay as needed. */ - let visibleTooltips = Ember.$(`.${this.get('classPrefix')}[aria-hidden="false"]`).length; + let shownTooltipsOrPopovers = Ember.$(`.${this.get('classPrefix')}[aria-hidden="false"]`).length; - if (visibleTooltips) { + if (shownTooltipsOrPopovers) { delay = 0; } } const _showTimer = run.later(this, () => { if (!this.get('destroying')) { - this.set('tooltipIsVisible', true); + this.set('isShown', true); } }, delay); @@ -366,7 +366,7 @@ export default EmberTetherComponent.extend({ /* If there is no delay, show the tooltop immediately */ - this.set('tooltipIsVisible', true); + this.set('isShown', true); } this.sendAction('onTooltipShow', this); @@ -378,7 +378,7 @@ export default EmberTetherComponent.extend({ logic for showing and hiding in the show() and hide() methods. */ - if (this.get('tooltipIsVisible')) { + if (this.get('isShown')) { this.hide(); } else { this.show(); diff --git a/tests/integration/components/delay-on-change-test.js b/tests/integration/components/delay-on-change-test.js index 6cf4f2d0..dfe5d6b1 100644 --- a/tests/integration/components/delay-on-change-test.js +++ b/tests/integration/components/delay-on-change-test.js @@ -17,7 +17,7 @@ test('It animates with a delay', function(assert) { this.render(hbs` {{tooltip-on-component delay=300 delayOnChange=false class='test-tooltip'}} - {{tooltip-on-component tooltipIsVisible=true delay=300 delayOnChange=false event='none'}} + {{tooltip-on-component isShown=true delay=300 delayOnChange=false event='none'}} `); assert.equal(this.$().find('.test-tooltip').attr('aria-hidden'), 'true', diff --git a/tests/integration/components/tooltip-is-visible-test.js b/tests/integration/components/tooltip-is-visible-test.js index 72d4a98b..6ddf04d6 100644 --- a/tests/integration/components/tooltip-is-visible-test.js +++ b/tests/integration/components/tooltip-is-visible-test.js @@ -5,17 +5,17 @@ import hbs from 'htmlbars-inline-precompile'; const { run } = Ember; -moduleForComponent('tooltip-on-component', 'Integration | Option | tooltipIsVisible', { +moduleForComponent('tooltip-on-component', 'Integration | Option | isShown', { integration: true }); -test('It toggles with tooltipIsVisible', function(assert) { +test('It toggles with isShown', function(assert) { assert.expect(2); this.set('showTooltip', true); - this.render(hbs`{{tooltip-on-component tooltipIsVisible=showTooltip}}`); + this.render(hbs`{{tooltip-on-component isShown=showTooltip}}`); assertShow(assert, this); From 26fcdd49fdf87f4d9df5c83b9f5f27bbeb5e394f Mon Sep 17 00:00:00 2001 From: a15n Date: Sat, 3 Sep 2016 10:45:58 -0700 Subject: [PATCH 29/38] replace tooltipCounterId with tooltipOrPopoverCounterId --- addon/components/tooltip-and-popover.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addon/components/tooltip-and-popover.js b/addon/components/tooltip-and-popover.js index 4204d475..33f443a1 100644 --- a/addon/components/tooltip-and-popover.js +++ b/addon/components/tooltip-and-popover.js @@ -5,7 +5,7 @@ const { $, computed, run } = Ember; const defaultPosition = 'center'; -let tooltipCounterId = 0; +let tooltipOrPopoverCounterId = 0; function cleanNumber(stringOrNumber) { let cleanNumber; @@ -144,9 +144,9 @@ export default EmberTetherComponent.extend({ let parentElementId = parentElement.attr('id'); if (!parentElementId) { - parentElementId = `target-for-tooltip-${tooltipCounterId}`; + parentElementId = `target-for-tooltip-or-popover-${tooltipOrPopoverCounterId}`; - tooltipCounterId++; + tooltipOrPopoverCounterId++; parentElement.attr('id', parentElementId); } From 4a7303d1bc87b662b1612aa39d9aaa4267e54672 Mon Sep 17 00:00:00 2001 From: a15n Date: Sat, 3 Sep 2016 10:52:11 -0700 Subject: [PATCH 30/38] replace onTooltip life actions with onShow/hide/etc --- README.md | 8 +++--- addon/components/tooltip-and-popover.js | 18 ++++++------ tests/integration/components/actions-test.js | 30 ++++++++++---------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index c67a0f67..43d13ad3 100644 --- a/README.md +++ b/README.md @@ -337,10 +337,10 @@ Four actions are available for you to hook onto through the tooltip lifecycle: ```hbs {{tooltip-on-component - onTooltipDestroy='onTooltipDestroy' - onTooltipHide='onTooltipHide' - onTooltipRender='onTooltipRender' - onTooltipShow='onTooltipShow' + onDestroy='onDestroy' + onHide='onHide' + onRender='onRender' + onShow='onShow' }} ``` diff --git a/addon/components/tooltip-and-popover.js b/addon/components/tooltip-and-popover.js index 33f443a1..2958c727 100644 --- a/addon/components/tooltip-and-popover.js +++ b/addon/components/tooltip-and-popover.js @@ -57,10 +57,10 @@ export default EmberTetherComponent.extend({ /* Actions */ - onTooltipDestroy: null, - onTooltipHide: null, - onTooltipRender: null, - onTooltipShow: null, + onDestroy: null, + onHide: null, + onRender: null, + onShow: null, /* Properties */ @@ -219,7 +219,7 @@ export default EmberTetherComponent.extend({ run.cancel(this.get('_showTimer')); this.set('isShown', false); - this.sendAction('onTooltipHide', this); + this.sendAction('onHide', this); }, didInsertElement() { @@ -235,7 +235,7 @@ export default EmberTetherComponent.extend({ const _tether = this.get('_tether'); const $_tether = $(_tether.element); - this.sendAction('onTooltipRender', this); + this.sendAction('onRender', this); $target.attr({ 'aria-describedby': `${this.get('elementId')}`, @@ -292,7 +292,7 @@ export default EmberTetherComponent.extend({ run.later(() => { this.positionTether(); - this.sendAction('onTooltipRender', this); + this.sendAction('onRender', this); }, this.get('_didUpdateTimeoutLength')); }, @@ -369,7 +369,7 @@ export default EmberTetherComponent.extend({ this.set('isShown', true); } - this.sendAction('onTooltipShow', this); + this.sendAction('onShow', this); }, toggle() { @@ -396,7 +396,7 @@ export default EmberTetherComponent.extend({ this._super(...arguments); // Removes tether - this.sendAction('onTooltipDestroy', this); + this.sendAction('onDestroy', this); }, }); diff --git a/tests/integration/components/actions-test.js b/tests/integration/components/actions-test.js index c9fa7250..102e4c46 100644 --- a/tests/integration/components/actions-test.js +++ b/tests/integration/components/actions-test.js @@ -8,12 +8,12 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | actions', { integration: true }); -test('It animates with delay passed as a number', function(assert) { +test('It calls lifecycle actions', function(assert) { const actionsCalledHash = { - 'onTooltipDestroy': 0, - 'onTooltipHide': 0, - 'onTooltipRender': 0, - 'onTooltipShow': 0, + onDestroy: 0, + onHide: 0, + onRender: 0, + onShow: 0, }; assert.expect(10); @@ -35,20 +35,20 @@ test('It animates with delay passed as a number', function(assert) { this.render(hbs` {{#unless destroyTooltip}} {{tooltip-on-component - onTooltipDestroy='onTooltipDestroy' - onTooltipHide='onTooltipHide' - onTooltipRender='onTooltipRender' - onTooltipShow='onTooltipShow' + onDestroy='onDestroy' + onHide='onHide' + onRender='onRender' + onShow='onShow' }} {{/unless}} `); /* Check render */ - assert.equal(actionsCalledHash.onTooltipRender, 1, + assert.equal(actionsCalledHash.onRender, 1, 'Should have called render'); - assert.equal(actionsCalledHash.onTooltipShow, 0, + assert.equal(actionsCalledHash.onShow, 0, 'Should not have called show'); /* Check show */ @@ -57,24 +57,24 @@ test('It animates with delay passed as a number', function(assert) { this.$().trigger('mouseover'); }); - assert.equal(actionsCalledHash.onTooltipShow, 1, + assert.equal(actionsCalledHash.onShow, 1, 'Should have called show'); - assert.equal(actionsCalledHash.onTooltipHide, 0, + assert.equal(actionsCalledHash.onHide, 0, 'Should not have called hide'); run(() => { this.$().trigger('mouseleave'); }); - assert.equal(actionsCalledHash.onTooltipHide, 1, + assert.equal(actionsCalledHash.onHide, 1, 'Should have called hide'); /* Check destroy */ this.set('destroyTooltip', true); - assert.equal(actionsCalledHash.onTooltipDestroy, 1, + assert.equal(actionsCalledHash.onDestroy, 1, 'Should have called destroy'); }); From 3248b18a71d18e8b3c9621d89cea9c3006b8be9f Mon Sep 17 00:00:00 2001 From: a15n Date: Wed, 7 Sep 2016 15:20:03 -0700 Subject: [PATCH 31/38] use .in & check popover.in and popover.find --- addon/components/popover-on-element.js | 35 +++++++++++++++----------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/addon/components/popover-on-element.js b/addon/components/popover-on-element.js index fc14ef2a..7fabea71 100644 --- a/addon/components/popover-on-element.js +++ b/addon/components/popover-on-element.js @@ -52,12 +52,10 @@ export default TooltipAndPopoverComponent.extend({ // if a click is NOT on $target and NOT an ancestor of $target. // If so then it must be a click elsewhere and should close the popover // see... https://css-tricks.com/dangers-stopping-event-propagation/ - const clickedElement = event.target; - const isClickOutsideTarget = $target[0] !== clickedElement; - const isClickOutsidePopover = !$target.find(clickedElement).length; - const isShown = this.get('isShown'); + const isClickedElementElsewhere = this._isElementElsewhere(event.target); + const isPopoverShown = this.get('isShown'); - if (isClickOutsideTarget && isClickOutsidePopover && isShown) { + if (isClickedElementElsewhere && isPopoverShown) { this.hide(); } }); @@ -67,7 +65,8 @@ export default TooltipAndPopoverComponent.extend({ // $target.on('mousedown') is called when the $popover is // clicked because the $popover is contained within the $target. // This will ignores those types of clicks. - if ($popover[0] === event.target || $popover.find(event.target).length) { + const isMouseDownElementInPopover = this._isElementInPopover(event.target); + if (isMouseDownElementInPopover) { return; } this.toggle(); @@ -79,14 +78,10 @@ export default TooltipAndPopoverComponent.extend({ $popover.on('focusout', () => { // use a run.later() to allow the 'focusout' event to finish handling run.later(() => { - const focusedElement = document.activeElement; - const isFocusedElementTarget = focusedElement === $target[0]; - const isFocusedElementPopover = focusedElement === $popover[0]; - const isFocusedElementInPopover = !!$popover.find(focusedElement).length; - if (isFocusedElementTarget || isFocusedElementPopover || isFocusedElementInPopover) { - return; + const isFocusedElementElsewhere = this._isElementElsewhere(document.activeElement); + if (isFocusedElementElsewhere) { + this.hide(); } - this.hide(); }); }); }, @@ -102,12 +97,24 @@ export default TooltipAndPopoverComponent.extend({ $target.add($popover).off(`${_showOn} mouseover ${_hideOn} mouseout mousedown focus focusout`); $(document).off(`click.${target}`); + }, + _isElementInPopover(newElement) { + // determines if newElement is $popover or contained within $popover + const $popover = this.$(); + return $popover.is(newElement) || $popover.find(newElement).length; + }, + _isElementElsewhere(newElement) { + // determines if newElement is not $target, not $popover, and not contained within either + const $target = $(this.get('target')); + + const isNewElementOutsideTarget = !$target.is(newElement) && !$target.find(newElement).length; + const isNewElementOutsidePopover = !this._isElementInPopover(newElement); + return isNewElementOutsideTarget && isNewElementOutsidePopover; }, actions: { hide() { this.hide(); } }, - }); From 4c451068fd19da3d48505c6575cbbf185537ae5d Mon Sep 17 00:00:00 2001 From: a15n Date: Wed, 7 Sep 2016 17:34:05 -0700 Subject: [PATCH 32/38] make hideDelay an integer --- addon/components/popover-on-element.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addon/components/popover-on-element.js b/addon/components/popover-on-element.js index 7fabea71..12d52b59 100644 --- a/addon/components/popover-on-element.js +++ b/addon/components/popover-on-element.js @@ -6,8 +6,10 @@ const { $, run } = Ember; export default TooltipAndPopoverComponent.extend({ - hideDelay: '250', + /* Options */ + hideDelay: 250, + /* Properties */ layout, classNames: ['ember-popover'], _isMouseInside: false, From ff262b63397af30f00135365f2d1119dcb3a2b5b Mon Sep 17 00:00:00 2001 From: a15n Date: Wed, 7 Sep 2016 20:41:06 -0700 Subject: [PATCH 33/38] focus events triggered without jQuery --- tests/integration/components/event-test.js | 12 +++- .../components/popover/focus-test.js | 71 +++++++++---------- 2 files changed, 41 insertions(+), 42 deletions(-) diff --git a/tests/integration/components/event-test.js b/tests/integration/components/event-test.js index 03a5ba34..197f68fa 100644 --- a/tests/integration/components/event-test.js +++ b/tests/integration/components/event-test.js @@ -57,18 +57,24 @@ test('It toggles with focus', function(assert) { assert.expect(3); - this.render(hbs`{{tooltip-on-component event='focus'}}`); + this.render(hbs` +
+ {{tooltip-on-element event='focus'}} +
+ `); + + const target = window.document.getElementById('target'); assertHide(assert, this); run(() => { - this.$().trigger('focus'); + target.dispatchEvent(new window.Event('focus')); }); assertShow(assert, this); run(this, () => { - this.$().trigger('blur'); + target.dispatchEvent(new window.Event('blur')); }); assertHide(assert, this); diff --git a/tests/integration/components/popover/focus-test.js b/tests/integration/components/popover/focus-test.js index cb7bcaa3..4c930b14 100644 --- a/tests/integration/components/popover/focus-test.js +++ b/tests/integration/components/popover/focus-test.js @@ -9,37 +9,34 @@ moduleForComponent('popover-on-element', 'Integration | Option | hover', { integration: true }); -test('Popover: target focus, popover focus, elsewhere focus', function(assert) { +test('Popover: target focus, popover focus, popover blur', function(assert) { this.render(hbs` -
- {{popover-on-element event="focus"}} +
+ {{popover-on-element event='focus' id="popover"}}
- `); const done = assert.async(); - const $target = $('.target'); - const $popover = $target.find('.ember-popover'); - const $elsewhere = $('.elsewhere'); + const target = window.document.getElementById('target'); + const popover = window.document.getElementById('popover'); assertPopoverHide(assert, this); run(() => { - $target.trigger('focus'); + target.dispatchEvent(new window.Event('focus')); }); assertPopoverShow(assert, this); run(() => { - $popover.trigger('focus'); + popover.dispatchEvent(new window.Event('focus')); }); assertPopoverShow(assert, this); run(() => { - $popover.trigger('focusout'); - $elsewhere.trigger('focus'); + popover.dispatchEvent(new window.Event('blur')); }); run.later(() => { @@ -51,45 +48,43 @@ test('Popover: target focus, popover focus, elsewhere focus', function(assert) { }); -test('Popover: target focus, targetInterior focus, popover focus, elsewhere focus', function(assert) { + +test('Popover: target focus, targetInterior focus, popover focus, popover blur', function(assert) { this.render(hbs` -
- - {{popover-on-element event="focus"}} +
+ + {{popover-on-element event='focus' id="popover"}}
- `); const done = assert.async(); - const $target = $('.target'); - const $targetInterior = $target.find('.target-interior'); - const $popover = $target.find('.ember-popover'); - const $elsewhere = $('.elsewhere'); + const target = window.document.getElementById('target'); + const targetInterior = window.document.getElementById('target-interior'); + const popover = window.document.getElementById('popover'); assertPopoverHide(assert, this); run(() => { - $target.trigger('focus'); + target.dispatchEvent(new window.Event('focus')); }); assertPopoverShow(assert, this); run(() => { - $targetInterior.trigger('focus'); + targetInterior.dispatchEvent(new window.Event('focus')); }); assertPopoverShow(assert, this); run(() => { - $popover.trigger('focus'); + popover.dispatchEvent(new window.Event('focus')); }); assertPopoverShow(assert, this); run(() => { - $popover.trigger('focusout'); - $elsewhere.trigger('focus'); + popover.dispatchEvent(new window.Event('blur')); }); run.later(() => { @@ -101,51 +96,49 @@ test('Popover: target focus, targetInterior focus, popover focus, elsewhere focu }); -test('Popover: target focus, popover focus, popoverInterior focus, elsewhere focus', function(assert) { +test('Popover: target focus, popover focus, popoverInterior focus, popover blur', function(assert) { this.render(hbs` -
- {{#popover-on-element event="focus"}} - +
+ {{#popover-on-element event='focus' id="popover"}} + {{/popover-on-element}}
- `); const done = assert.async(); - const $target = $('.target'); - const $popover = $target.find('.ember-popover'); - const $popoverInterior = $target.find('.popover-interior'); - const $elsewhere = $('.elsewhere'); + const target = window.document.getElementById('target'); + const popover = window.document.getElementById('popover'); + const popoverInterior = window.document.getElementById('popover-interior'); assertPopoverHide(assert, this); run(() => { - $target.trigger('focus'); + target.dispatchEvent(new window.Event('focus')); }); assertPopoverShow(assert, this); run(() => { - $popover.trigger('focus'); + popover.dispatchEvent(new window.Event('focus')); }); assertPopoverShow(assert, this); run(() => { - $popoverInterior.trigger('focus'); + popoverInterior.dispatchEvent(new window.Event('focus')); }); assertPopoverShow(assert, this); run(() => { - $elsewhere.trigger('focus'); + popover.dispatchEvent(new window.Event('blur')); }); run.later(() => { assertPopoverHide(assert, this); done(); - }, 10); + }, 100); assert.expect(5); From 5045c4c5e8818690c70b163b6fdf81ae86072057 Mon Sep 17 00:00:00 2001 From: a15n Date: Wed, 28 Sep 2016 17:58:16 -0700 Subject: [PATCH 34/38] remove className which didn't apply CSS styles --- addon/components/tooltip-and-popover.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addon/components/tooltip-and-popover.js b/addon/components/tooltip-and-popover.js index 2958c727..b0b9cf34 100644 --- a/addon/components/tooltip-and-popover.js +++ b/addon/components/tooltip-and-popover.js @@ -67,7 +67,6 @@ export default EmberTetherComponent.extend({ attributeBindings: ['aria-hidden', 'role', 'tabindex'], classNameBindings: ['effectClass'], classPrefix: 'ember-tooltip-or-popover', - classNames: ['ember-tooltip-or-popover'], _didUpdateTimeoutLength: 1000, // 1000 ms or 0 ms, depending whether in test mode _hideTimer: null, @@ -348,7 +347,7 @@ export default EmberTetherComponent.extend({ already a tooltip/popover shown in the DOM. Check that here and adjust the delay as needed. */ - let shownTooltipsOrPopovers = Ember.$(`.${this.get('classPrefix')}[aria-hidden="false"]`).length; + let shownTooltipsOrPopovers = Ember.$(`.${this.get('classPrefix')}-element[aria-hidden="false"]`).length; if (shownTooltipsOrPopovers) { delay = 0; From 110c0fa2ac1198cb3f846e82264d32d9e27a0ac3 Mon Sep 17 00:00:00 2001 From: a15n Date: Wed, 28 Sep 2016 18:46:27 -0700 Subject: [PATCH 35/38] add backwards compatibility for tooltipIsVisible --- addon/components/tooltip-and-popover.js | 4 ++++ .../components/tooltip-is-visible-test.js | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/addon/components/tooltip-and-popover.js b/addon/components/tooltip-and-popover.js index b0b9cf34..997f1758 100644 --- a/addon/components/tooltip-and-popover.js +++ b/addon/components/tooltip-and-popover.js @@ -41,6 +41,10 @@ export default EmberTetherComponent.extend({ spacing: 10, tabindex: '0', // A positive integer (to enable) or -1 (to disable) isShown: false, + tooltipIsVisible: computed.deprecatingAlias('isShown', { + id: 'tooltip-and-popover.tooltipIsVisible', + until: '3.0.0', + }), keepInWindow: true, /* diff --git a/tests/integration/components/tooltip-is-visible-test.js b/tests/integration/components/tooltip-is-visible-test.js index 6ddf04d6..2c9e20cf 100644 --- a/tests/integration/components/tooltip-is-visible-test.js +++ b/tests/integration/components/tooltip-is-visible-test.js @@ -26,3 +26,23 @@ test('It toggles with isShown', function(assert) { assertHide(assert, this); }); + +test('It toggles with tooltipIsVisible', function(assert) { + // tooltipIsVisible is deprecated in favor of isShown + // tooltipIsVisible will be supported until v3.0.0 + + assert.expect(2); + + this.set('showTooltip', true); + + this.render(hbs`{{tooltip-on-component tooltipIsVisible=showTooltip}}`); + + assertShow(assert, this); + + run(() => { + this.set('showTooltip', false); + }); + + assertHide(assert, this); + +}); From 599399719575a69686473dc0ca38ad56d76375f6 Mon Sep 17 00:00:00 2001 From: a15n Date: Wed, 28 Sep 2016 19:00:01 -0700 Subject: [PATCH 36/38] add backwards support for onTooltip____ actions --- addon/components/tooltip-and-popover.js | 17 +++++ tests/integration/components/actions-test.js | 74 ++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/addon/components/tooltip-and-popover.js b/addon/components/tooltip-and-popover.js index 997f1758..f201bf88 100644 --- a/addon/components/tooltip-and-popover.js +++ b/addon/components/tooltip-and-popover.js @@ -66,6 +66,23 @@ export default EmberTetherComponent.extend({ onRender: null, onShow: null, + onTooltipDestroy: computed.deprecatingAlias('onDestroy', { + id: 'tooltip-and-popover.onTooltipDestroy', + until: '3.0.0', + }), + onTooltipHide: computed.deprecatingAlias('onHide', { + id: 'tooltip-and-popover.onTooltipHide', + until: '3.0.0', + }), + onTooltipRender: computed.deprecatingAlias('onRender', { + id: 'tooltip-and-popover.onTooltipRender', + until: '3.0.0', + }), + onTooltipShow: computed.deprecatingAlias('onShow', { + id: 'tooltip-and-popover.onTooltipShow', + until: '3.0.0', + }), + /* Properties */ attributeBindings: ['aria-hidden', 'role', 'tabindex'], diff --git a/tests/integration/components/actions-test.js b/tests/integration/components/actions-test.js index 102e4c46..f06217a6 100644 --- a/tests/integration/components/actions-test.js +++ b/tests/integration/components/actions-test.js @@ -8,6 +8,80 @@ moduleForComponent('tooltip-on-component', 'Integration | Option | actions', { integration: true }); +test('It supports deprecated lifecycle actions', function(assert) { + // onTooltip____ actions are deprecated in favor of on on_____ actions + // these actions will be supported until v3.0.0 + + const actionsCalledHash = { + onTooltipDestroy: 0, + onTooltipHide: 0, + onTooltipRender: 0, + onTooltipShow: 0, + }; + + assert.expect(10); + + /* Setup the actions and handlers... */ + + Object.keys(actionsCalledHash).forEach((action) => { + this.on(action, () => { + assert.ok(true, `Should call ${action}`); + + /* Count the calls... */ + + actionsCalledHash[action]++; + }); + }); + + /* Now, let's go through the component lifecycle */ + + this.render(hbs` + {{#unless destroyTooltip}} + {{tooltip-on-component + onTooltipDestroy='onTooltipDestroy' + onTooltipHide='onTooltipHide' + onTooltipRender='onTooltipRender' + onTooltipShow='onTooltipShow' + }} + {{/unless}} + `); + + /* Check render */ + + assert.equal(actionsCalledHash.onTooltipRender, 1, + 'Should have called render'); + + assert.equal(actionsCalledHash.onTooltipShow, 0, + 'Should not have called show'); + + /* Check show */ + + run(() => { + this.$().trigger('mouseover'); + }); + + assert.equal(actionsCalledHash.onTooltipShow, 1, + 'Should have called show'); + + assert.equal(actionsCalledHash.onTooltipHide, 0, + 'Should not have called hide'); + + run(() => { + this.$().trigger('mouseleave'); + }); + + assert.equal(actionsCalledHash.onTooltipHide, 1, + 'Should have called hide'); + + /* Check destroy */ + + this.set('destroyTooltip', true); + + assert.equal(actionsCalledHash.onTooltipDestroy, 1, + 'Should have called destroy'); + +}); + test('It calls lifecycle actions', function(assert) { const actionsCalledHash = { onDestroy: 0, From b5c05a9bdc0f56d8d3c65bd81c51c2a7d163c020 Mon Sep 17 00:00:00 2001 From: a15n Date: Wed, 28 Sep 2016 19:27:52 -0700 Subject: [PATCH 37/38] update the README --- README.md | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 43d13ad3..9e7f748e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -Ember-tooltips [![Build Status](https://travis-ci.org/sir-dunxalot/ember-tooltips.svg)](https://travis-ci.org/sir-dunxalot/ember-tooltips) [![npm](https://img.shields.io/npm/v/ember-tooltips.svg)](https://www.npmjs.com/package/ember-tooltips) +Ember-tooltips (and popovers) [![Build Status](https://travis-ci.org/sir-dunxalot/ember-tooltips.svg)](https://travis-ci.org/sir-dunxalot/ember-tooltips) [![npm](https://img.shields.io/npm/v/ember-tooltips.svg)](https://www.npmjs.com/package/ember-tooltips) ====== -Render tooltips on components and other HTML elements using HTMLBars. +Render tooltips and popovers on components and other HTML elements using HTMLBars. ## Installation @@ -87,9 +87,13 @@ You can also specify the ID of the element to attach the tooltip to: The `target` property must be an ID, including the `#`. +### Popover on Element + +Popovers can be created with `{{popover-on-element}}` and `{{popover-on-component}}` with the same `target` behavior as tooltips. + ## Options -Options are set as attributes on the tooltip components. Current tooltip properties this addon supports are: +Options are set as attributes on the tooltip/popover components. Current tooltip/popover properties this addon supports are: - [class](#class) - [delay](#delay) @@ -103,6 +107,7 @@ Options are set as attributes on the tooltip components. Current tooltip propert - [showOn](#show-on) - [spacing](#spacing) - [isShown](#is-shown) +- [hideDelay (popover only)](#hide-delay) #### Class @@ -316,6 +321,20 @@ This can be useful alongside `event='none'` when you only want to toolip to show {{tooltip-on-component isShown=showTooltip}} ``` +#### Hide delay + +| Type | Number | +|---------|---------| +| Default | 250 | + +**POPOVER ONLY:** The number of milliseconds before the popover will hide after the user hovers away from the popover and the popover target. This is only applicable when `event='hover'`. + +```hbs +{{popover-on-component event="hover" hideDelay=300}} +``` + +![popover-hover](https://cloud.githubusercontent.com/assets/7050871/18113238/e010ee64-6ee2-11e6-9ff1-a0c674a6d702.gif) + ### Setting Defaults You can set the default for any option by extending the `{{tooltip-on-element}}` component: @@ -333,7 +352,7 @@ export default TooltipOnElementComponent.extend({ ## Actions -Four actions are available for you to hook onto through the tooltip lifecycle: +Four actions are available for you to hook onto through the tooltip/popover lifecycle: ```hbs {{tooltip-on-component From 70608edce5154dddf1c17fc4110f49ed7a7ffb80 Mon Sep 17 00:00:00 2001 From: a15n Date: Wed, 5 Oct 2016 10:38:22 -0700 Subject: [PATCH 38/38] increase timeout for popover focus tests --- tests/integration/components/popover/focus-test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/components/popover/focus-test.js b/tests/integration/components/popover/focus-test.js index 4c930b14..e9dd1682 100644 --- a/tests/integration/components/popover/focus-test.js +++ b/tests/integration/components/popover/focus-test.js @@ -42,7 +42,7 @@ test('Popover: target focus, popover focus, popover blur', function(assert) { run.later(() => { assertPopoverHide(assert, this); done(); - }, 10); + }, 100); assert.expect(4); @@ -90,7 +90,7 @@ test('Popover: target focus, targetInterior focus, popover focus, popover blur', run.later(() => { assertPopoverHide(assert, this); done(); - }, 10); + }, 100); assert.expect(5);