Skip to content

Commit

Permalink
Add if-key helper.
Browse files Browse the repository at this point in the history
- can be used any place a function that received a KeyboardEvent would be used
- does not participate in the wider ember-keyboard functionality (responders, priority, etc), but can be useful if you just want to leverage the key combo
matching code of this addon
  • Loading branch information
lukemelia committed May 25, 2020
1 parent 63bd97b commit 2a86e9d
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 13 deletions.
32 changes: 19 additions & 13 deletions API-DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,23 @@ interface IEmberKeyboardEvent {

### Low-level key-combo matching API

A low-level API for the matching engine that determines whether a particular keyboard event is considered to match a specified key-combo will also be exposed.
A low-level API for the matching engine that determines whether a particular keyboard event is
considered to match a specified key-combo will also be exposed.

It will be available as an `if-key` helper:
It will be available (and used internally) as an `isKey` JS function:

```js
import { isKey } from 'ember-keyboard';

function onEvent(ev) {
if (isKey('keydown:alt+x', ev)) {
this.handleAltX();
}
}
```

A variation will also be available as an `if-key` helper that can be used
any place a function that received a KeyboardEvent would be used:

```hbs
{{!-- attach your own event handler using the {{on}} modifier --}}
Expand All @@ -237,17 +251,9 @@ It will be available as an `if-key` helper:
<SomeComponent @onKey={{if-key "alt+x" this.doThing}}/>
```

It will also be available as an `isKey` JS function:

```js
import { isKey } from 'ember-keyboard';

function onEvent(ev) {
if (isKey('keydown:alt+x', ev)) {
this.handleAltX();
}
}
```
Note that usage like this will not participate in the wider ember-keyboard functionality
(responders, priority, etc), but can be useful if you just want to leverage the key combo
matching code.

## Alternatives

Expand Down
21 changes: 21 additions & 0 deletions addon/helpers/if-key.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { helper } from '@ember/component/helper';
import isKey from 'ember-keyboard/utils/is-key';
import listenerName from 'ember-keyboard/utils/listener-name';
import { assert } from '@ember/debug';

export default helper(function ifKey([keyCombo, callback]/*, hash*/) {
return function(event) {
assert(
'ember-keyboard: You must pass a function as the second argument to the `if-key` helper',
typeof callback === 'function'
);
assert(
'ember-keyboard: The `if-key` helper expects to be invoked with a KeyboardEvent',
event instanceof KeyboardEvent
);

if (isKey(listenerName(event.type, keyCombo), event)) {
callback(event);
};
};
});
1 change: 1 addition & 0 deletions app/helpers/if-key.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default, ifKey } from 'ember-keyboard/helpers/if-key';
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"ember-fn-helper-polyfill": "^1.0.2",
"ember-load-initializers": "^2.1.1",
"ember-maybe-import-regenerator": "^0.1.6",
"ember-on-helper": "^0.1.0",
"ember-on-modifier": "^1.0.1",
"ember-qunit": "^4.6.0",
"ember-resolver": "^7.0.0",
Expand Down
38 changes: 38 additions & 0 deletions tests/dummy/app/templates/usage.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,44 @@ pressed together with the Alt key:
</button>
```
### Low-level key-matching API
A low-level API for the matching engine that determines whether a particular keyboard event is
considered to match a specified key-combo will also be exposed.
It will be available (and used internally) as an `isKey` JS function:
```js
import { isKey } from 'ember-keyboard';
function onEvent(ev) {
if (isKey('keydown:alt+x', ev)) {
this.handleKeydownWithAltX();
}
}
```
A variation will also be available as an `if-key` helper that can be used
any place a function that received a KeyboardEvent would be used:
```hbs
{{!-- attach your own event handler using the {{on}} modifier --}}
<div {{on 'keydown' (if-key 'alt+c' this.doThing)}}></div>
{{!-- combining with the ember-on-helper addon --}}
{{on-document 'keydown' (if-key 'alt+KeyX' this.doThing)}}
{{!-- use some third-party component API --}}
<SomeComponent @onKey={{if-key 'alt+x' this.doThing}}/>
```
Note that low-level usage like this will not participate in the wider ember-keyboard functionality
(responders, priority, etc), but can be useful if you just want to leverage the key combo
matching code of ember-keyboard.
### Using ember-keyboard in routes and classic components
First, add `EKMixin` to a route or a classic Ember component:
Expand Down
69 changes: 69 additions & 0 deletions tests/integration/helpers/if-key-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { module, skip, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { click, render, resetOnerror, setupOnerror, triggerEvent } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';

module('Integration | Helper | if-key', function(hooks) {
setupRenderingTest(hooks);

let onTriggerCalled;
hooks.beforeEach(function() {
onTriggerCalled = false;
this.set('onTrigger', () => {
onTriggerCalled = true;
});
});


module('error cases', function(hooks) {
hooks.afterEach(() => resetOnerror());

// This doesn't work. I wish it did, but can't figure out why not.
skip('errors if invoked without a handler', async function(assert) {
assert.expect(1);
setupOnerror(function(error) {
assert.strictEqual(
error.message,
"Assertion Failed: ember-keyboard: The if-key helper must be provided a function as its second argument",
'error is thrown'
);
});
await render(hbs`{{on-document "keydown" (if-key "alt+c" unknownEvent)}}`);
await triggerEvent(document.body, 'keydown', { altKey: true, key: 'c' });
});

// This doesn't work. I wish it did, but can't figure out why not.
skip('warns if called without a keyboard event', async function(assert) {
assert.expect(1);
setupOnerror(function(error) {
assert.strictEqual(
error.message,
"Assertion Failed: ember-keyboard: The if-key helper expects to be invoked with a KeyboardEvent",
'error is thrown'
);
});
await render(hbs`<button {{on 'click' (if-key "alt+c" onTrigger)}}>Press me</button>`);
await click('button');
});
});

test('called with event', async function(assert) {
let onTriggerCalledWith;
this.set('onTrigger', (ev) => {
onTriggerCalledWith = ev;
});
await render(hbs`{{on-document "keydown" (if-key "alt+c" onTrigger)}}`);
await triggerEvent(document.body, 'keydown', { altKey: true, key: 'c' });
assert.ok(onTriggerCalledWith instanceof KeyboardEvent);
});

test('not called if key combo does not match', async function(assert) {
let onTriggerCalledWith;
this.set('onTrigger', (ev) => {
onTriggerCalledWith = ev;
});
await render(hbs`{{on-document "keydown" (if-key "alt+c" onTrigger)}}`);
await triggerEvent(document.body, 'keydown', { shiftKey: true, key: 'z' });
assert.ok(!onTriggerCalledWith, 'trigger is on invoked if key does not match');
});
});
7 changes: 7 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6081,6 +6081,13 @@ ember-modifier@^1.0.3:
ember-cli-string-utils "^1.1.0"
ember-modifier-manager-polyfill "^1.2.0"

ember-on-helper@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/ember-on-helper/-/ember-on-helper-0.1.0.tgz#c8b1fef9173fc8546c4933b57ecd7ffbcebad99e"
integrity sha512-jjafBnWfoA4VSSje476ft5G+urlvvuSDddwAJjKDCjKY9mbe3hAEsJiMBAaPObJRMm1FOglCuKjQZfwDDls6MQ==
dependencies:
ember-cli-babel "^7.7.3"

ember-on-modifier@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/ember-on-modifier/-/ember-on-modifier-1.0.1.tgz#b99e9c9d7919a9f536bfc9d4a68704462eceb0fe"
Expand Down

0 comments on commit 2a86e9d

Please sign in to comment.