From e92ea73d53b08d2f967c96dd940836e022cdf8dc Mon Sep 17 00:00:00 2001 From: Alex LaFroscia Date: Tue, 29 Jan 2019 15:25:13 -0800 Subject: [PATCH] feat: add `from-event` element modifier` --- addon/modifiers/from-event.js | 71 +++++++++++++ app/modifiers/from-event.js | 1 + package.json | 1 + .../integration/modifiers/from-event-test.js | 99 +++++++++++++++++++ yarn.lock | 46 ++++++++- 5 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 addon/modifiers/from-event.js create mode 100644 app/modifiers/from-event.js create mode 100644 tests/integration/modifiers/from-event-test.js diff --git a/addon/modifiers/from-event.js b/addon/modifiers/from-event.js new file mode 100644 index 0000000..ac4d3a5 --- /dev/null +++ b/addon/modifiers/from-event.js @@ -0,0 +1,71 @@ +import Ember from "ember"; +import { fromEvent } from "rxjs"; + +export default Ember._setModifierManager( + () => ({ + createModifier() { + return { + subscription: undefined, + element: undefined + }; + }, + + _setupSubscription(state, eventName, operatorOrObserver, maybeObserver) { + const { element } = state; + let operator, observer; + + if (operatorOrObserver && maybeObserver) { + operator = operatorOrObserver; + observer = maybeObserver; + } else if (operatorOrObserver && !maybeObserver) { + observer = operatorOrObserver; + } + + let observable = fromEvent(element, eventName); + + if (operator) { + observable = observable.pipe(operator); + } + + state.subscription = observable.subscribe(observer); + }, + + installModifier( + state, + element, + { + positional: [eventName, operatorOrObserver, maybeObserver] + } + ) { + state.element = element; + + this._setupSubscription( + state, + eventName, + operatorOrObserver, + maybeObserver + ); + }, + + updateModifier( + state, + { + positional: [eventName, operatorOrSubscribe, maybeObserver] + } + ) { + state.subscription.unsubscribe(); + + this._setupSubscription( + state, + eventName, + operatorOrSubscribe, + maybeObserver + ); + }, + + destroyModifier({ subscription }) { + subscription.unsubscribe(); + } + }), + class FromEventModifier {} +); diff --git a/app/modifiers/from-event.js b/app/modifiers/from-event.js new file mode 100644 index 0000000..7c78730 --- /dev/null +++ b/app/modifiers/from-event.js @@ -0,0 +1 @@ +export { default } from "ember-rx/modifiers/from-event"; diff --git a/package.json b/package.json index 0ae8266..c0214a2 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "dependencies": { "ember-auto-import": "^1.2.19", "ember-cli-babel": "^7.1.2", + "ember-modifier-manager-polyfill": "^1.0.1", "ember-stream-helper": "^1.0.1", "rxjs": "^6.3.3" }, diff --git a/tests/integration/modifiers/from-event-test.js b/tests/integration/modifiers/from-event-test.js new file mode 100644 index 0000000..d1485ec --- /dev/null +++ b/tests/integration/modifiers/from-event-test.js @@ -0,0 +1,99 @@ +import { module, test } from "qunit"; +import { setupRenderingTest } from "ember-qunit"; +import { setupScheduler } from "ember-rx/test-support"; +import { render, click } from "@ember/test-helpers"; +import { Subject } from "rxjs"; +import { filter } from "rxjs/operators"; +import hbs from "htmlbars-inline-precompile"; +import td from "testdouble"; + +module("Integration | Modifier | from-event", function(hooks) { + setupRenderingTest(hooks); + setupScheduler(hooks); + + test("it can subscribe to events", async function(assert) { + this.observer = td.function(); + + await render(hbs` + + `); + + await click("button"); + + assert.verify( + this.observer(td.matchers.isA(MouseEvent)), + "The observer was called with the event" + ); + }); + + test("it can pipe the observable through an operator", async function(assert) { + this.operator = filter((_event, index) => index % 2 === 0); + this.observer = td.function(); + + await render(hbs` + + `); + + await click("button"); + await click("button"); + + assert.verify( + this.observer(td.matchers.isA(MouseEvent)), + { times: 1 }, + "The observer was called one time" + ); + }); + + test("it handles the arguments changing", async function(assert) { + const originalObserver = td.function("original observer"); + const newObserver = td.function("new observer"); + + this.observer = originalObserver; + + await render(hbs` + + `); + + this.set("observer", newObserver); + + await click("button"); + + assert.verify( + originalObserver(td.matchers.isA(MouseEvent)), + { times: 0 }, + "The original observer is never called" + ); + + assert.verify( + newObserver(td.matchers.isA(MouseEvent)), + { times: 1 }, + "The new observer is called" + ); + }); + + test("it can receive a `Subject` to surface the observable", async function(assert) { + this.subject = new Subject(); + const observer = td.function("Original observer"); + + this.subject.subscribe(observer); + + await render(hbs` + + `); + + await click("button"); + + assert.verify( + observer(td.matchers.isA(MouseEvent)), + "The observer is called through the Subject" + ); + }); +}); diff --git a/yarn.lock b/yarn.lock index 8a88570..59b817a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1347,7 +1347,7 @@ babel-plugin-debug-macros@^0.1.10: dependencies: semver "^5.3.0" -babel-plugin-debug-macros@^0.2.0-beta.6: +babel-plugin-debug-macros@^0.2.0, babel-plugin-debug-macros@^0.2.0-beta.6: version "0.2.0" resolved "https://registry.yarnpkg.com/babel-plugin-debug-macros/-/babel-plugin-debug-macros-0.2.0.tgz#0120ac20ce06ccc57bf493b667cf24b85c28da7a" integrity sha512-Wpmw4TbhR3Eq2t3W51eBAQSdKlr+uAyF0GI4GtPfMCD12Y4cIdpKC9l0RjNTH/P9isFypSqqewMPm7//fnZlNA== @@ -3668,6 +3668,30 @@ ember-cli-babel@^7.1.2, ember-cli-babel@^7.1.3, ember-cli-babel@^7.2.0, ember-cl ensure-posix-path "^1.0.2" semver "^5.5.0" +ember-cli-babel@^7.4.0: + version "7.4.1" + resolved "https://registry.yarnpkg.com/ember-cli-babel/-/ember-cli-babel-7.4.1.tgz#9892f5883f5a6b1f0f86fb1331fc491338f372ad" + integrity sha512-h6qZKHyULm5SYhvjNOeXvLl3kHaBdh37g5QqTTiC/vMiWP/xnyNp2bMoq52qq+SLm/bE8+5UcVTKjrNl0+IqXA== + dependencies: + "@babel/core" "^7.0.0" + "@babel/plugin-transform-modules-amd" "^7.0.0" + "@babel/plugin-transform-runtime" "^7.2.0" + "@babel/polyfill" "^7.0.0" + "@babel/preset-env" "^7.0.0" + "@babel/runtime" "^7.2.0" + amd-name-resolver "^1.2.1" + babel-plugin-debug-macros "^0.2.0-beta.6" + babel-plugin-ember-modules-api-polyfill "^2.6.0" + babel-plugin-module-resolver "^3.1.1" + broccoli-babel-transpiler "^7.1.0" + broccoli-debug "^0.6.4" + broccoli-funnel "^2.0.1" + broccoli-source "^1.1.0" + clone "^2.1.2" + ember-cli-version-checker "^2.1.2" + ensure-posix-path "^1.0.2" + semver "^5.5.0" + ember-cli-broccoli-sane-watcher@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ember-cli-broccoli-sane-watcher/-/ember-cli-broccoli-sane-watcher-3.0.0.tgz#dc1812c047e1ceec4413d3c41b51a9ffc61b4cfe" @@ -3810,7 +3834,7 @@ ember-cli-uglify@^2.1.0: broccoli-uglify-sourcemap "^2.1.1" lodash.defaultsdeep "^4.6.0" -ember-cli-version-checker@^2.0.0, ember-cli-version-checker@^2.1.0, ember-cli-version-checker@^2.1.2: +ember-cli-version-checker@^2.0.0, ember-cli-version-checker@^2.1.0, ember-cli-version-checker@^2.1.1, ember-cli-version-checker@^2.1.2: version "2.2.0" resolved "https://registry.yarnpkg.com/ember-cli-version-checker/-/ember-cli-version-checker-2.2.0.tgz#47771b731fe0962705e27c8199a9e3825709f3b3" integrity sha512-G+KtYIVlSOWGcNaTFHk76xR4GdzDLzAS4uxZUKdASuFX0KJE43C6DaqL+y3VTpUFLI2FIkAS6HZ4I1YBi+S3hg== @@ -3922,6 +3946,15 @@ ember-cli@~3.7.1: watch-detector "^0.1.0" yam "^1.0.0" +ember-compatibility-helpers@^1.2.0-beta.1: + version "1.2.0-beta.1" + resolved "https://registry.yarnpkg.com/ember-compatibility-helpers/-/ember-compatibility-helpers-1.2.0-beta.1.tgz#cd5898734f398f977707cbf8d448689377449180" + integrity sha512-ewKxBP0DIcbuMyGR8P+ned3H6vgCI5+2t+5MOwcaIFxRYUx6Bm0Dqk6Y8UWzwVgkxlZspC34k8QE/QLlSI7oiA== + dependencies: + babel-plugin-debug-macros "^0.2.0" + ember-cli-version-checker "^2.1.1" + semver "^5.4.1" + ember-disable-prototype-extensions@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/ember-disable-prototype-extensions/-/ember-disable-prototype-extensions-1.1.3.tgz#1969135217654b5e278f9fe2d9d4e49b5720329e" @@ -3951,6 +3984,15 @@ ember-maybe-import-regenerator@^0.1.6: ember-cli-babel "^6.0.0-beta.4" regenerator-runtime "^0.9.5" +ember-modifier-manager-polyfill@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ember-modifier-manager-polyfill/-/ember-modifier-manager-polyfill-1.0.1.tgz#98e7dbda0a8e16137057ef23cd8e23bfb38d15cf" + integrity sha512-mOur7Kkh84FpCwvYnANQ5YD4jq2YcFW6zQUhJC5VW90etvtsSj7lVXWL5CZrxs0KskPCNAm0+b7LAtr0T6k9sA== + dependencies: + ember-cli-babel "^7.4.0" + ember-cli-version-checker "^2.1.2" + ember-compatibility-helpers "^1.2.0-beta.1" + ember-qunit@^3.4.1: version "3.5.3" resolved "https://registry.yarnpkg.com/ember-qunit/-/ember-qunit-3.5.3.tgz#bfd0bff8298c78c77e870cca43fe0826e78a0d09"