Giter VIP home page Giter VIP logo

react-keyboard-event-handler's Introduction

react-keyboard-event-handler

A React component for handling keyboard events (keyup, keydown and keypress*).

Main features

  1. Supports combined keys ( e.g. CTRL + S and even CTRL + SHIFT + S );
  2. Supports handling modifier key alone (e.g. handle pressing 'ctrl' key);
  3. Supports almost all keys including function keys (e.g. 'F1');
  4. Provides easy-to-use and consistent key names to free you from dealing with numeric key codes and/or browser compatibilities;
  5. Supports key alias such 'alphanumeric' and 'all' as short cuts for handling multiple keys;
  6. Supports multiple handler instances and provides an easy way to control enable/disable status for each handler via props isDisabled and isExclusive.

Live demo

demo/dist/index.html

Installation

npm install react-keyboard-event-handler

Usage

Handling global key events

By default, KeyboardEventHandler only handles global key events sourced from document.body. That is, key events fired without any focused element (event.target). It will not handle key events sourced from form controls (e.g. input ), links or any tab-enabled(focusable) elements (e.g. elements with tabIndex attribute).

Web browsers come with default keyboard behaviors for tab-enabled elements. It may be more appropriate to let the browser do its job in most cases.

import KeyboardEventHandler from 'react-keyboard-event-handler';

const ComponentA = (props) => (<div>
  <div>key detected: {props.eventKey}</div>
  <KeyboardEventHandler
    handleKeys={['a', 'b', 'c']}
    onKeyEvent={(key, e) => console.log(`do something upon keydown event of ${key}`)} />
</div>);

You can change this default, however, by setting handleFocusableElements prop to true;

Handling key events sourced from children elements

If KeyboardEventHandler wraps around any children elements, it will handle and ONLY handle key events sourced from its descendant elements, including any form controls, links or tab-enabled elements.

import KeyboardEventHandler from 'react-keyboard-event-handler';

const ComponentA = (props) => (<div>
  <div>key detected: {props.eventKey}</div>
  <KeyboardEventHandler
    handleKeys={['a', 'b', 'c']}
    onKeyEvent={(key, e) => console.log(`do something upon keydown event of ${key}`)} >
    <input type="text" placeholder="Key events will be handled"/>
    <a href="#" >Key events from focusable element will be handled</a>
  </KeyboardEventHandler>
</div>);

For form control elements, React provides with onKeyDown, onKeyPress and onKeyUp synthetic events. However, you may find it easier to work with the key names/alias provided by KeyboardEventHandler.

API summary

Property Type Default Description
handleKeys Array [] An array of keys this handler should handle.
There are also some handy alias for keys, see bellow for details. e.g. ['a', 'b', 'numeric']
handleEventType String keydown Keyboard event type.
This can be 'keyup', 'keydown' or 'keypress'.
*Note: 'keypress' event only support printable keys. i.e. no support for modifier keys or 'tab', 'enter' etc.
handleFocusableElements Bool false By default, handler only handles key events sourced from doucment.body. When this props is set to true, it will also handle key events from all focusable elements. This props only apply when there are no children.
isDisabled Boolean false Enable/Disable handling keyboard events
isExclusive Boolean false When set to true, all other handler instances are suspended.
This is useful for temporary disabling all other keyboard event handlers.
For example, for suppressing any other handlers on a page when a modal opens with its keyboard event handling.
onKeyEvent function () => null

A callback function to call when the handler detects a matched key event.

The signature of the callback function is:
function(key, event) { ... }

key
The key name matches the current keyboard event.
event
The native keyboard event. e.g. you can use event.keyCode to get the numeric key code. This is useful for handling keys that are not supported (i.e. does not have a key name defined for the keys).
children Any null If KeyboardEventHandler wraps around any children elements, it will handle and ONLY handle key events sourced from its descendant elements, including any form controls, links or tab-enabled elements. handleFocusableElements has no effect when children exists.

Key names and key alias

The handleKeys prop accepts an array of key names. Key names and key alias free developers from dealing with numeric char codes and/or key codes and browser compatibility issues with KeyboardEvent.code and KeyboardEvent.key. (Ref: JavaScript Madness: Keyboard Events)

  • Key names are in LOWER CASE for consistency. handleKeys=['a'] will still handle key event for 'A' with caps lock on.
  • To handle combined keys like shift and a, use key names in the format of shift+a;
  • You can also use key name aliases like 'numbers' or 'alphanumeric'.

Common keys

You can handle one or more common keys by using an array of their names.

<KeyboardEventHandler
    handleKeys={['a']}
    onKeyEvent={(key, e) => console.log('only handle "a" key')} />

Key name Description / key code
a, b, ... z letter keys, 65 ~ 90 and 97 ~ 112
0, 1, ... 9 number keys 48 ~ 57 and 41 , 96 ~ 105
f1, f2, ... f19 function keys 112 ~ 130
backspace 8
del/delete 46
ins/insert 45
tab 9
enter/return 13
esc 27
space 32
pageup 33
pagedown 34
end 35
home 36
left 37
up 38
right 39
down 40
shift 16
ctrl 17
alt 18
cap 20
num Num Lock, 144
clear 12
meta Meta, Win, Window, Cmd, Command, 91
; 186, 59
= 187, 61
, 188, 44
-/minus 189, 45, 173, 109
. 190, 110
/ 191, 111
` 192
[ 219
\ 220
] 221
* 106
+/plus 107
+/plus 107
'/quote 222

Note: Native keyboard events with modifier key(s) will NOT match common keys in handleKeys. e.g. handleKeys=['a'] will not handler events with combined keys 'Ctrl' and 'a'. To match native keyboard event with modifiers, read the next section.

Modifier keys

You can handle modifier keys combined with a common key by using key name in the format of ctrl+a or ctrl+shift+a. To use the + common key with modifier keys, use the alias key 'plus'. e.g. ctrl+plus.

<KeyboardEventHandler
    handleKeys={['ctrl+a']}
    onKeyEvent={(key, e) => console.log('only handle "a" key with control key pressed')} />

Key name Description
ctrl control, ctrl key
shift shift key
meta meta, cmd, Window, command key
alt option, alt key

Tips:

  • Modifier keys only work well with common keys a-z. OS and/or browsers use other combinations for other purposes. For example, cmd + right is used as the shortcut to navigate 'forward' in some browsers.
  • Modifier keys are themself common keys. You can handle key event of single 'ctrl' key with handleKeys=['ctrl'];

Key set alias

Key set alias provide any easy way to specify common key sets. It is useful when you want to handle multiple keys and put all handling logic for each key inside the handler callback function.

<KeyboardEventHandler
    handleKeys={['numeric']}
    onKeyEvent={(key, e) => console.log('only handle number key events')} />

Alias Keys Description
'alphabetic' 'a', 'b', ...'z' 26 letter keys
'numeric' '0', '1', ....'9 10 number keys
'alphanumeric' 'a'...'z', '0'...'9' 36 alphanumeric keys
'function' 'f1'...'f19' 19 Fn keys
'all' n/a All keyboard events. If a key event does not match any common keys defined above, the key parameter to the callback function will have the value of 'other'. You can use the second parameter (the raw key event object) to implement you own key handling logic.

Note:

  1. Alias keys are aliases to a list of common keys. Expect the same behavior as if the respective array of common key names is in use.
  2. When a keyboard event matches, the first (key) parameter to the callback function will be the matched lowercase common key name. e.g. a for alias numeric.
  3. Alias key names do not work with modifiers. e.g. handleKeys=['ctrl+numeric'] // doesn't work
  4. You can mix alias with common keys. e.g. handleKeys=['numeric', 'a', 'enter', 'ctrl+b']

About exclusive handlers

For example, in an app with a list of products, you could have a handler for navigating (highlighting) the products with the up and down keys. Upon selecting (or hitting the 'enter' key on) a product, a modal pops up.

Within the modal is a list of options for the selected product. Another key handler can be used inside the modal using for navigating the options with the up and down keys, too.

However, the key handler for the product list should be first disabled (i.e. isDisabled={true}). Otherwise, the user will be navigating the product options in the modal and the product list in the background at the same time.

There could be other key handlers in your app, they all should be disabled to avoid unexpected results.

The isExclusive prop can be helpful in this situation. When a handler set to isExclusive, all other key handlers will be suspended.

In the above example, the key handler in the modal could set to be isExclusive. When the modal opens, all other handlers will be temporarily suspended. When the modal is closed/unmounted, they will be working again.

If more than one enabled handlers are isExclusive, the most recently mounted/assigned handler wins.

Technically, exclusive handlers are put into a stack upon mounted or when changed from non-exclusive to exclusive; Exclusive handlers are removed from the stack upon unmounted or disabled or changed to non-exclusive. The one left on the top of the stack is the one only exclusive handler.

About Higher Order Component

I believe this is not a good use case of HoC. I found it hard to come up with a meaningful use case for passing a keyboard event object or the relevant key to a component.

However, if you have a different view on this, please create an issue/request on GitHub.

Testing

Limitation

Unfortunately, there's no good way for testing keyboard events with Enzyme when using this react component.

Enzyme has two main limitations (ref: https://github.com/airbnb/enzyme/blob/master/docs/future.md):

  1. Event simulation is limited for Shallow rendering. But this component needs componentDidMount for registering keyboard events;

  2. Event propagation is not supported. However, Key events on wrapped components are bubbled up and handled by at the document level by this component.

Therefore, when testing with Enzyme:

  1. We can only simulate keyboard events fired from document.body;
  2. mount is required.
  3. There's no good way, if there's any, for testing/simulating key events from wrapped child component;

Example

  import simulateEvent from 'simulate-event';
  ...

  it('should be able to handle key events in case-insensitive way ', () => {
    const handleKeyEvent = Sinon.spy();
    render(<KeyboardEventHandler handleKeys={['ctRl + A']} onKeyEvent={handleKeyEvent} />);
    simulateEvent.simulate(document.body, 'keydown', { keyCode: 65, ctrlKey: true });
    expect(handleKeyEvent.calledWith('ctRl + A')).to.be.true;
  });

react-keyboard-event-handler's People

Contributors

brian-lim-42 avatar dlin-me avatar maxou44 avatar naturalethic avatar stancobridge avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

react-keyboard-event-handler's Issues

Unknown plugin "add-module-exports" specified in ......./react-keyboard-event-handler

import KeyboardEventHandler from 'react-keyboard-event-handler';

Causes an error

error: bundling failed: ReferenceError: Unknown plugin "add-module-exports" specified in "/Users/amaljose/ReactProjects/Hello360/node_modules/react-keyboard-event-handler/.babelrc" at 0, attempted to resolve relative to "/Users/amaljose/ReactProjects/Hello360/node_modules/react-keyboard-event-handler"
    at /Users/amaljose/ReactProjects/Hello360/node_modules/babel-core/lib/transformation/file/options/option-manager.js:180:17
    at Array.map (<anonymous>)
    at Function.normalisePlugins (/Users/amaljose/ReactProjects/Hello360/node_modules/babel-core/lib/transformation/file/options/option-manager.js:158:20)
    at OptionManager.mergeOptions (/Users/amaljose/ReactProjects/Hello360/node_modules/babel-core/lib/transformation/file/options/option-manager.js:234:36)
    at OptionManager.init (/Users/amaljose/ReactProjects/Hello360/node_modules/babel-core/lib/transformation/file/options/option-manager.js:368:12)
    at File.initOptions (/Users/amaljose/ReactProjects/Hello360/node_modules/babel-core/lib/transformation/file/index.js:212:65)
    at new File (/Users/amaljose/ReactProjects/Hello360/node_modules/babel-core/lib/transformation/file/index.js:135:24)
    at Pipeline.transform (/Users/amaljose/ReactProjects/Hello360/node_modules/babel-core/lib/transformation/pipeline.js:46:16)
    at Object.transform (/Users/amaljose/ReactProjects/Hello360/node_modules/metro-bundler/src/transformer.js:133:11)

no keyboard events with Material-UI's Modal/Dialog

For whatever reason react-keyboard-event-handler doesn't appear to work with https://material-ui.com/api/dialog/
It works outside of it but in a Dialog, nothing.

For anyone googling about the subject, on hindsight realised I was being dumb spending time debugging when all I needed was

  useEffect(() => {
    const handleKey = (e) => {
      if (e.key >= 1 && e.key < 10) setStars(Number(e.key))
    }
    window.addEventListener('keydown', handleKey)
    return () => window.removeEventListener('keydown', handleKey)
  },[])

(of course unmount the component to unregister listener instead of just hiding it)

Attempting to call a function that passes in variable

I am attempting to use the KeyboardEventHandler to modify the valueSelected prop of my component, but when I pass in the variable I'm using to set this prop, it mutates the variable into a function.

Here are the relevant lines of code:

valueSelected={selectedServerType}

<KeyboardEventHandler
handleKeys={['left', 'right']}
onKeyEvent={
(selectedServerType = key =>
this.handleKeyPress(key, selectedServerType))
}
/>

When selectedServerType first gets passed into handleKeyPress, and I console.log the value, it returns 'function selectedServerType()'

How can I pass the variable into handleKeyPress without it being mutated into a function?

typescript

Wonderful lib! How about add typescript support?

React 17 compatibility

Hi, could you please update this to work with React 17? I get this error when trying to install in my React 17 project with Node 15 / npm 7:

npm ERR! Could not resolve dependency:
npm ERR! peer react@"15.x || 16.x" from [email protected]

Thanks!

Catch "ctrl" + "+" keys

Hello,

I'm trying to catch the CTRL + + keys, I tried "ctrl++" but it don't work and create a Javascript error:

react-keyboard-event-handler.js:358 Uncaught TypeError: Cannot read property 'indexOf' of undefined
    at p (react-keyboard-event-handler.js:358)
    at react-keyboard-event-handler.js:261
    at Array.find (<anonymous>)
    at t.findMatchedKey (react-keyboard-event-handler.js:260)
    at t.value (react-keyboard-event-handler.js:183)

KeyboardEventHandler not calling function in test

Cross-posted with issue #5 (closed)

I'm trying to test a keyboard event. Here is the text of my test:

it('triggers change in environment with arrow keys', () => {
wrapper.find('t').simulate('keydown', { keyCode: 39 });
expect(defaultProps.setServerTypeHandler).toHaveBeenCalledWith('dedicated');
});

In this case, the function that contains the call to setServerTypeHandler, which is the function that gets called on the keyboard event, is not getting called in the test - I have checked this in the test coverage, and neither the onKeyEvent nor the function, handleKeyPress, are covered. Is there a reason for this?

Module parse failed: Unexpected token

I get the following error when compiling via babel (es2015, react):

ERROR in ./node_modules/react-keyboard-event-handler/src/KeyboardEventHandler.js 102:23
Module parse failed: Unexpected token (102:23)
You may need an appropriate loader to handle this file type.
|   render() {
|     const { children } = this.props;
>     return children ? (<span ref={ e => {
|         this.childrenContainer = e;
|       }}>{children}</span>) : null;

Warnings after upgrading to React 16.9: componentWillMount and componentWillReceiveProps renamed

After upgrading to React 16.9.0 I get the following two warnings:

Warning: componentWillMount has been renamed, and is not recommended for use. See https://fb.me/react-async-component-lifecycle-hooks for details.

and

Warning: componentWillReceiveProps has been renamed, and is not recommended for use. See https://fb.me/react-async-component-lifecycle-hooks for details.

The warning suggests to rename these methods to *_UNSAFE, but this is a workaround and I can imagine you might want a better solution?

down and up keys control the scrollbar on chrome

Love this library. I have a page that has long enough content to cause the scrollbar to appear on the browser. I have used your library to assign functionality to the up and down arrows. This functionality works but browser window still scrolls. is there any way to prevent the browser window from scrolling?

Function Keys No longer Working(F1 - F12)

Love the lib, currently I am heavily dependent on the function keys working with this library. In version 1.3.1. I had no issues whatsoever, it appears that keys outside of your alias are mapped to 'other' and should fall under the alias 'all' . However, as of 1.3.2 my keys are no longer being detected, stepping into the code, I can see alphanumeric and numeric are still registering, but no longer function keys F1 etc.. Please take a look when you can and let me know if something in my code needs to change to handle the update. Once again, thank you for a wonderful lib!

escape button event doesn't works in fullscreen mode of the browser.

made the following function to detect pressing of escape button when in fullscreen mode.

import KeyboardEventHandler from "react-keyboard-event-handler";
import React from "react";
const handleKeyboard = (key, e) => {
e.preventDefault();
if (key === "esc") {
console.log("esc");
}
};
const ComponentA = (props) => (


<KeyboardEventHandler
handleKeys={["all"]}
onKeyEvent={handleKeyboard}
/>

);

export default ComponentA;

Block browser shortcut

Is there a way to block browser capturing the keys, like ctrl+o or ctrl+s?
Try example in chrome and use ctrl+o.

Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.