From 11cbc399482e7c6cdddbf6bafd39dd1e22063ad5 Mon Sep 17 00:00:00 2001 From: John Cowen Date: Wed, 15 May 2019 14:21:19 +0100 Subject: [PATCH] ui: Expands create-listeners API to allow recursive and functions (#5616) 1. Adds a Listeners class, which lets us... 2. Add Listeners recursively. So you can createListeners().add(createListeners()) 3. Also add the ability to `.add` as a plain function This moves the entire idea more towards a generic teardown utility --- ui-v2/app/utils/dom/create-listeners.js | 64 +++++++++++-------- .../unit/utils/dom/create-listeners-test.js | 26 +++++++- 2 files changed, 61 insertions(+), 29 deletions(-) diff --git a/ui-v2/app/utils/dom/create-listeners.js b/ui-v2/app/utils/dom/create-listeners.js index 623046198fda..d317a773fc29 100644 --- a/ui-v2/app/utils/dom/create-listeners.js +++ b/ui-v2/app/utils/dom/create-listeners.js @@ -1,32 +1,40 @@ -export default function(listeners = []) { - const add = function(target, event, handler) { - let addEventListener = 'addEventListener'; - let removeEventListener = 'removeEventListener'; - if (typeof target[addEventListener] === 'undefined') { - addEventListener = 'on'; - removeEventListener = 'off'; +class Listeners { + constructor(listeners = []) { + this.listeners = listeners; + } + add(target, event, handler) { + let remove; + if (typeof target === 'function') { + remove = target; + } else if (target instanceof Listeners) { + remove = target.remove.bind(target); + } else { + let addEventListener = 'addEventListener'; + let removeEventListener = 'removeEventListener'; + if (typeof target[addEventListener] === 'undefined') { + addEventListener = 'on'; + removeEventListener = 'off'; + } + target[addEventListener](event, handler); + remove = function() { + target[removeEventListener](event, handler); + return handler; + }; } - target[addEventListener](event, handler); - const remove = function() { - target[removeEventListener](event, handler); - return handler; + this.listeners.push(remove); + return () => { + const pos = this.listeners.findIndex(function(item) { + return item === remove; + }); + return this.listeners.splice(pos, 1)[0](); }; - listeners.push(remove); - return remove; - }; - // TODO: Allow passing of a 'listener remove' in here - // call it, find in the array and remove - // Post-thoughts, pretty sure this is covered now by returning the remove - // function above, use-case for wanting to use this method to remove individual - // listeners is probably pretty limited, this method itself could be easily implemented - // from the outside also, but I suppose its handy to keep here - const remove = function() { - const handlers = listeners.map(item => item()); - listeners.splice(0, listeners.length); + } + remove() { + const handlers = this.listeners.map(item => item()); + this.listeners.splice(0, this.listeners.length); return handlers; - }; - return { - add: add, - remove: remove, - }; + } +} +export default function(listeners = []) { + return new Listeners(listeners); } diff --git a/ui-v2/tests/unit/utils/dom/create-listeners-test.js b/ui-v2/tests/unit/utils/dom/create-listeners-test.js index ae735467bad5..afe2f618167e 100644 --- a/ui-v2/tests/unit/utils/dom/create-listeners-test.js +++ b/ui-v2/tests/unit/utils/dom/create-listeners-test.js @@ -9,7 +9,7 @@ test('it has add and remove methods', function(assert) { assert.ok(typeof listeners.add === 'function'); assert.ok(typeof listeners.remove === 'function'); }); -test('add returns an remove function', function(assert) { +test('add returns a remove function', function(assert) { const listeners = createListeners(); const remove = listeners.add({ addEventListener: function() {}, @@ -64,6 +64,30 @@ test('listeners are removed on remove', function(assert) { assert.ok(stub.calledOnce); assert.ok(stub.calledWith(name, handler)); }); +test('listeners as functions are removed on remove', function(assert) { + const listeners = createListeners(); + const stub = this.stub(); + const remove = listeners.add(stub); + remove(); + assert.ok(stub.calledOnce); +}); +test('listeners as other listeners are removed on remove', function(assert) { + const listeners = createListeners(); + const listeners2 = createListeners(); + const stub = this.stub(); + listeners2.add(stub); + const remove = listeners.add(listeners2); + remove(); + assert.ok(stub.calledOnce); +}); +test('listeners as functions of other listeners are removed on remove', function(assert) { + const listeners = createListeners(); + const listeners2 = createListeners(); + const stub = this.stub(); + const remove = listeners.add(listeners2.add(stub)); + remove(); + assert.ok(stub.calledOnce); +}); test('remove returns the original handler', function(assert) { const listeners = createListeners(); const target = {