From f3195f0c120114e1e2514c2eff8c859db88405b1 Mon Sep 17 00:00:00 2001 From: Oscar Date: Wed, 26 Oct 2016 17:57:03 +0200 Subject: [PATCH] Better event handling for multiple modals with clickOutsideToClose - Registers unique click event handlers for each modal to prevent accidental removal of the wrong one. - Adds acceptance test helper nativeClick to help with testing on ember versions < 2.5. --- addon/components/modal-dialog.js | 6 ++--- tests/.jshintrc | 3 ++- tests/acceptance/tether-dialog-test.js | 17 +++++++++++++++ tests/dummy/app/controllers/application.js | 4 ++++ tests/dummy/app/templates/-tether-dialog.hbs | 13 +++++++++++ tests/helpers/acceptance-helpers.js | 23 ++++++++++++++++++++ tests/helpers/modal-asserts.js | 4 ++-- tests/helpers/start-app.js | 1 + 8 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 tests/helpers/acceptance-helpers.js diff --git a/addon/components/modal-dialog.js b/addon/components/modal-dialog.js index a13f66cf..9f5cc179 100644 --- a/addon/components/modal-dialog.js +++ b/addon/components/modal-dialog.js @@ -2,7 +2,7 @@ import Ember from 'ember'; import layout from '../templates/components/modal-dialog'; const { dasherize } = Ember.String; -const { $, computed, inject } = Ember; +const { $, computed, guidFor, inject } = Ember; const { oneWay } = computed; const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent); const computedJoin = function(prop) { @@ -61,14 +61,14 @@ export default Ember.Component.extend({ this.send('close'); } }; - const registerClick = () => $(document).on('click.ember-modal-dialog', handleClick); + const registerClick = () => $(document).on(`click.ember-modal-dialog-${guidFor(this)}`, handleClick); // setTimeout needed or else the click handler will catch the click that spawned this modal dialog setTimeout(registerClick); this._super(...arguments); }, willDestroyElement() { - $(document).off('click.ember-modal-dialog'); + $(document).off(`click.ember-modal-dialog-${guidFor(this)}`); this._super(...arguments); }, diff --git a/tests/.jshintrc b/tests/.jshintrc index 6ec0b7c1..f060e79b 100644 --- a/tests/.jshintrc +++ b/tests/.jshintrc @@ -21,7 +21,8 @@ "andThen", "currentURL", "currentPath", - "currentRouteName" + "currentRouteName", + "nativeClick" ], "node": false, "browser": false, diff --git a/tests/acceptance/tether-dialog-test.js b/tests/acceptance/tether-dialog-test.js index 209dfd22..6a8ed585 100644 --- a/tests/acceptance/tether-dialog-test.js +++ b/tests/acceptance/tether-dialog-test.js @@ -82,6 +82,23 @@ test('modal without overlay click outside to close', function(assert) { dialogText: 'Without Overlay - Click Outside to Close', closeSelector: '#example-without-overlay-click-outside-to-close' }); + + assert.dialogOpensAndCloses({ + openSelector: '#example-without-overlay-click-outside-to-close button:nth-of-type(2)', + dialogText: 'Without Overlay Another One - Click Outside to Close', + closeSelector: '#example-without-overlay-click-outside-to-close button:nth-of-type(1)' + }); + + assert.dialogOpensAndCloses({ + openSelector: '#example-without-overlay-click-outside-to-close button:nth-of-type(2)', + dialogText: 'Without Overlay Another One - Click Outside to Close', + closeSelector: '#example-without-overlay-click-outside-to-close' + }); + + andThen(function() { + assert.equal(Ember.$(dialogSelector).length, 0, 'All modals are absent'); + }); + }); test('target - selector', function(assert) { diff --git a/tests/dummy/app/controllers/application.js b/tests/dummy/app/controllers/application.js index b2a52c44..2ee86561 100644 --- a/tests/dummy/app/controllers/application.js +++ b/tests/dummy/app/controllers/application.js @@ -8,6 +8,7 @@ export default Ember.Controller.extend({ isShowingTranslucent: false, isShowingWithoutOverlay: false, isShowingWithoutOverlayClickOutsideToClose: false, + isShowingWithoutOverlayClickOutsideToCloseAnotherOne: false, isShowingCustomStyles: false, isShowingTargetSelector: false, isShowingTargetView: false, @@ -53,6 +54,9 @@ export default Ember.Controller.extend({ toggleWithoutOverlayClickOutsideToClose() { this.toggleProperty('isShowingWithoutOverlayClickOutsideToClose'); }, + toggleWithoutOverlayClickOutsideToCloseAnotherOne() { + this.toggleProperty('isShowingWithoutOverlayClickOutsideToCloseAnotherOne'); + }, toggleCustomStyles() { this.toggleProperty('isShowingCustomStyles'); }, diff --git a/tests/dummy/app/templates/-tether-dialog.hbs b/tests/dummy/app/templates/-tether-dialog.hbs index ab087e6c..0e8fcfd5 100644 --- a/tests/dummy/app/templates/-tether-dialog.hbs +++ b/tests/dummy/app/templates/-tether-dialog.hbs @@ -48,6 +48,8 @@

Without Overlay - Click Outside to Close

+ {{code-snippet name='without-overlay-click-outside-to-close-tether-dialog.hbs'}} {{#if isShowingWithoutOverlayClickOutsideToClose}} {{!-- BEGIN-SNIPPET without-overlay-click-outside-to-close-tether-dialog --}} @@ -60,6 +62,17 @@ {{/tether-dialog}} {{!-- END-SNIPPET --}} {{/if}} + {{#if isShowingWithoutOverlayClickOutsideToCloseAnotherOne}} + {{#tether-dialog close='toggleWithoutOverlayClickOutsideToCloseAnotherOne' + hasOverlay=false + clickOutsideToClose=true + offset='100px 0'}} +

Stop! Another modal!

+

Without Overlay Another One - Click Outside to Close

+ + {{/tether-dialog}} + {{/if}} +
diff --git a/tests/helpers/acceptance-helpers.js b/tests/helpers/acceptance-helpers.js new file mode 100644 index 00000000..161c2513 --- /dev/null +++ b/tests/helpers/acceptance-helpers.js @@ -0,0 +1,23 @@ +import Ember from 'ember'; + +function fireNativeMouseEvent(eventType, selectorOrDomElement, context) { + let event = new window.Event(eventType, { bubbles: true, cancelable: true, view: window }); + let target; + if (typeof selectorOrDomElement === 'string') { + target = Ember.$(selectorOrDomElement, context)[0]; + } else { + target = selectorOrDomElement; + } + Ember.run(() => target.dispatchEvent(event)); +} + +export default function acceptanceTestHelpers() { + + Ember.Test.registerAsyncHelper('nativeClick', function(app, selectorOrDomElement, context) { + fireNativeMouseEvent('click', selectorOrDomElement, context); + wait(); + }); + +} + +export default acceptanceTestHelpers(); diff --git a/tests/helpers/modal-asserts.js b/tests/helpers/modal-asserts.js index bae7a4d9..5870ae01 100644 --- a/tests/helpers/modal-asserts.js +++ b/tests/helpers/modal-asserts.js @@ -25,7 +25,7 @@ export default function registerAssertHelpers() { message = message || `Dialog triggered by ${options.openSelector} failed to open and close`; const dialogContent = [dialogSelector, `:contains(${options.dialogText})`].join(''); const self = this; - return click(options.openSelector, options.context).then(function() { + return nativeClick(options.openSelector, options.context).then(function() { if (options.hasOverlay) { self.isPresentOnce(overlaySelector); } @@ -33,7 +33,7 @@ export default function registerAssertHelpers() { if (options.whileOpen) { options.whileOpen(); } - return click(options.closeSelector, options.context).then(function() { + return nativeClick(options.closeSelector, options.context).then(function() { self.isAbsent(overlaySelector); self.isAbsent(dialogContent); }); diff --git a/tests/helpers/start-app.js b/tests/helpers/start-app.js index 3fe35b97..ec53eb8f 100644 --- a/tests/helpers/start-app.js +++ b/tests/helpers/start-app.js @@ -2,6 +2,7 @@ import Ember from 'ember'; import Application from '../../app'; import config from '../../config/environment'; import registerAssertHelpers from './modal-asserts'; +import './acceptance-helpers'; export default function startApp(attrs) { let application;