Skip to content

Commit

Permalink
馃専 Fix #156: Add a customKeyCodes configuration option
Browse files Browse the repository at this point in the history
  • Loading branch information
greena13 committed Jun 12, 2019
1 parent 9688e92 commit e12dfdf
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 1 deletion.
33 changes: 32 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ See the [upgrade notes](https://github.com/greena13/react-hotkeys/releases/tag/v
- [Browser key names](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values) and [Mousetrap syntax](https://github.com/ccampbell/mousetrap)
- Define [global](#GlobalHotKeys-component) and [in-focus](#HotKeys-component) hot keys
- [Display a list of available hot keys to the user](#Displaying-a-list-of-available-hot-keys)
- [Defining custom key codes](#defining-custom-key-codes) for WebOS and other environments
- Works with React's Synthetic KeyboardEvents and event delegation and provides [predictable and expected behaviour](#Interaction-with-React) to anyone familiar with React
- Optimized by default, but allows you to turn off different optimisation measures in a granular fashion
- Customizable through a simple [configuration API](#Configuration)
Expand Down Expand Up @@ -91,6 +92,7 @@ export default MyNode;
- [Specifying key events (keydown, keypress, keyup)](#specifying-key-events-keydown-keypress-keyup)
- [Specifying key map display data](#specifying-key-map-display-data)
- [Deciding which key map syntax to use](#deciding-which-key-map-syntax-to-use)
- [Defining custom key codes](#defining-custom-key-codes)
- [Defining Handlers](#defining-handlers)
- [DEPRECATED: Hard Sequence Handlers](#deprecated-hard-sequence-handlers)
- [Interaction with React](#interaction-with-react)
Expand Down Expand Up @@ -347,6 +349,29 @@ As a general rule, you should use the syntax that is the most brief, but still a
| Object | Have a single key sequence and want to specify a different key event or display data |
| Object (sequences attribute) | Have multiple key sequences that trigger the same action, and want to specify a different key event or display data |

#### Defining custom key codes

When you are working in a WebOS environment, or a similar, you may have need to define custom key codes. You can do so using the `customKeyCodes` [Configuration option](#configuration):

```javascript

import {configure} from 'react-hotkeys';

configure({
customKeyCodes: {
10009: 'BackTV'
}
})
```

Once defined, you are then able to use the key names in you action sequences:

```javascript
const keyMap = {
MY_ACTION: 'BackTV',
};
```

## Defining Handlers

Key maps trigger actions when they match a key sequence. Handlers are the functions that `react-hotkeys` calls to handle those actions.
Expand Down Expand Up @@ -1058,7 +1083,7 @@ configure({
*/
ignoreEventsCondition: function,

/**
/**
* Whether to allow hard sequences, or the binding of handlers to actions
* that have names that are valid key sequences, which implicitly define
* actions that are triggered by that key sequence
Expand Down Expand Up @@ -1114,6 +1139,12 @@ configure({
* allowCombinationSubmatches is false.
*/
allowCombinationSubmatches: false,

/**
* A mapping of custom key codes to key names that you can then use in your
* key sequences
*/
customKeyCodes: {},
});
```

Expand Down
5 changes: 5 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,11 @@ export interface ConfigurationOptions {
* allowCombinationSubmatches is false.
*/
allowCombinationSubmatches?: boolean,

/**
* A mapping of custom key codes to key names
*/
customKeyCodes?: { [key: number]: string },
}

/**
Expand Down
9 changes: 9 additions & 0 deletions src/helpers/resolving-handlers/getKeyName.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
*/

import reactsGetEventKey from '../../vendor/react-dom/reactsGetEventKey';
import Configuration from '../../lib/Configuration';
import hasKey from '../../utils/object/hasKey';

/**
* Returns normalized name of key
Expand All @@ -12,6 +14,13 @@ import reactsGetEventKey from '../../vendor/react-dom/reactsGetEventKey';
*/
function getKeyName(event) {
const keyName = function(){
const customKeyCodes = Configuration.option('customKeyCodes');
const keyCode = event.keyCode || event.charCode;

if (hasKey(customKeyCodes, keyCode)) {
return customKeyCodes[keyCode];
}

if (event.nativeEvent) {
return event.key;
} else {
Expand Down
10 changes: 10 additions & 0 deletions src/lib/Configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ const _defaultConfiguration = {
* This does not affect the behaviour of React Hotkeys, but rather what happens to
* the event once React Hotkeys is done with it (whether it's allowed to propagate
* any further through the Render tree).
* @type {Boolean}
*/
stopEventPropagationAfterHandling: true,

Expand All @@ -103,15 +104,24 @@ const _defaultConfiguration = {
* This does not affect the behaviour of React Hotkeys, but rather what happens to
* the event once React Hotkeys is done with it (whether it's allowed to propagate
* any further through the Render tree).
* @type {Boolean}
*/
stopEventPropagationAfterIgnoring: true,

/**
* Whether to allow combination submatches - e.g. if there is an action bound to
* cmd, pressing shift+cmd will *not* trigger that action when
* allowCombinationSubmatches is false.
* @type {Boolean}
*/
allowCombinationSubmatches: false,

/**
* A mapping of custom key codes to key names that you can then use in your
* key sequences
* @type {Object<Number, KeyName>}
*/
customKeyCodes: {},
};

const _configuration = {
Expand Down
55 changes: 55 additions & 0 deletions test/GlobalHotKeys/UsingCustomKeyCodes.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React from 'react';
import {mount} from 'enzyme';
import {expect} from 'chai';
import sinon from 'sinon';
import simulant from 'simulant';

import { configure, GlobalHotKeys } from '../../src/';

describe('GlobalHotKeys: Using custom key codes:', function () {
beforeEach(function () {
this.reactDiv = document.createElement('div');
document.body.appendChild(this.reactDiv);
});

afterEach(function() {
document.body.removeChild(this.reactDiv);
});

describe('when a custom key code has been added and a keyMap has been defined in terms of the custom key', () => {
beforeEach(function () {
this.customKeyName = 'BackTV';

configure({ customKeyCodes: { 10009: this.customKeyName }});

const keyMap = {
ACTION: this.customKeyName
};

this.handler = sinon.spy();

const handlers = {
ACTION: this.handler
};

this.wrapper = mount(
<GlobalHotKeys keyMap={keyMap} handlers={handlers}>
<div className="childElement" />
</GlobalHotKeys>,
{ attachTo: this.reactDiv }
);
});

afterEach(function() {
configure({ customKeyCodes: {}});
});

it('then calls the corresponding handler when the custom key occurs', function() {
expect(this.handler).to.not.have.been.called;

simulant.fire(this.reactDiv, 'keydown', { keyCode: 10009 });

expect(this.handler).to.have.been.calledOnce;
});
});
});
45 changes: 45 additions & 0 deletions test/HotKeys/UsingCustomKeyCodes.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';
import {mount} from 'enzyme';
import {expect} from 'chai';
import sinon from 'sinon';

import {HotKeys, configure} from '../../src/';
import FocusableElement from '../support/FocusableElement';

describe('Using custom key codes:', function () {
describe('when a custom key code has been added and a keyMap has been defined in terms of the custom key', () => {
beforeEach(function () {
this.customKeyName = 'BackTV';
this.customKeyCode = 10009;

configure({ customKeyCodes: { [this.customKeyCode]: this.customKeyName }});

const keyMap = {
ACTION: this.customKeyName
};

this.handler = sinon.spy();

this.wrapper = mount(
<HotKeys keyMap={keyMap} handlers={{ACTION: this.handler}}>
<div className="childElement" />
</HotKeys>
);

this.targetElement = new FocusableElement(this.wrapper, '.childElement');
this.targetElement.focus();
});

afterEach(function() {
configure({ customKeyCodes: {}});
});

it('then calls the corresponding handler when the custom key occurs', function() {
expect(this.handler).to.not.have.been.called;

this.targetElement.keyDown(undefined, { keyCode: this.customKeyCode });

expect(this.handler).to.have.been.calledOnce;
});
});
});

0 comments on commit e12dfdf

Please sign in to comment.