From e7517741a284a2c764bf3a58cc28e27cdadf43f6 Mon Sep 17 00:00:00 2001 From: BrianSipple Date: Fri, 20 May 2016 00:23:47 -0700 Subject: [PATCH 1/3] Enable mouseleave toggling behavior for tether-dialog --- CHANGELOG.md | 28 +++++++++++++- README.md | 1 + addon/components/positioned-container.js | 5 ++- addon/components/tether-dialog.js | 38 ++++++++++++++++++- addon/initializers/add-modals-container.js | 16 +++++--- .../current/components/tether-dialog.hbs | 1 + bower.json | 4 +- ember-cli-build.js | 4 ++ package.json | 6 +-- tests/acceptance/modal-dialog-test.js | 7 +--- tests/acceptance/tether-dialog-test.js | 21 ++++++++++ tests/dummy/app/controllers/application.js | 9 +++++ tests/dummy/app/templates/-tether-dialog.hbs | 24 ++++++++++++ tests/helpers/modal-asserts.js | 30 ++++++--------- tests/index.html | 6 +++ 15 files changed, 160 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 388a4ba6..c9a3f2a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,25 @@ # Change Log -## [0.8.3](https://github.com/yapplabs/ember-modal-dialog/tree/0.8.3) (2015-12-22) -[Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.8.2...0.8.3) +## [0.8.4](https://github.com/yapplabs/ember-modal-dialog/tree/0.8.4) (2016-05-11) +[Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.8.3...0.8.4) + +**Closed issues:** + +- How about keeping targetModifier: null as default for tether-dialogs? [\#127](https://github.com/yapplabs/ember-modal-dialog/issues/127) +- Rendering Link-to helpers inside the dialog changes the route, but doesn't close the modal. [\#119](https://github.com/yapplabs/ember-modal-dialog/issues/119) +- clickOutsideToClose false is ignored on close action [\#115](https://github.com/yapplabs/ember-modal-dialog/issues/115) +- Ember 1.12 compatible release? [\#112](https://github.com/yapplabs/ember-modal-dialog/issues/112) +- Extending breaks wormhole to modal-overlays on 2.1.x and 2.2.x [\#97](https://github.com/yapplabs/ember-modal-dialog/issues/97) + +**Merged pull requests:** + +- Improve tether-dialog positioning [\#128](https://github.com/yapplabs/ember-modal-dialog/pull/128) ([chrislopresto](https://github.com/chrislopresto)) +- Update ember-tether version [\#126](https://github.com/yapplabs/ember-modal-dialog/pull/126) ([chrislopresto](https://github.com/chrislopresto)) +- Added link to introduction video [\#118](https://github.com/yapplabs/ember-modal-dialog/pull/118) ([taras](https://github.com/taras)) +- Fixes ember-try dependency for ember 1.13.10 [\#117](https://github.com/yapplabs/ember-modal-dialog/pull/117) ([jeremywrowe](https://github.com/jeremywrowe)) + +## [v0.8.3](https://github.com/yapplabs/ember-modal-dialog/tree/v0.8.3) (2015-12-22) +[Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.8.2...v0.8.3) **Closed issues:** @@ -32,6 +50,8 @@ **Closed issues:** - How to pass along close action from inner component? [\#84](https://github.com/yapplabs/ember-modal-dialog/issues/84) +- Acceptance testing the tether-dialog when input is in the block? [\#81](https://github.com/yapplabs/ember-modal-dialog/issues/81) +- Alignment 'none' issue in positioned container [\#75](https://github.com/yapplabs/ember-modal-dialog/issues/75) **Merged pull requests:** @@ -41,6 +61,10 @@ ## [v0.8.0](https://github.com/yapplabs/ember-modal-dialog/tree/v0.8.0) (2015-09-10) [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.7.7...v0.8.0) +**Fixed bugs:** + +- Opening modal fails in Ember 1.13.7+ [\#71](https://github.com/yapplabs/ember-modal-dialog/issues/71) + **Closed issues:** - Custom modal template [\#69](https://github.com/yapplabs/ember-modal-dialog/issues/69) diff --git a/README.md b/README.md index d4fa6dac..fd4fbf74 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,7 @@ The tether-dialog component supports all of the modal-dialog properties specifie Property | Purpose --------------------- | ------------- `hasOverlay` | Toggles presence of overlay div in DOM +`mouseleaveToClose` | Closes the dialog on a `mouseleave` event. Useful when providing 'pop-out' modal functionality for a specific area of the page. (default: `false`) `tetherClassPrefix` | Proxies to Hubspot Tether* `offset` | Proxies to Hubspot Tether* `targetOffset` | Proxies to Hubspot Tether* diff --git a/addon/components/positioned-container.js b/addon/components/positioned-container.js index 3c9a6b6f..e45bc22e 100644 --- a/addon/components/positioned-container.js +++ b/addon/components/positioned-container.js @@ -14,7 +14,10 @@ export default Ember.Component.extend({ // center (relative to container) targetAttachment: 'center', - isPositioned: computed('targetAttachment', 'target', function() { + isPositioned: computed('targetAttachment', 'target', 'renderInPlace', function() { + if (this.get('renderInPlace')) { + return false; + } if (this.get('target') && this.get('targetAttachment')) { return true; } diff --git a/addon/components/tether-dialog.js b/addon/components/tether-dialog.js index f53ad625..5ee4e706 100644 --- a/addon/components/tether-dialog.js +++ b/addon/components/tether-dialog.js @@ -16,17 +16,51 @@ export default ModalDialog.extend({ targetAttachment: 'middle center', attachment: 'middle center', - targetModifier: 'visible', hasOverlay: true, + target: 'viewport', // element, css selector, view instance, 'viewport', or 'scroll-handle' + + mouseleaveToClose: false, + + // if `mouseleaveToClose`, cache this in didInsertElement so that each dialog has a unqiue listener namespace + _eventListenerNameSpace: null, tetherClassPrefix: 'ember-tether', // offset - passed in // targetOffset - passed in + // targetModifier - passed in makeOverlayClickableOnIOS: Ember.on('didInsertElement', function() { if (isIOS && get(this, 'hasOverlay')) { Ember.$('div[data-ember-modal-dialog-overlay]').css('cursor', 'pointer'); } - }) + }), + + didInsertElement() { + this._super(...arguments); + + if (this.get('mouseleaveToClose')) { + this._eventListenerNameSpace = `ember-modal-dialog.${this._mouseLeaveEventId}`; + + Ember.$('.ember-modal-dialog').on(`mouseleave.${this._eventListenerNameSpace}`, (event) => { + // DON'T close if the mouse is leaving the target and entering the target + if (!event.relatedTarget || !Ember.$(event.relatedTarget).hasClass('ember-tether-target')) { + this.send('close'); + } + }); + Ember.$(this.get('target')).on(`mouseleave.${this._eventListenerNameSpace}`, (event) => { + // DON'T close if the mouse is leaving the target and entering the dialog + if (!event.relatedTarget || !Ember.$(event.relatedTarget).hasClass('ember-modal-dialog')) { + this.send('close'); + } + }); + + } + }, + + willDestroyElement() { + this._super(...arguments); + Ember.$('.ember-modal-dialog').off(`mouseleave.${this._eventListenerNameSpace}`); + Ember.$(this.get('target')).off(`mouseleave.${this._eventListenerNameSpace}`); + } }); diff --git a/addon/initializers/add-modals-container.js b/addon/initializers/add-modals-container.js index b28b613f..903d47df 100644 --- a/addon/initializers/add-modals-container.js +++ b/addon/initializers/add-modals-container.js @@ -1,21 +1,25 @@ /*globals document */ -const hasDOM = typeof document !== 'undefined'; +let hasDOM = typeof document !== 'undefined'; function appendContainerElement(rootElementId, id) { if (!hasDOM) { return; } - const rootEl = document.querySelector(rootElementId); - const modalContainerEl = document.createElement('div'); + if (document.getElementById(id)) { + return; + } + + let rootEl = document.querySelector(rootElementId); + let modalContainerEl = document.createElement('div'); modalContainerEl.id = id; rootEl.appendChild(modalContainerEl); } export default function() { - const application = arguments[1] || arguments[0]; - const emberModalDialog = application.emberModalDialog || {}; - const modalContainerElId = emberModalDialog.modalRootElementId || 'modal-overlays'; + let application = arguments[1] || arguments[0]; + let emberModalDialog = application.emberModalDialog || {}; + let modalContainerElId = emberModalDialog.modalRootElementId || 'modal-overlays'; application.register('config:modals-container-id', modalContainerElId, diff --git a/addon/templates/current/components/tether-dialog.hbs b/addon/templates/current/components/tether-dialog.hbs index 67b15abe..f2fb2b09 100644 --- a/addon/templates/current/components/tether-dialog.hbs +++ b/addon/templates/current/components/tether-dialog.hbs @@ -10,6 +10,7 @@ {{#ember-modal-dialog-positioned-container classNameBindings="containerClassNamesString targetAttachmentClass container-class" targetAttachment=targetAttachment target=target + renderInPlace=renderInPlace }} {{yield}} {{/ember-modal-dialog-positioned-container}} diff --git a/bower.json b/bower.json index fe714b8f..31da7b5a 100644 --- a/bower.json +++ b/bower.json @@ -8,9 +8,9 @@ "ember-qunit": "0.4.15", "ember-qunit-notifications": "0.0.7", "ember-resolver": "~0.1.18", - "tether": "~0.7.2", "jquery": "1.11.3", "loader.js": "ember-cli/loader.js#3.2.1", - "qunit": "~1.19.0" + "qunit": "~1.19.0", + "bind-polyfill": "^1.0.0" } } diff --git a/ember-cli-build.js b/ember-cli-build.js index 65b0a673..c65cdbda 100644 --- a/ember-cli-build.js +++ b/ember-cli-build.js @@ -13,5 +13,9 @@ module.exports = function(/*defaults*/) { behave. You most likely want to be modifying `./index.js` or app's build file */ + if (app.env === 'test') { + app.import('bower_components/bind-polyfill/index.js'); + } + return app.toTree(); }; diff --git a/package.json b/package.json index 1a42b476..79de5acf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ember-modal-dialog", - "version": "0.8.3", + "version": "0.8.4", "description": "An ember-cli addon for implementing modal dialogs", "directories": { "doc": "doc", @@ -33,13 +33,13 @@ "ember-cli-sass": "3.1.1", "ember-cli-sri": "^1.0.3", "ember-cli-uglify": "^1.2.0", - "ember-code-snippet": "1.1.0", + "ember-code-snippet": "1.3.0", "ember-disable-prototype-extensions": "^1.0.0", "ember-disable-proxy-controllers": "^1.0.0", "ember-export-application-global": "^1.0.3", "ember-legacy-views": "0.2.0", "ember-suave": "1.2.3", - "ember-tether": "^0.1.3", + "ember-tether": "^0.3.1", "ember-truth-helpers": "1.2.0", "ember-try": "0.0.6", "glob": "4.4.0" diff --git a/tests/acceptance/modal-dialog-test.js b/tests/acceptance/modal-dialog-test.js index 2c762481..983c90ec 100644 --- a/tests/acceptance/modal-dialog-test.js +++ b/tests/acceptance/modal-dialog-test.js @@ -76,7 +76,6 @@ test('target - selector', function(assert) { openSelector: '#example-target-selector button', dialogText: 'Target - Selector', closeSelector: dialogCloseButton, - tethered: true, whileOpen() { assert.ok(Ember.$(dialogSelector).hasClass('ember-modal-dialog-target-attachment-left'), 'has targetAttachment class name'); } @@ -87,8 +86,7 @@ test('target - element', function(assert) { assert.dialogOpensAndCloses({ openSelector: '#example-target-element button', dialogText: 'Target - Element', - closeSelector: dialogCloseButton, - tethered: true + closeSelector: dialogCloseButton }); }); @@ -96,8 +94,7 @@ test('target - view', function(assert) { assert.dialogOpensAndCloses({ openSelector: '#example-target-view button', dialogText: 'Target - View', - closeSelector: dialogCloseButton, - tethered: true + closeSelector: dialogCloseButton }); }); diff --git a/tests/acceptance/tether-dialog-test.js b/tests/acceptance/tether-dialog-test.js index fe7645dc..87e97345 100644 --- a/tests/acceptance/tether-dialog-test.js +++ b/tests/acceptance/tether-dialog-test.js @@ -105,6 +105,27 @@ test('target - view', function(assert) { }); }); +test('mouseleaveToClose', function(assert) { + const openSelector = '#targetedMouseleaveToClose'; + const dialogText = 'Target w/ mouseleaveToClose'; + + assert.dialogOpensAndCloses({ + openSelector, + dialogText, + closeSelector: dialogCloseButton, + hasOverlay: false, + tethered: true + }); + + click(openSelector).then(() => { + assert.closesOnMouseleave(dialogSelector, dialogText); + }); + + click(openSelector).then(() => { + assert.closesOnMouseleave(openSelector, dialogText); + }); +}); + test('subclassed modal', function(assert) { assert.dialogOpensAndCloses({ openSelector: '#example-subclass button', diff --git a/tests/dummy/app/controllers/application.js b/tests/dummy/app/controllers/application.js index 7b47301a..8a246db4 100644 --- a/tests/dummy/app/controllers/application.js +++ b/tests/dummy/app/controllers/application.js @@ -12,6 +12,7 @@ export default Ember.Controller.extend({ isShowingTargetSelector: false, isShowingTargetView: false, isShowingTargetElement: false, + isShowingMouseleaveToClose: false, isShowingSubclassed: false, isShowingInPlace: false, isInPlace: true, @@ -91,6 +92,9 @@ export default Ember.Controller.extend({ } this.toggleProperty('isShowingTargetElement'); }, + toggleMouseleaveToClose() { + this.toggleProperty('isShowingMouseleaveToClose'); + }, toggleSubclassed() { this.toggleProperty('isShowingSubclassed'); }, @@ -125,6 +129,11 @@ export default Ember.Controller.extend({ this.set('isShowingTargetElement', false); this.set('exampleTargetAttachment', 'middle left'); this.set('exampleAttachment', 'middle right'); + }, + closeMouseleaveToClose() { + this.set('isShowingMouseleaveToClose', false); + this.set('exampleTargetAttachment', 'middle left'); + this.set('exampleAttachment', 'middle right'); } } }); diff --git a/tests/dummy/app/templates/-tether-dialog.hbs b/tests/dummy/app/templates/-tether-dialog.hbs index 85e1b0e6..67528a12 100644 --- a/tests/dummy/app/templates/-tether-dialog.hbs +++ b/tests/dummy/app/templates/-tether-dialog.hbs @@ -155,6 +155,30 @@ {{/if}} +
+

Close w/ mouseleave

+
+ +
+ {{code-snippet name='target-selector-tether-dialog.hbs'}} + {{#if isShowingMouseleaveToClose}} + {{!-- BEGIN-SNIPPET target-element-tether-dialog --}} + {{#tether-dialog close='toggleMouseleaveToClose' + hasOverlay=false + targetAttachment=exampleTargetAttachment + attachment=exampleAttachment + mouseleaveToClose=true + target='#targetedMouseleaveToClose' }} +

Stop! Modal Time!

+

Target w/ mouseleaveToClose - Selector: '#targetedMouseleaveToClose'

+

Target Attachment: {{exampleTargetAttachment}}

+

Attachment: {{exampleAttachment}}

+ + {{/tether-dialog}} + {{!-- END-SNIPPET --}} + {{/if}} +
+

Via Subclass

diff --git a/tests/helpers/modal-asserts.js b/tests/helpers/modal-asserts.js index d56a875d..2e4b5eef 100644 --- a/tests/helpers/modal-asserts.js +++ b/tests/helpers/modal-asserts.js @@ -33,25 +33,17 @@ export default function registerAssertHelpers() { if (options.whileOpen) { options.whileOpen(); } - // TODO: Craft a better approach for testing the tethered modals that - // tether appends to the body tag - if (options.tethered) { - // HACK: Click open button 4 more times to let the modal go around - // the horn and then disappear. This is obviously tightly coupled - // to arbitrary demo behavior. - for (let i = 1; i <= 4; i++) { - click(options.openSelector, options.context); - } - andThen(function() { - self.isAbsent(overlaySelector); - self.isAbsent(dialogContent); - }); - } else { - return click(options.closeSelector, options.context).then(function() { - self.isAbsent(overlaySelector); - self.isAbsent(dialogContent); - }); - } + return click(options.closeSelector, options.context).then(function() { + self.isAbsent(overlaySelector); + self.isAbsent(dialogContent); + }); + }); + }; + + assert.closesOnMouseleave = function(mouseLeaveSelector, dialogText) { + const dialogContent = [dialogSelector, `:contains(${dialogText})`].join(''); + return triggerEvent(dialogContent, 'mouseleave').then(() => { + this.isAbsent(dialogContent); }); }; } diff --git a/tests/index.html b/tests/index.html index 5b6aa01b..de94d2b1 100644 --- a/tests/index.html +++ b/tests/index.html @@ -18,6 +18,12 @@ .ember-tether.ember-modal-dialog { z-index: 1000000; } + + #ember-testing-container { + /* Set position static to short-circuit Hubspot Tether's positioning */ + /* https://github.com/HubSpot/tether/pull/98/ */ + position: static !important; + } {{content-for 'head-footer'}} From c7d15ee37643dd96c7dde50db653a96ad3de413e Mon Sep 17 00:00:00 2001 From: BrianSipple Date: Sun, 22 May 2016 20:20:20 -0700 Subject: [PATCH 2/3] update execution of tests --- tests/acceptance/tether-dialog-test.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/acceptance/tether-dialog-test.js b/tests/acceptance/tether-dialog-test.js index 61e27b16..c57b4b41 100644 --- a/tests/acceptance/tether-dialog-test.js +++ b/tests/acceptance/tether-dialog-test.js @@ -126,13 +126,15 @@ test('mouseleaveToClose', function(assert) { tethered: true }); - click(openSelector).then(() => { - assert.closesOnMouseleave(dialogSelector, dialogText); - }); - - click(openSelector).then(() => { - assert.closesOnMouseleave(openSelector, dialogText); - }); + click(openSelector) + .then(() => { + return assert.closesOnMouseleave(dialogSelector, dialogText); + }) + .then(() => { + click(openSelector).then(() => { + assert.closesOnMouseleave(openSelector, dialogText); + }); + }); }); test('subclassed modal', function(assert) { From 183a77358f7f8510ea2648b740e5872404542c37 Mon Sep 17 00:00:00 2001 From: BrianSipple Date: Tue, 24 May 2016 00:43:07 -0700 Subject: [PATCH 3/3] try breaking asserts out into separate test functions --- addon/components/tether-dialog.js | 2 +- tests/acceptance/tether-dialog-test.js | 28 +++++++++++++++++--------- tests/helpers/modal-asserts.js | 3 ++- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/addon/components/tether-dialog.js b/addon/components/tether-dialog.js index 5ee4e706..b6a2a6ac 100644 --- a/addon/components/tether-dialog.js +++ b/addon/components/tether-dialog.js @@ -39,7 +39,7 @@ export default ModalDialog.extend({ this._super(...arguments); if (this.get('mouseleaveToClose')) { - this._eventListenerNameSpace = `ember-modal-dialog.${this._mouseLeaveEventId}`; + this._eventListenerNameSpace = `ember-modal-dialog.${this.elementId}`; Ember.$('.ember-modal-dialog').on(`mouseleave.${this._eventListenerNameSpace}`, (event) => { // DON'T close if the mouse is leaving the target and entering the target diff --git a/tests/acceptance/tether-dialog-test.js b/tests/acceptance/tether-dialog-test.js index c57b4b41..5a74b682 100644 --- a/tests/acceptance/tether-dialog-test.js +++ b/tests/acceptance/tether-dialog-test.js @@ -114,7 +114,7 @@ test('target - view', function(assert) { }); }); -test('mouseleaveToClose', function(assert) { +test('mouseleaveToClose functions with manual close button', function(assert) { const openSelector = '#targetedMouseleaveToClose'; const dialogText = 'Target w/ mouseleaveToClose'; @@ -125,16 +125,24 @@ test('mouseleaveToClose', function(assert) { hasOverlay: false, tethered: true }); +}); + +test('mouseleaveToClose closes when a mouseleave event occurs on the dialog', function(assert) { + const openSelector = '#targetedMouseleaveToClose'; + const dialogText = 'Target w/ mouseleaveToClose'; - click(openSelector) - .then(() => { - return assert.closesOnMouseleave(dialogSelector, dialogText); - }) - .then(() => { - click(openSelector).then(() => { - assert.closesOnMouseleave(openSelector, dialogText); - }); - }); + click(openSelector).then(() => { + return assert.closesOnMouseleave(dialogSelector, dialogText); + }); +}); + +test('mouseleaveToClose closes when a mouseleave event occurs on the element used to open', function(assert) { + const openSelector = '#targetedMouseleaveToClose'; + const dialogText = 'Target w/ mouseleaveToClose'; + + click(openSelector).then(() => { + return assert.closesOnMouseleave(openSelector, dialogText); + }); }); test('subclassed modal', function(assert) { diff --git a/tests/helpers/modal-asserts.js b/tests/helpers/modal-asserts.js index 2e4b5eef..8df04473 100644 --- a/tests/helpers/modal-asserts.js +++ b/tests/helpers/modal-asserts.js @@ -42,7 +42,8 @@ export default function registerAssertHelpers() { assert.closesOnMouseleave = function(mouseLeaveSelector, dialogText) { const dialogContent = [dialogSelector, `:contains(${dialogText})`].join(''); - return triggerEvent(dialogContent, 'mouseleave').then(() => { + + return triggerEvent(mouseLeaveSelector, 'mouseleave').then(() => { this.isAbsent(dialogContent); }); };