diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..0ae7e5c --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +node_modules +/lib diff --git a/.eslintrc b/.eslintrc index d6fd9c4..bc29492 100644 --- a/.eslintrc +++ b/.eslintrc @@ -11,7 +11,6 @@ "node": true }, "rules": { - "array-bracket-spacing": 0, "arrow-body-style": 0, "comma-dangle": [ 2, "always-multiline" ], "consistent-return": 0, @@ -21,7 +20,6 @@ "global-require": 0, "import/default": 2, "import/export": 2, - "import/imports-first": 0, "import/named": 2, "import/namespace": 2, "import/no-extraneous-dependencies": [ @@ -36,24 +34,16 @@ } ], "import/prefer-default-export": 1, - "jsx-quotes": 0, - "new-cap": 0, - "max-len": 0, - "no-console": 0, - "no-fallthrough": 1, - "no-global-assign": 0, - "no-irregular-whitespace": [ - "error", { - "skipStrings": true, - "skipTemplates": true, - "skipRegExps": true + "no-console": [ + "error", + { + "allow": ["warn", "error", "info"] } ], "no-lonely-if": 0, "no-param-reassign": 0, "no-shadow": 1, "no-underscore-dangle": 0, - "no-unsafe-negation": 0, "no-unused-expressions": 0, "no-unused-vars": [ "warn", { @@ -76,11 +66,7 @@ "react/jsx-curly-spacing": 0, "react/jsx-no-bind": 0, "react/jsx-filename-extension": 0, - "semi": [ 2, "never" ], - }, - "globals": { - "require": false, - "ga": false + "react/forbid-prop-types": 0 }, "settings": { "import/ignore": [ @@ -92,6 +78,6 @@ "config": "webpack-js.config.js" } }, - "import/parser": "babel-eslint", + "import/parser": "babel-eslint" } -} \ No newline at end of file +} diff --git a/example/app.js b/example/App.js similarity index 73% rename from example/app.js rename to example/App.js index 673743c..c88d956 100644 --- a/example/app.js +++ b/example/App.js @@ -1,46 +1,45 @@ -import PropTypes from 'prop-types' +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; -let { Shortcuts } = require('../src') +let { Shortcuts } = require('../src'); -Shortcuts = React.createFactory(Shortcuts) -const { div, h1, p } = React.DOM +Shortcuts = React.createFactory(Shortcuts); +const { div, h1, p } = React.DOM; -export default React.createClass({ - displayName: 'App', +class App extends Component { + constructor(props, context) { + super(props, context); - childContextTypes: { - shortcuts: PropTypes.object.isRequired, - }, - - getInitialState() { - return { who: 'Nobody' } - }, + this.state = { + who: 'Nobody', + }; + } getChildContext() { - return { shortcuts: this.props.shortcuts } - }, + return { shortcuts: this.props.shortcuts }; + } _handleShortcuts(command) { switch (command) { - case 'MOVE_LEFT': return this.setState({ who: 'Hemingway - left' }) - case 'DELETE': return this.setState({ who: 'Hemingway - delete' }) - case 'MOVE_RIGHT': return this.setState({ who: 'Hemingway - right' }) - case 'MOVE_UP': return this.setState({ who: 'Hemingway - top' }) + case 'MOVE_LEFT': return this.setState({ who: 'Hemingway - left' }); + case 'DELETE': return this.setState({ who: 'Hemingway - delete' }); + case 'MOVE_RIGHT': return this.setState({ who: 'Hemingway - right' }); + case 'MOVE_UP': return this.setState({ who: 'Hemingway - top' }); } - }, + } _handleShortcuts2(command) { switch (command) { - case 'MOVE_LEFT': return this.setState({ who: 'Franz Kafka - left' }) - case 'DELETE': return this.setState({ who: 'Franz Kafka - delete' }) - case 'MOVE_RIGHT': return this.setState({ who: 'Franz Kafka - right' }) - case 'MOVE_UP': return this.setState({ who: 'Franz Kafka - top' }) + case 'MOVE_LEFT': return this.setState({ who: 'Franz Kafka - left' }); + case 'DELETE': return this.setState({ who: 'Franz Kafka - delete' }); + case 'MOVE_RIGHT': return this.setState({ who: 'Franz Kafka - right' }); + case 'MOVE_UP': return this.setState({ who: 'Franz Kafka - top' }); } - }, + } _handleRoot(command) { - this.setState({ who: 'Root shortcuts component' }) - }, + this.setState({ who: 'Root shortcuts component' }); + } render() { return ( @@ -75,6 +74,16 @@ export default React.createClass({ ) ) - ) - }, -}) + ); + } +} + +App.propTypes = { + shortcuts: PropTypes.object.isRequired, +}; + +App.childContextTypes = { + shortcuts: PropTypes.object.isRequired, +}; + +export default App; diff --git a/example/keymap.js b/example/keymap.js index 6db13aa..8781969 100644 --- a/example/keymap.js +++ b/example/keymap.js @@ -9,4 +9,4 @@ export default { linux: 'delete', }, }, -} +}; diff --git a/example/main.js b/example/main.js index 56bdb27..e2bb077 100644 --- a/example/main.js +++ b/example/main.js @@ -1,13 +1,17 @@ -import ReactDOM from 'react-dom' -import './main.less' -import keymap from './keymap' -import App from './app' -import { ShortcutManager } from '../src' +import React from 'react'; +import ReactDOM from 'react-dom'; -const shortcutManager = new ShortcutManager(keymap) +import './main.less'; + +import keymap from './keymap'; +import App from './App'; + +import { ShortcutManager } from '../src'; + +const shortcutManager = new ShortcutManager(keymap); // Just for testing -window.shortcutManager = shortcutManager +window.shortcutManager = shortcutManager; -const element = React.createElement(App, { shortcuts: shortcutManager }) -ReactDOM.render(element, document.getElementById('app')) +const element = React.createElement(App, { shortcuts: shortcutManager }); +ReactDOM.render(element, document.getElementById('app')); diff --git a/src/component/shortcuts.js b/src/component/Shortcuts.js similarity index 56% rename from src/component/shortcuts.js rename to src/component/Shortcuts.js index cca27f1..5a636b9 100644 --- a/src/component/shortcuts.js +++ b/src/component/Shortcuts.js @@ -1,65 +1,33 @@ -import React from 'react' -import invariant from 'invariant' -import Combokeys from 'combokeys' -import PropTypes from 'prop-types' +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; -import helpers from '../helpers' +import invariant from 'invariant'; +import Combokeys from 'combokeys'; -export default class extends React.Component { - static displayName = 'Shortcuts'; - - static contextTypes = { - shortcuts: PropTypes.object.isRequired, - }; - - static propTypes = { - children: PropTypes.node, - handler: PropTypes.func, - name: PropTypes.string, - tabIndex: PropTypes.number, - className: PropTypes.string, - eventType: PropTypes.string, - stopPropagation: PropTypes.bool, - preventDefault: PropTypes.bool, - targetNodeSelector: PropTypes.string, - global: PropTypes.bool, - isolate: PropTypes.bool, - alwaysFireHandler: PropTypes.bool, - }; - - static defaultProps = { - tabIndex: -1, - className: null, - eventType: null, - stopPropagation: true, - preventDefault: false, - targetNodeSelector: null, - global: false, - isolate: false, - alwaysFireHandler: false, - }; +import helpers from '../helpers'; +class Shortcuts extends Component { componentDidMount() { - this._onUpdate() + this._onUpdate(); if (this.props.name) { - this.context.shortcuts.addUpdateListener(this._onUpdate) + this.context.shortcuts.addUpdateListener(this._onUpdate); } } componentWillUnmount() { - this._unbindShortcuts() + this._unbindShortcuts(); if (this.props.name) { - this.context.shortcuts.removeUpdateListener(this._onUpdate) + this.context.shortcuts.removeUpdateListener(this._onUpdate); } if (this.props.global) { - const element = this._getElementToBind() + const element = this._getElementToBind(); element.removeEventListener( 'shortcuts:global', this._customGlobalHandler - ) + ); } } @@ -69,37 +37,37 @@ export default class extends React.Component { _lastEvent = null; _bindShortcuts = (shortcutsArr) => { - const element = this._getElementToBind() - element.setAttribute('tabindex', this.props.tabIndex) - this._combokeys = new Combokeys(element) - this._decorateCombokeys() + const element = this._getElementToBind(); + element.setAttribute('tabindex', this.props.tabIndex); + this._combokeys = new Combokeys(element); + this._decorateCombokeys(); this._combokeys.bind( shortcutsArr, this._handleShortcuts, this.props.eventType - ) + ); if (this.props.global) { - element.addEventListener('shortcuts:global', this._customGlobalHandler) + element.addEventListener('shortcuts:global', this._customGlobalHandler); } }; _customGlobalHandler = (e) => { - const { character, modifiers, event } = e.detail + const { character, modifiers, event } = e.detail; - let targetNode = null + let targetNode = null; if (this.props.targetNodeSelector) { - targetNode = document.querySelector(this.props.targetNodeSelector) + targetNode = document.querySelector(this.props.targetNodeSelector); } if (e.target !== this._domNode && e.target !== targetNode) { - this._combokeys.handleKey(character, modifiers, event, true) + this._combokeys.handleKey(character, modifiers, event, true); } }; _decorateCombokeys = () => { - const element = this._getElementToBind() - const originalHandleKey = this._combokeys.handleKey.bind(this._combokeys) + const element = this._getElementToBind(); + const originalHandleKey = this._combokeys.handleKey.bind(this._combokeys); // NOTE: stopCallback is a method that is called to see // if the keyboard event should fire @@ -107,23 +75,17 @@ export default class extends React.Component { const isInputLikeElement = domElement.tagName === 'INPUT' || domElement.tagName === 'SELECT' || domElement.tagName === 'TEXTAREA' || - (domElement.contentEditable && domElement.contentEditable === 'true') + (domElement.contentEditable && domElement.contentEditable === 'true'); - let isReturnString + let isReturnString; if (event.key) { - isReturnString = event.key.length === 1 + isReturnString = event.key.length === 1; } else { - isReturnString = Boolean(helpers.getCharacter(event)) + isReturnString = Boolean(helpers.getCharacter(event)); } - if ( - isInputLikeElement && isReturnString && !this.props.alwaysFireHandler - ) { - return true - } - - return false - } + return !!(isInputLikeElement && isReturnString && !this.props.alwaysFireHandler); + }; this._combokeys.handleKey = ( character, @@ -136,14 +98,14 @@ export default class extends React.Component { event.timeStamp === this._lastEvent.timeStamp && event.type === this._lastEvent.type ) { - return + return; } - this._lastEvent = event + this._lastEvent = event; - let isolateOwner = false + let isolateOwner = false; if (this.props.isolate && !event.__isolateShortcuts) { - event.__isolateShortcuts = true - isolateOwner = true + event.__isolateShortcuts = true; + isolateOwner = true; } if (!isGlobalHandler) { @@ -153,55 +115,55 @@ export default class extends React.Component { bubbles: true, cancelable: true, }) - ) + ); } // NOTE: works normally if it's not an isolated event if (!event.__isolateShortcuts) { if (this.props.preventDefault) { - event.preventDefault() + event.preventDefault(); } if (this.props.stopPropagation && !isGlobalHandler) { - event.stopPropagation() + event.stopPropagation(); } - originalHandleKey(character, modifiers, event) - return + originalHandleKey(character, modifiers, event); + return; } // NOTE: global shortcuts should work even for an isolated event if (this.props.global || isolateOwner) { - originalHandleKey(character, modifiers, event) + originalHandleKey(character, modifiers, event); } - } + }; }; _getElementToBind = () => { - let element = null + let element = null; if (this.props.targetNodeSelector) { - element = document.querySelector(this.props.targetNodeSelector) + element = document.querySelector(this.props.targetNodeSelector); invariant( element, `Node selector '${this.props.targetNodeSelector}' was not found.` - ) + ); } else { - element = this._domNode + element = this._domNode; } - return element + return element; }; _unbindShortcuts = () => { if (this._combokeys) { - this._combokeys.detach() - this._combokeys.reset() + this._combokeys.detach(); + this._combokeys.reset(); } }; _onUpdate = () => { const shortcutsArr = this.props.name && - this.context.shortcuts.getShortcuts(this.props.name) - this._unbindShortcuts() - this._bindShortcuts(shortcutsArr || []) + this.context.shortcuts.getShortcuts(this.props.name); + this._unbindShortcuts(); + this._bindShortcuts(shortcutsArr || []); }; _handleShortcuts = (event, keyName) => { @@ -209,10 +171,10 @@ export default class extends React.Component { const shortcutName = this.context.shortcuts.findShortcutName( keyName, this.props.name - ) + ); if (this.props.handler) { - this.props.handler(shortcutName, event) + this.props.handler(shortcutName, event); } } }; @@ -221,13 +183,46 @@ export default class extends React.Component { return (
{ - this._domNode = node + this._domNode = node; }} tabIndex={this.props.tabIndex} className={this.props.className} > {this.props.children}
- ) + ); } } + +Shortcuts.contextTypes = { + shortcuts: PropTypes.object.isRequired, +}; + +Shortcuts.propTypes = { + children: PropTypes.node, + handler: PropTypes.func, + name: PropTypes.string, + tabIndex: PropTypes.number, + className: PropTypes.string, + eventType: PropTypes.string, + stopPropagation: PropTypes.bool, + preventDefault: PropTypes.bool, + targetNodeSelector: PropTypes.string, + global: PropTypes.bool, + isolate: PropTypes.bool, + alwaysFireHandler: PropTypes.bool, +}; + +Shortcuts.defaultProps = { + tabIndex: -1, + className: null, + eventType: null, + stopPropagation: true, + preventDefault: false, + targetNodeSelector: null, + global: false, + isolate: false, + alwaysFireHandler: false, +}; + +export default Shortcuts; diff --git a/src/component/index.js b/src/component/index.js index a3d3fc0..c0c281a 100644 --- a/src/component/index.js +++ b/src/component/index.js @@ -1 +1 @@ -module.exports = require('./shortcuts') +module.exports = require('./Shortcuts'); diff --git a/src/helpers.js b/src/helpers.js index a647b25..7ec89fa 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -1,29 +1,29 @@ -import platform from 'platform' +import platform from 'platform'; const getPlatformName = () => { - let os = platform.os.family || '' - os = os.toLowerCase().replace(/ /g, '') + let os = platform.os.family || ''; + os = os.toLowerCase().replace(/ /g, ''); if (/\bwin/.test(os)) { - os = 'windows' + os = 'windows'; } else if (/darwin|osx/.test(os)) { - os = 'osx' + os = 'osx'; } else if (/linux|freebsd|sunos|ubuntu|debian|fedora|redhat|suse/.test(os)) { - os = 'linux' + os = 'linux'; } else { - os = 'other' + os = 'other'; } - return os -} + return os; +}; const getCharacter = (event) => { if (event.which == null) { // NOTE: IE - return String.fromCharCode(event.keyCode) + return String.fromCharCode(event.keyCode); } else if (event.which !== 0 && event.charCode !== 0) { // NOTE: the rest - return String.fromCharCode(event.which) + return String.fromCharCode(event.which); } - return null -} + return null; +}; -export default { getPlatformName, getCharacter } +export default { getPlatformName, getCharacter }; diff --git a/src/index.js b/src/index.js index dfeb718..91125e4 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,4 @@ module.exports = { ShortcutManager: require('./shortcut-manager'), Shortcuts: require('./component/'), -} +}; diff --git a/src/shortcut-manager.js b/src/shortcut-manager.js index d2a2e86..f240728 100644 --- a/src/shortcut-manager.js +++ b/src/shortcut-manager.js @@ -1,59 +1,59 @@ -import reduce from 'just-reduce-object' -import invariant from 'invariant' -import { EventEmitter } from 'events' -import helpers from './helpers' -import { isPlainObject, findKey, isArray, map, compact, flatten } from './utils' +import reduce from 'just-reduce-object'; +import invariant from 'invariant'; +import { EventEmitter } from 'events'; +import helpers from './helpers'; +import { isPlainObject, findKey, isArray, map, compact, flatten } from './utils'; const warning = (text) => { if (process && process.env.NODE_ENV !== 'production') { - console.warn(text) + console.warn(text); } -} +}; class ShortcutManager extends EventEmitter { static CHANGE_EVENT = 'shortcuts:update' constructor(keymap = {}) { - super() - this._keymap = keymap + super(); + this._keymap = keymap; } addUpdateListener(callback) { invariant(callback, - 'addUpdateListener: callback argument is not defined or falsy') - this.on(ShortcutManager.CHANGE_EVENT, callback) + 'addUpdateListener: callback argument is not defined or falsy'); + this.on(ShortcutManager.CHANGE_EVENT, callback); } removeUpdateListener(callback) { - this.removeListener(ShortcutManager.CHANGE_EVENT, callback) + this.removeListener(ShortcutManager.CHANGE_EVENT, callback); } _platformName = helpers.getPlatformName() _parseShortcutDescriptor = (item) => { if (isPlainObject(item)) { - return item[this._platformName] + return item[this._platformName]; } - return item + return item; } setKeymap(keymap) { invariant(keymap, - 'setKeymap: keymap argument is not defined or falsy.') - this._keymap = keymap - this.emit(ShortcutManager.CHANGE_EVENT) + 'setKeymap: keymap argument is not defined or falsy.'); + this._keymap = keymap; + this.emit(ShortcutManager.CHANGE_EVENT); } extendKeymap(keymap) { invariant(keymap, - 'extendKeymap: keymap argument is not defined or falsy.') - this._keymap = Object.assign({}, this._keymap, keymap) - this.emit(ShortcutManager.CHANGE_EVENT) + 'extendKeymap: keymap argument is not defined or falsy.'); + this._keymap = Object.assign({}, this._keymap, keymap); + this.emit(ShortcutManager.CHANGE_EVENT); } getAllShortcuts() { - return this._keymap + return this._keymap; } getAllShortcutsForPlatform(platformName) { @@ -61,67 +61,67 @@ class ShortcutManager extends EventEmitter { return reduce(shortcuts, (result, keyName, keyValue) => { if (isPlainObject(keyValue)) { if (keyValue[platformName]) { - keyValue = keyValue[platformName] + keyValue = keyValue[platformName]; } else { - result[keyName] = _transformShortcuts(keyValue) - return result + result[keyName] = _transformShortcuts(keyValue); + return result; } } - result[keyName] = keyValue - return result - }, {}) - } + result[keyName] = keyValue; + return result; + }, {}); + }; - return _transformShortcuts(this._keymap) + return _transformShortcuts(this._keymap); } getAllShortcutsForCurrentPlatform() { - return this.getAllShortcutsForPlatform(this._platformName) + return this.getAllShortcutsForPlatform(this._platformName); } getShortcuts(componentName) { invariant(componentName, - 'getShortcuts: name argument is not defined or falsy.') + 'getShortcuts: name argument is not defined or falsy.'); - const cursor = this._keymap[componentName] + const cursor = this._keymap[componentName]; if (!cursor) { - warning(`getShortcuts: There are no shortcuts with name ${componentName}.`) - return + warning(`getShortcuts: There are no shortcuts with name ${componentName}.`); + return; } - const shortcuts = compact(flatten(map(cursor, this._parseShortcutDescriptor))) + const shortcuts = compact(flatten(map(cursor, this._parseShortcutDescriptor))); - return shortcuts + return shortcuts; } _parseShortcutKeyName(obj, keyName) { const result = findKey(obj, (item) => { if (isPlainObject(item)) { - item = item[this._platformName] + item = item[this._platformName]; } if (isArray(item)) { - const index = item.indexOf(keyName) - if (index >= 0) { item = item[index] } + const index = item.indexOf(keyName); + if (index >= 0) { item = item[index]; } } - return item === keyName - }) + return item === keyName; + }); - return result + return result; } findShortcutName(keyName, componentName) { invariant(keyName, - 'findShortcutName: keyName argument is not defined or falsy.') + 'findShortcutName: keyName argument is not defined or falsy.'); invariant(componentName, - 'findShortcutName: componentName argument is not defined or falsy.') + 'findShortcutName: componentName argument is not defined or falsy.'); - const cursor = this._keymap[componentName] - const result = this._parseShortcutKeyName(cursor, keyName) + const cursor = this._keymap[componentName]; + const result = this._parseShortcutKeyName(cursor, keyName); - return result + return result; } } -export default ShortcutManager +export default ShortcutManager; diff --git a/src/utils.js b/src/utils.js index c078ab4..3715e44 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,49 +1,49 @@ -export const isArray = arr => Array.isArray(arr) +export const isArray = arr => Array.isArray(arr); export const isPlainObject = (obj) => { - const isObject = typeof obj === 'object' && obj !== null && !isArray(obj) - if (!isObject || (obj.toString && obj.toString() !== '[object Object]')) return false - const proto = Object.getPrototypeOf(obj) + const isObject = typeof obj === 'object' && obj !== null && !isArray(obj); + if (!isObject || (obj.toString && obj.toString() !== '[object Object]')) return false; + const proto = Object.getPrototypeOf(obj); if (proto === null) { - return true + return true; } - const Ctor = Object.prototype.hasOwnProperty.call(proto, 'constructor') && proto.constructor + const Ctor = Object.prototype.hasOwnProperty.call(proto, 'constructor') && proto.constructor; return typeof Ctor === 'function' && Ctor instanceof Ctor && - Function.prototype.toString.call(Ctor) === Function.prototype.toString.call(Object) -} + Function.prototype.toString.call(Ctor) === Function.prototype.toString.call(Object); +}; export const findKey = (obj, fn) => { - if (!isPlainObject(obj) && !isArray(obj)) return + if (!isPlainObject(obj) && !isArray(obj)) return; - const keys = Object.keys(obj) - return keys.find(key => fn(obj[key])) -} + const keys = Object.keys(obj); + return keys.find(key => fn(obj[key])); +}; -export const compact = arr => arr.filter(Boolean) +export const compact = arr => arr.filter(Boolean); const flattenOnce = (arr, recurse = true) => { return arr.reduce((acc, val) => { - if (isArray(val) && recurse) return acc.concat(flattenOnce(val, false)) - acc.push(val) - return acc - }, []) -} + if (isArray(val) && recurse) return acc.concat(flattenOnce(val, false)); + acc.push(val); + return acc; + }, []); +}; export const flatten = (arr) => { - if (!isArray(arr)) throw new Error('flatten expects an array') - return flattenOnce(arr) -} + if (!isArray(arr)) throw new Error('flatten expects an array'); + return flattenOnce(arr); +}; export const map = (itr, fn) => { - if (isArray(itr)) return itr.map(fn) + if (isArray(itr)) return itr.map(fn); - const results = [] - const keys = Object.keys(itr) - const len = keys.length + const results = []; + const keys = Object.keys(itr); + const len = keys.length; for (let i = 0; i < len; i += 1) { - const key = keys[i] - results.push(fn(itr[key], key)) + const key = keys[i]; + results.push(fn(itr[key], key)); } - return results -} + return results; +}; diff --git a/test/shortcuts.spec.js b/test/Shortcuts.spec.js similarity index 57% rename from test/shortcuts.spec.js rename to test/Shortcuts.spec.js index a4bcd49..52c1074 100644 --- a/test/shortcuts.spec.js +++ b/test/Shortcuts.spec.js @@ -1,443 +1,442 @@ -import jsdom from 'jsdom' -import chai from 'chai' -import sinonChai from 'sinon-chai' -import sinon from 'sinon' -import _ from 'lodash' +import jsdom from 'jsdom'; +import chai from 'chai'; +import sinonChai from 'sinon-chai'; +import sinon from 'sinon'; +import _ from 'lodash'; -import keymap from './keymap' +import keymap from './keymap'; describe('Shortcuts component', () => { - let baseProps = null - let baseContext = null + let baseProps = null; + let baseContext = null; - let simulant = null - let ShortcutManager = null - let Shortcuts = null - let ReactDOM = null - let React = null - let enzyme = null + let simulant = null; + let ShortcutManager = null; + let Shortcuts = null; + let ReactDOM = null; + let React = null; + let enzyme = null; - chai.use(sinonChai) - const { expect } = chai + chai.use(sinonChai); + const { expect } = chai; beforeEach(() => { - global.document = jsdom.jsdom('') - global.window = document.defaultView - global.Image = window.Image - global.navigator = window.navigator - global.CustomEvent = window.CustomEvent - simulant = require('simulant') - ReactDOM = require('react-dom') - React = require('react') - enzyme = require('enzyme') - const chaiEnzyme = require('chai-enzyme') + global.document = jsdom.jsdom(''); + global.window = document.defaultView; + global.Image = window.Image; + global.navigator = window.navigator; + global.CustomEvent = window.CustomEvent; + simulant = require('simulant'); + ReactDOM = require('react-dom'); + React = require('react'); + enzyme = require('enzyme'); + const chaiEnzyme = require('chai-enzyme'); - chai.use(chaiEnzyme()) + chai.use(chaiEnzyme()); - ShortcutManager = require('../src').ShortcutManager - const shortcutsManager = new ShortcutManager(keymap) + ShortcutManager = require('../src').ShortcutManager; + const shortcutsManager = new ShortcutManager(keymap); - Shortcuts = require('../src/').Shortcuts + Shortcuts = require('../src/').Shortcuts; baseProps = { handler: sinon.spy(), name: 'TESTING', className: null, - } - baseContext = { shortcuts: shortcutsManager } - }) + }; + baseContext = { shortcuts: shortcutsManager }; + }); it('should render component', () => { - const shortcutComponent = React.createElement(Shortcuts, baseProps) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const shortcutComponent = React.createElement(Shortcuts, baseProps); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper.find('div')).to.have.length(1) - }) + expect(wrapper.find('div')).to.have.length(1); + }); it('should have a tabIndex of -1 by default', () => { - let shortcutComponent = React.createElement(Shortcuts, baseProps) - let wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + let shortcutComponent = React.createElement(Shortcuts, baseProps); + let wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper.props().tabIndex).to.be.equal(-1) + expect(wrapper.props().tabIndex).to.be.equal(-1); - let props = _.assign({}, baseProps, { tabIndex: 42 }) - shortcutComponent = React.createElement(Shortcuts, props) - wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const props = _.assign({}, baseProps, { tabIndex: 42 }); + shortcutComponent = React.createElement(Shortcuts, props); + wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper.props().tabIndex).to.be.equal(props.tabIndex) - let realTabIndex = ReactDOM.findDOMNode(wrapper.instance()).getAttribute('tabindex') - expect(realTabIndex).to.have.equal(String(props.tabIndex)) + expect(wrapper.props().tabIndex).to.be.equal(props.tabIndex); + let realTabIndex = ReactDOM.findDOMNode(wrapper.instance()).getAttribute('tabindex'); + expect(realTabIndex).to.have.equal(String(props.tabIndex)); - props.tabIndex = 0 - shortcutComponent = React.createElement(Shortcuts, props) - wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + props.tabIndex = 0; + shortcutComponent = React.createElement(Shortcuts, props); + wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper.props().tabIndex).to.be.equal(props.tabIndex) - realTabIndex = ReactDOM.findDOMNode(wrapper.instance()).getAttribute('tabindex') - expect(realTabIndex).to.have.equal(String(props.tabIndex)) - }) + expect(wrapper.props().tabIndex).to.be.equal(props.tabIndex); + realTabIndex = ReactDOM.findDOMNode(wrapper.instance()).getAttribute('tabindex'); + expect(realTabIndex).to.have.equal(String(props.tabIndex)); + }); it('should not have className by default', () => { - const shortcutComponent = React.createElement(Shortcuts, baseProps) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const shortcutComponent = React.createElement(Shortcuts, baseProps); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper.props().className).to.be.equal(null) - }) + expect(wrapper.props().className).to.be.equal(null); + }); it('should have className', () => { - const props = _.assign({}, baseProps, { className: 'testing' }) - const shortcutComponent = React.createElement(Shortcuts, props) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const props = _.assign({}, baseProps, { className: 'testing' }); + const shortcutComponent = React.createElement(Shortcuts, props); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper.props().className).to.be.equal('testing') - expect(wrapper).to.have.className('testing') - }) + expect(wrapper.props().className).to.be.equal('testing'); + expect(wrapper).to.have.className('testing'); + }); it('should have isolate prop set to false by default', () => { - const shortcutComponent = React.createElement(Shortcuts, baseProps) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const shortcutComponent = React.createElement(Shortcuts, baseProps); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper.props().isolate).to.be.equal(false) - }) + expect(wrapper.props().isolate).to.be.equal(false); + }); it('should have isolate prop', () => { - const props = _.assign({}, baseProps, { isolate: true }) - const shortcutComponent = React.createElement(Shortcuts, props) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const props = _.assign({}, baseProps, { isolate: true }); + const shortcutComponent = React.createElement(Shortcuts, props); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper.props().isolate).to.be.equal(true) - }) + expect(wrapper.props().isolate).to.be.equal(true); + }); it('should not have children by default', () => { - const shortcutComponent = React.createElement(Shortcuts, baseProps) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const shortcutComponent = React.createElement(Shortcuts, baseProps); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper.props().children).to.be.equal(undefined) - }) + expect(wrapper.props().children).to.be.equal(undefined); + }); it('should have children', () => { - const props = _.assign({}, baseProps, { children: React.DOM.div() }) - const shortcutComponent = React.createElement(Shortcuts, props) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const props = _.assign({}, baseProps, { children: React.DOM.div() }); + const shortcutComponent = React.createElement(Shortcuts, props); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper).to.contain(React.DOM.div()) - }) + expect(wrapper).to.contain(React.DOM.div()); + }); it('should have handler prop', () => { - const shortcutComponent = React.createElement(Shortcuts, baseProps) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const shortcutComponent = React.createElement(Shortcuts, baseProps); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper.props().handler).to.be.function - }) + expect(wrapper.props().handler).to.be.function; + }); it('should have name prop', () => { const props = _.assign({}, baseProps, - {name: 'TESTING'}) - const shortcutComponent = React.createElement(Shortcuts, props) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + { name: 'TESTING' }); + const shortcutComponent = React.createElement(Shortcuts, props); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper.props().name).to.be.equal('TESTING') - }) + expect(wrapper.props().name).to.be.equal('TESTING'); + }); it('should not have eventType prop by default', () => { - const shortcutComponent = React.createElement(Shortcuts, baseProps) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const shortcutComponent = React.createElement(Shortcuts, baseProps); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper.props().eventType).to.be.equal(null) - }) + expect(wrapper.props().eventType).to.be.equal(null); + }); it('should have eventType prop', () => { - const props = _.assign({}, baseProps, { eventType: 'keyUp' }) - const shortcutComponent = React.createElement(Shortcuts, props) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const props = _.assign({}, baseProps, { eventType: 'keyUp' }); + const shortcutComponent = React.createElement(Shortcuts, props); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper.props().eventType).to.be.equal('keyUp') - }) + expect(wrapper.props().eventType).to.be.equal('keyUp'); + }); it('should have stopPropagation prop by default', () => { - const shortcutComponent = React.createElement(Shortcuts, baseProps) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const shortcutComponent = React.createElement(Shortcuts, baseProps); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper.props().stopPropagation).to.be.equal(true) - }) + expect(wrapper.props().stopPropagation).to.be.equal(true); + }); it('should have stopPropagation prop set to false', () => { - const props = _.assign({}, baseProps, { stopPropagation: false }) - const shortcutComponent = React.createElement(Shortcuts, props) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const props = _.assign({}, baseProps, { stopPropagation: false }); + const shortcutComponent = React.createElement(Shortcuts, props); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper.props().stopPropagation).to.be.equal(false) - }) + expect(wrapper.props().stopPropagation).to.be.equal(false); + }); it('should have preventDefault prop set to false by default', () => { - const shortcutComponent = React.createElement(Shortcuts, baseProps) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const shortcutComponent = React.createElement(Shortcuts, baseProps); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper.props().preventDefault).to.be.equal(false) - }) + expect(wrapper.props().preventDefault).to.be.equal(false); + }); it('should have preventDefault prop set to true', () => { - const props = _.assign({}, baseProps, { preventDefault: true }) - const shortcutComponent = React.createElement(Shortcuts, props) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const props = _.assign({}, baseProps, { preventDefault: true }); + const shortcutComponent = React.createElement(Shortcuts, props); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper.props().preventDefault).to.be.equal(true) - }) + expect(wrapper.props().preventDefault).to.be.equal(true); + }); it('should not have targetNodeSelector prop by default', () => { - const shortcutComponent = React.createElement(Shortcuts, baseProps) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const shortcutComponent = React.createElement(Shortcuts, baseProps); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper.props().targetNodeSelector).to.be.equal(null) - }) + expect(wrapper.props().targetNodeSelector).to.be.equal(null); + }); it('should have targetNode prop', () => { - const props = _.assign({}, baseProps, { targetNodeSelector: 'body' }) - const shortcutComponent = React.createElement(Shortcuts, props) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const props = _.assign({}, baseProps, { targetNodeSelector: 'body' }); + const shortcutComponent = React.createElement(Shortcuts, props); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper.props().targetNodeSelector).to.be.equal('body') - }) + expect(wrapper.props().targetNodeSelector).to.be.equal('body'); + }); it('should have global prop set to false by default', () => { - const shortcutComponent = React.createElement(Shortcuts, baseProps) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const shortcutComponent = React.createElement(Shortcuts, baseProps); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper.props().global).to.be.equal(false) - }) + expect(wrapper.props().global).to.be.equal(false); + }); it('should have global prop set to true', () => { - const props = _.assign({}, baseProps, { global: true }) - const shortcutComponent = React.createElement(Shortcuts, props) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const props = _.assign({}, baseProps, { global: true }); + const shortcutComponent = React.createElement(Shortcuts, props); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - expect(wrapper.props().global).to.be.equal(true) - }) + expect(wrapper.props().global).to.be.equal(true); + }); it('should fire the handler prop with the correct argument', () => { - const shortcutComponent = React.createElement(Shortcuts, baseProps) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const shortcutComponent = React.createElement(Shortcuts, baseProps); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - const node = ReactDOM.findDOMNode(wrapper.instance()) - node.focus() + const node = ReactDOM.findDOMNode(wrapper.instance()); + node.focus(); - const enter = 13 - simulant.fire(node, 'keydown', { keyCode: enter }) + const enter = 13; + simulant.fire(node, 'keydown', { keyCode: enter }); - expect(wrapper.props().handler).to.have.been.calledWith('OPEN') + expect(wrapper.props().handler).to.have.been.calledWith('OPEN'); - const esc = 27 - simulant.fire(node, 'keydown', { keyCode: esc }) + const esc = 27; + simulant.fire(node, 'keydown', { keyCode: esc }); - expect(wrapper.props().handler).to.have.been.calledWith('CLOSE') - }) + expect(wrapper.props().handler).to.have.been.calledWith('CLOSE'); + }); it('should not fire the handler', () => { - const props = _.assign({}, baseProps, { name: 'NON-EXISTING' }) - const shortcutComponent = React.createElement(Shortcuts, props) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const props = _.assign({}, baseProps, { name: 'NON-EXISTING' }); + const shortcutComponent = React.createElement(Shortcuts, props); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - const node = ReactDOM.findDOMNode(wrapper.instance()) - node.focus() + const node = ReactDOM.findDOMNode(wrapper.instance()); + node.focus(); - const enter = 13 - simulant.fire(node, 'keydown', { keyCode: enter }) + const enter = 13; + simulant.fire(node, 'keydown', { keyCode: enter }); - expect(wrapper.props().handler).to.not.have.been.called - }) + expect(wrapper.props().handler).to.not.have.been.called; + }); it('should not fire twice when global prop is truthy', () => { - const props = _.assign({}, baseProps, { global: true }) - const shortcutComponent = React.createElement(Shortcuts, props) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const props = _.assign({}, baseProps, { global: true }); + const shortcutComponent = React.createElement(Shortcuts, props); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - const node = ReactDOM.findDOMNode(wrapper.instance()) - node.focus() + const node = ReactDOM.findDOMNode(wrapper.instance()); + node.focus(); - const enter = 13 - simulant.fire(node, 'keydown', { keyCode: enter }) + const enter = 13; + simulant.fire(node, 'keydown', { keyCode: enter }); - expect(wrapper.props().handler).to.have.been.calledOnce - }) + expect(wrapper.props().handler).to.have.been.calledOnce; + }); it('should not fire when the component has been unmounted', () => { - const shortcutComponent = React.createElement(Shortcuts, baseProps) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const shortcutComponent = React.createElement(Shortcuts, baseProps); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - const node = ReactDOM.findDOMNode(wrapper.instance()) - node.focus() + const node = ReactDOM.findDOMNode(wrapper.instance()); + node.focus(); - wrapper.unmount() + wrapper.unmount(); - const enter = 13 - simulant.fire(node, 'keydown', { keyCode: enter }) + const enter = 13; + simulant.fire(node, 'keydown', { keyCode: enter }); - expect(wrapper.props().handler).to.not.have.been.called - }) + expect(wrapper.props().handler).to.not.have.been.called; + }); it('should update the shortcuts and fire the handler', () => { - const shortcutComponent = React.createElement(Shortcuts, baseProps) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const shortcutComponent = React.createElement(Shortcuts, baseProps); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - const node = ReactDOM.findDOMNode(wrapper.instance()) - node.focus() + const node = ReactDOM.findDOMNode(wrapper.instance()); + node.focus(); - const space = 32 - simulant.fire(node, 'keydown', { keyCode: space }) + const space = 32; + simulant.fire(node, 'keydown', { keyCode: space }); - expect(wrapper.props().handler).to.not.have.been.called + expect(wrapper.props().handler).to.not.have.been.called; const editedKeymap = _.assign({}, keymap, { 'TESTING': { 'SPACE': 'space', }, } - ) - baseContext.shortcuts.setKeymap(editedKeymap) + ); + baseContext.shortcuts.setKeymap(editedKeymap); - simulant.fire(node, 'keydown', { keyCode: space }) + simulant.fire(node, 'keydown', { keyCode: space }); - expect(baseProps.handler).to.have.been.called + expect(baseProps.handler).to.have.been.called; // NOTE: rollback the previous keymap - baseContext.shortcuts.setKeymap(keymap) - }) + baseContext.shortcuts.setKeymap(keymap); + }); it('should fire the handler from a child input', () => { const props = _.assign({}, baseProps, { children: React.DOM.input({ type: 'text', className: 'input' }), - }) - const shortcutComponent = React.createElement(Shortcuts, props) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + }); + const shortcutComponent = React.createElement(Shortcuts, props); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - const parentNode = ReactDOM.findDOMNode(wrapper.instance()) - const node = parentNode.querySelector('.input') - node.focus() + const parentNode = ReactDOM.findDOMNode(wrapper.instance()); + const node = parentNode.querySelector('.input'); + node.focus(); - const enter = 13 - simulant.fire(node, 'keydown', { keyCode: enter, key: 'Enter' }) + const enter = 13; + simulant.fire(node, 'keydown', { keyCode: enter, key: 'Enter' }); - expect(wrapper.props().handler).to.have.been.called - }) + expect(wrapper.props().handler).to.have.been.called; + }); it('should fire the handler when using targetNodeSelector', () => { - const props = _.assign({}, baseProps, { targetNodeSelector: 'body' }) - const shortcutComponent = React.createElement(Shortcuts, props) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const props = _.assign({}, baseProps, { targetNodeSelector: 'body' }); + const shortcutComponent = React.createElement(Shortcuts, props); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - const enter = 13 - simulant.fire(document.body, 'keydown', { keyCode: enter, key: 'Enter' }) + const enter = 13; + simulant.fire(document.body, 'keydown', { keyCode: enter, key: 'Enter' }); - expect(wrapper.props().handler).to.have.been.called - }) + expect(wrapper.props().handler).to.have.been.called; + }); it('should throw and error if targetNodeSelector is not found', () => { - const props = _.assign({}, baseProps, { targetNodeSelector: 'non-existing' }) - const shortcutComponent = React.createElement(Shortcuts, props) + const props = _.assign({}, baseProps, { targetNodeSelector: 'non-existing' }); + const shortcutComponent = React.createElement(Shortcuts, props); try { - enzyme.mount(shortcutComponent, { context: baseContext }) + enzyme.mount(shortcutComponent, { context: baseContext }); } catch (err) { - expect(err).to.match(/Node selector 'non-existing' was not found/) + expect(err).to.match(/Node selector 'non-existing' {2}was not found/); } - }) + }); it('should fire the handler from focused input', () => { - const props = _.assign({}, baseProps, { - alwaysFireHandler: true, - children: React.DOM.input({type: 'text', className: 'input'}) - }) - const shortcutComponent = React.createElement(Shortcuts, props) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const props = _.assign({}, baseProps, { + alwaysFireHandler: true, + children: React.DOM.input({ type: 'text', className: 'input' }), + }); + const shortcutComponent = React.createElement(Shortcuts, props); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - const parentNode = ReactDOM.findDOMNode(wrapper.instance()) - const node = parentNode.querySelector('.input') - node.focus() + const parentNode = ReactDOM.findDOMNode(wrapper.instance()); + const node = parentNode.querySelector('.input'); + node.focus(); - const enter = 13 - simulant.fire(node, 'keydown', { keyCode: enter }) + const enter = 13; + simulant.fire(node, 'keydown', { keyCode: enter }); - expect(wrapper.props().handler).to.have.been.called - }) + expect(wrapper.props().handler).to.have.been.called; + }); describe('Shortcuts component inside Shortcuts component:', () => { - it('should not fire parent handler when child handler is fired', () => { const props = _.assign({}, baseProps, { children: React.createElement(Shortcuts, _.assign({}, baseProps, { className: 'test' })), - }) - const shortcutComponent = React.createElement(Shortcuts, props) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + }); + const shortcutComponent = React.createElement(Shortcuts, props); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - const parentNode = ReactDOM.findDOMNode(wrapper.instance()) - const node = parentNode.querySelector('.test') + const parentNode = ReactDOM.findDOMNode(wrapper.instance()); + const node = parentNode.querySelector('.test'); - node.focus() + node.focus(); - const enter = 13 - simulant.fire(node, 'keydown', { keyCode: enter }) + const enter = 13; + simulant.fire(node, 'keydown', { keyCode: enter }); - expect(baseProps.handler).to.have.been.calledOnce - }) + expect(baseProps.handler).to.have.been.calledOnce; + }); it('should fire parent handler when child handler is fired', () => { const props = _.assign({}, baseProps, { children: React.createElement(Shortcuts, _.assign({}, baseProps, { className: 'test', stopPropagation: false })), - }) - const shortcutComponent = React.createElement(Shortcuts, props) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + }); + const shortcutComponent = React.createElement(Shortcuts, props); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - const parentNode = ReactDOM.findDOMNode(wrapper.instance()) - const node = parentNode.querySelector('.test') + const parentNode = ReactDOM.findDOMNode(wrapper.instance()); + const node = parentNode.querySelector('.test'); - node.focus() + node.focus(); - const enter = 13 - simulant.fire(node, 'keydown', { keyCode: enter }) + const enter = 13; + simulant.fire(node, 'keydown', { keyCode: enter }); - expect(baseProps.handler).to.have.been.calledTwice - }) + expect(baseProps.handler).to.have.been.calledTwice; + }); it('should fire parent handler when parent handler has global prop', () => { const props = _.assign({}, baseProps, { children: React.createElement(Shortcuts, _.assign({}, baseProps, { className: 'test' })), global: true, - }) + }); - const shortcutComponent = React.createElement(Shortcuts, props) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const shortcutComponent = React.createElement(Shortcuts, props); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - const parentNode = ReactDOM.findDOMNode(wrapper.instance()) - const node = parentNode.querySelector('.test') + const parentNode = ReactDOM.findDOMNode(wrapper.instance()); + const node = parentNode.querySelector('.test'); - node.focus() + node.focus(); - const enter = 13 - simulant.fire(node, 'keydown', { keyCode: enter }) + const enter = 13; + simulant.fire(node, 'keydown', { keyCode: enter }); - expect(baseProps.handler).to.have.been.calledTwice - }) + expect(baseProps.handler).to.have.been.calledTwice; + }); it('should fire parent handler but not the child handler', () => { const props = _.assign({}, baseProps, { children: React.createElement(Shortcuts, _.assign({}, baseProps, { name: 'NON-EXISTING', className: 'test' })), global: true, - }) + }); - const shortcutComponent = React.createElement(Shortcuts, props) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const shortcutComponent = React.createElement(Shortcuts, props); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - const parentNode = ReactDOM.findDOMNode(wrapper.instance()) - const node = parentNode.querySelector('.test') + const parentNode = ReactDOM.findDOMNode(wrapper.instance()); + const node = parentNode.querySelector('.test'); - node.focus() + node.focus(); - const enter = 13 - simulant.fire(node, 'keydown', { keyCode: enter }) + const enter = 13; + simulant.fire(node, 'keydown', { keyCode: enter }); - expect(baseProps.handler).to.have.been.calledOnce - }) + expect(baseProps.handler).to.have.been.calledOnce; + }); it('should fire for all global components', () => { const props = _.assign({}, baseProps, { @@ -446,65 +445,65 @@ describe('Shortcuts component', () => { children: React.createElement(Shortcuts, _.assign({}, baseProps, { name: 'NON-EXISTING', className: 'test' })), })), global: true, - }) + }); - const shortcutComponent = React.createElement(Shortcuts, props) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const shortcutComponent = React.createElement(Shortcuts, props); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - const parentNode = ReactDOM.findDOMNode(wrapper.instance()) - const node = parentNode.querySelector('.test') + const parentNode = ReactDOM.findDOMNode(wrapper.instance()); + const node = parentNode.querySelector('.test'); - node.focus() + node.focus(); - const enter = 13 - simulant.fire(node, 'keydown', { keyCode: enter }) + const enter = 13; + simulant.fire(node, 'keydown', { keyCode: enter }); - expect(baseProps.handler).to.have.been.calledTwice - }) + expect(baseProps.handler).to.have.been.calledTwice; + }); it('should not fire parent handler when a child has isolate prop set to true', () => { - const childHandlerSpy = sinon.spy() + const childHandlerSpy = sinon.spy(); const props = _.assign({}, baseProps, { children: React.createElement(Shortcuts, _.assign({}, baseProps, { className: 'test', isolate: true, handler: childHandlerSpy, })), - }) + }); - const shortcutComponent = React.createElement(Shortcuts, props) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const shortcutComponent = React.createElement(Shortcuts, props); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - const parentNode = ReactDOM.findDOMNode(wrapper.instance()) - const node = parentNode.querySelector('.test') + const parentNode = ReactDOM.findDOMNode(wrapper.instance()); + const node = parentNode.querySelector('.test'); - node.focus() + node.focus(); - const enter = 13 - simulant.fire(node, 'keydown', { keyCode: enter }) + const enter = 13; + simulant.fire(node, 'keydown', { keyCode: enter }); - expect(childHandlerSpy).to.have.been.called - expect(baseProps.handler).to.not.have.been.called - }) + expect(childHandlerSpy).to.have.been.called; + expect(baseProps.handler).to.not.have.been.called; + }); it('should fire parent handler when is global and a child has isolate prop set to true', () => { const props = _.assign({}, baseProps, { global: true, children: React.createElement(Shortcuts, _.assign({}, baseProps, { className: 'test', isolate: true })), - }) + }); - const shortcutComponent = React.createElement(Shortcuts, props) - const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }) + const shortcutComponent = React.createElement(Shortcuts, props); + const wrapper = enzyme.mount(shortcutComponent, { context: baseContext }); - const parentNode = ReactDOM.findDOMNode(wrapper.instance()) - const node = parentNode.querySelector('.test') + const parentNode = ReactDOM.findDOMNode(wrapper.instance()); + const node = parentNode.querySelector('.test'); - node.focus() + node.focus(); - const enter = 13 - simulant.fire(node, 'keydown', { keyCode: enter }) + const enter = 13; + simulant.fire(node, 'keydown', { keyCode: enter }); - expect(baseProps.handler).to.have.been.called - }) - }) -}) + expect(baseProps.handler).to.have.been.called; + }); + }); +}); diff --git a/test/keymap.js b/test/keymap.js index 3c1c285..671d1b1 100644 --- a/test/keymap.js +++ b/test/keymap.js @@ -25,4 +25,4 @@ export default { 'CLOSE': 'esc', }, 'NON-EXISTING': {}, -} +}; diff --git a/test/shortcut-manager.spec.js b/test/shortcut-manager.spec.js index 67b4bb4..b0ce4ee 100644 --- a/test/shortcut-manager.spec.js +++ b/test/shortcut-manager.spec.js @@ -1,47 +1,47 @@ -import jsdom from 'jsdom' -import chai from 'chai' -import _ from 'lodash' -import sinonChai from 'sinon-chai' -import sinon from 'sinon' +import jsdom from 'jsdom'; +import chai from 'chai'; +import _ from 'lodash'; +import sinonChai from 'sinon-chai'; +import sinon from 'sinon'; -import keymap from './keymap' +import keymap from './keymap'; -chai.use(sinonChai) +chai.use(sinonChai); -const { expect } = chai +const { expect } = chai; describe('Shortcut manager', () => { - let ShortcutManager = null + let ShortcutManager = null; before(() => { - global.document = jsdom.jsdom('') - global.window = document.defaultView - global.Image = window.Image - global.navigator = window.navigator - global.CustomEvent = window.CustomEvent + global.document = jsdom.jsdom(''); + global.window = document.defaultView; + global.Image = window.Image; + global.navigator = window.navigator; + global.CustomEvent = window.CustomEvent; - ShortcutManager = require('../src').ShortcutManager - }) + ShortcutManager = require('../src').ShortcutManager; + }); it('should return empty object when calling empty constructor', () => { - const manager = new ShortcutManager() - expect(manager.getAllShortcuts()).to.be.empty - }) + const manager = new ShortcutManager(); + expect(manager.getAllShortcuts()).to.be.empty; + }); it('should return all shortcuts', () => { - const manager = new ShortcutManager(keymap) - expect(manager.getAllShortcuts()).to.not.be.empty - expect(manager.getAllShortcuts()).to.be.equal(keymap) + const manager = new ShortcutManager(keymap); + expect(manager.getAllShortcuts()).to.not.be.empty; + expect(manager.getAllShortcuts()).to.be.equal(keymap); - manager.setKeymap({}) - expect(manager.getAllShortcuts()).to.be.empty + manager.setKeymap({}); + expect(manager.getAllShortcuts()).to.be.empty; - manager.setKeymap(keymap) - expect(manager.getAllShortcuts()).to.be.equal(keymap) - }) + manager.setKeymap(keymap); + expect(manager.getAllShortcuts()).to.be.equal(keymap); + }); it('should return all shortcuts for the Windows platform', () => { - const manager = new ShortcutManager(keymap) + const manager = new ShortcutManager(keymap); const keyMapResult = { 'Test': { MOVE_LEFT: 'left', @@ -59,13 +59,13 @@ describe('Shortcut manager', () => { 'CLOSE': 'esc', }, 'NON-EXISTING': {}, - } + }; - expect(manager.getAllShortcutsForPlatform('windows')).to.eql(keyMapResult) - }) + expect(manager.getAllShortcutsForPlatform('windows')).to.eql(keyMapResult); + }); it('should return all shortcuts for the macOs platform', () => { - const manager = new ShortcutManager(keymap) + const manager = new ShortcutManager(keymap); const keyMapResult = { 'Test': { MOVE_LEFT: 'left', @@ -83,88 +83,88 @@ describe('Shortcut manager', () => { 'CLOSE': 'esc', }, 'NON-EXISTING': {}, - } + }; - expect(manager.getAllShortcutsForPlatform('osx')).to.eql(keyMapResult) - }) + expect(manager.getAllShortcutsForPlatform('osx')).to.eql(keyMapResult); + }); it('should expose the change event type as a static constant', () => expect(ShortcutManager.CHANGE_EVENT).to.exist - ) + ); it('should have static CHANGE_EVENT', () => expect(ShortcutManager.CHANGE_EVENT).to.be.equal('shortcuts:update') - ) + ); it('should call onUpdate', () => { - const manager = new ShortcutManager() - const spy = sinon.spy() - manager.addUpdateListener(spy) - manager.setKeymap({}) - expect(spy).to.have.beenCalled - }) + const manager = new ShortcutManager(); + const spy = sinon.spy(); + manager.addUpdateListener(spy); + manager.setKeymap({}); + expect(spy).to.have.beenCalled; + }); it('should throw an error when setKeymap is called without arg', () => { - const manager = new ShortcutManager(keymap) - const error = /setKeymap: keymap argument is not defined or falsy./ - expect(manager.setKeymap).to.throw(error) - }) + const manager = new ShortcutManager(keymap); + const error = /setKeymap: keymap argument is not defined or falsy./; + expect(manager.setKeymap).to.throw(error); + }); it('should extend the keymap', () => { - const manager = new ShortcutManager() - const newKeymap = { 'TESTING-NAMESPACE': {} } - const extendedKeymap = Object.assign({}, keymap, newKeymap) - manager.setKeymap(keymap) - manager.extendKeymap(newKeymap) + const manager = new ShortcutManager(); + const newKeymap = { 'TESTING-NAMESPACE': {} }; + const extendedKeymap = Object.assign({}, keymap, newKeymap); + manager.setKeymap(keymap); + manager.extendKeymap(newKeymap); - expect(manager.getAllShortcuts()).to.eql(extendedKeymap) - }) + expect(manager.getAllShortcuts()).to.eql(extendedKeymap); + }); it('should return array of shortcuts', () => { - const manager = new ShortcutManager(keymap) - let shortcuts = manager.getShortcuts('Test') - expect(shortcuts).to.be.an.array + const manager = new ShortcutManager(keymap); + let shortcuts = manager.getShortcuts('Test'); + expect(shortcuts).to.be.an.array; - let shouldContainStrings = _.every(shortcuts, _.isString) - expect(shouldContainStrings).to.be.equal(true) - expect(shortcuts.length).to.be.equal(5) + let shouldContainStrings = _.every(shortcuts, _.isString); + expect(shouldContainStrings).to.be.equal(true); + expect(shortcuts.length).to.be.equal(5); - shortcuts = manager.getShortcuts('Next') - expect(shortcuts).to.be.an.array - shouldContainStrings = _.every(shortcuts, _.isString) - expect(shouldContainStrings).to.be.equal(true) - expect(shortcuts.length).to.be.equal(5) - }) + shortcuts = manager.getShortcuts('Next'); + expect(shortcuts).to.be.an.array; + shouldContainStrings = _.every(shortcuts, _.isString); + expect(shouldContainStrings).to.be.equal(true); + expect(shortcuts.length).to.be.equal(5); + }); it('should not throw an error when getting not existing key from keymap', () => { - const manager = new ShortcutManager(keymap) - const notExist = () => manager.getShortcuts('NotExist') - expect(notExist).to.not.throw() - }) + const manager = new ShortcutManager(keymap); + const notExist = () => manager.getShortcuts('NotExist'); + expect(notExist).to.not.throw(); + }); it('should return correct key label', () => { - const manager = new ShortcutManager() - manager.setKeymap(keymap) + const manager = new ShortcutManager(); + manager.setKeymap(keymap); // Test - expect(manager.findShortcutName('alt+backspace', 'Test')).to.be.equal('DELETE') - expect(manager.findShortcutName('w', 'Test')).to.be.equal('MOVE_UP') - expect(manager.findShortcutName('up', 'Test')).to.be.equal('MOVE_UP') - expect(manager.findShortcutName('left', 'Test')).to.be.equal('MOVE_LEFT') - expect(manager.findShortcutName('right', 'Test')).to.be.equal('MOVE_RIGHT') + expect(manager.findShortcutName('alt+backspace', 'Test')).to.be.equal('DELETE'); + expect(manager.findShortcutName('w', 'Test')).to.be.equal('MOVE_UP'); + expect(manager.findShortcutName('up', 'Test')).to.be.equal('MOVE_UP'); + expect(manager.findShortcutName('left', 'Test')).to.be.equal('MOVE_LEFT'); + expect(manager.findShortcutName('right', 'Test')).to.be.equal('MOVE_RIGHT'); // Next - expect(manager.findShortcutName('alt+o', 'Next')).to.be.equal('OPEN') - expect(manager.findShortcutName('d', 'Next')).to.be.equal('ABORT') - expect(manager.findShortcutName('c', 'Next')).to.be.equal('ABORT') - expect(manager.findShortcutName('esc', 'Next')).to.be.equal('CLOSE') - expect(manager.findShortcutName('enter', 'Next')).to.be.equal('CLOSE') - }) + expect(manager.findShortcutName('alt+o', 'Next')).to.be.equal('OPEN'); + expect(manager.findShortcutName('d', 'Next')).to.be.equal('ABORT'); + expect(manager.findShortcutName('c', 'Next')).to.be.equal('ABORT'); + expect(manager.findShortcutName('esc', 'Next')).to.be.equal('CLOSE'); + expect(manager.findShortcutName('enter', 'Next')).to.be.equal('CLOSE'); + }); it('should throw an error', () => { - const manager = new ShortcutManager() - const fn = () => manager.findShortcutName('left') - expect(manager.findShortcutName).to.throw(/findShortcutName: keyName argument is not defined or falsy./) - expect(fn).to.throw(/findShortcutName: componentName argument is not defined or falsy./) - }) -}) + const manager = new ShortcutManager(); + const fn = () => manager.findShortcutName('left'); + expect(manager.findShortcutName).to.throw(/findShortcutName: keyName argument is not defined or falsy./); + expect(fn).to.throw(/findShortcutName: componentName argument is not defined or falsy./); + }); +}); diff --git a/test/utils.js b/test/utils.js index 160873e..9400c5d 100644 --- a/test/utils.js +++ b/test/utils.js @@ -1,13 +1,13 @@ -import chai from 'chai' -import _ from 'lodash' -import { isArray, isPlainObject, findKey, compact, flatten, map } from '../src/utils' +import chai from 'chai'; +import _ from 'lodash'; +import { isArray, isPlainObject, findKey, compact, flatten, map } from '../src/utils'; describe('utils', () => { - const { expect } = chai - let primitives + const { expect } = chai; + let primitives; beforeEach(() => { - function fn() { this.a = 1 } + function fn() { this.a = 1; } primitives = [ ['array'], @@ -17,40 +17,40 @@ describe('utils', () => { null, undefined, NaN, - new Map([[ 1, 'one' ], [ 2, 'two' ]]), + new Map([[1, 'one'], [2, 'two']]), new fn(), true, 42, - ] - }) + ]; + }); describe('isArray', () => { it('should be true for arrays', () => { primitives.forEach((val, idx) => { if (idx === 0) { - expect(isArray(val)).to.be.true - expect(_.isArray(val)).to.be.true + expect(isArray(val)).to.be.true; + expect(_.isArray(val)).to.be.true; } else { - expect(isArray(val)).to.be.false - expect(_.isArray(val)).to.be.false + expect(isArray(val)).to.be.false; + expect(_.isArray(val)).to.be.false; } - }) - }) - }) + }); + }); + }); describe('isPlainObject', () => { it('should be true for plain objects', () => { primitives.forEach((val, idx) => { if (idx === 1 || idx === 2) { - expect(isPlainObject(val)).to.be.true - expect(_.isPlainObject(val)).to.be.true + expect(isPlainObject(val)).to.be.true; + expect(_.isPlainObject(val)).to.be.true; } else { - expect(isPlainObject(val)).to.be.false - expect(_.isPlainObject(val)).to.be.false + expect(isPlainObject(val)).to.be.false; + expect(_.isPlainObject(val)).to.be.false; } - }) - }) - }) + }); + }); + }); describe('findKey', () => { it('should return the matching key', () => { @@ -59,15 +59,15 @@ describe('utils', () => { obj: { val: 4, }, - } + }; - const checkOne = val => val === 1 - const checkTwo = val => typeof val === 'object' + const checkOne = val => val === 1; + const checkTwo = val => typeof val === 'object'; - expect(findKey(obj, checkOne)).to.deep.equal(_.findKey(obj, checkOne)) - expect(findKey(obj, checkTwo)).to.deep.equal(_.findKey(obj, checkTwo)) - }) - }) + expect(findKey(obj, checkOne)).to.deep.equal(_.findKey(obj, checkOne)); + expect(findKey(obj, checkTwo)).to.deep.equal(_.findKey(obj, checkTwo)); + }); + }); describe('compact', () => { it('removes falsy values', () => { @@ -81,52 +81,52 @@ describe('utils', () => { NaN, '', 'false, null, 0, "", undefined, and NaN are falsy', - ] + ]; - expect(compact(values)).to.deep.equal(_.compact(values)) - }) - }) + expect(compact(values)).to.deep.equal(_.compact(values)); + }); + }); describe('flatten', () => { it('flattens an array 1 level', () => { - const value = [1, [2, [3, [4]], 5, [[[6], 7], 8], 9]] - expect(flatten(value)).to.deep.equal(_.flatten(value)) - }) - }) + const value = [1, [2, [3, [4]], 5, [[[6], 7], 8], 9]]; + expect(flatten(value)).to.deep.equal(_.flatten(value)); + }); + }); describe('map', () => { it('should map an array', () => { - const values = [1, 2, 3, 4] - const mapFn = val => val * 10 + const values = [1, 2, 3, 4]; + const mapFn = val => val * 10; - expect(map(values, mapFn)).to.deep.equal(_.map(values, mapFn)) - expect(map(values, mapFn)).to.deep.equal([10, 20, 30, 40]) + expect(map(values, mapFn)).to.deep.equal(_.map(values, mapFn)); + expect(map(values, mapFn)).to.deep.equal([10, 20, 30, 40]); // ensure that values array is not mutated - expect(values).to.deep.equal([1, 2, 3, 4]) - }) + expect(values).to.deep.equal([1, 2, 3, 4]); + }); it('should map an object', () => { const obj = { one: 1, two: 2, three: 3, - } - const mapFn = (val, key) => `${key} - ${val * 10}` + }; + const mapFn = (val, key) => `${key} - ${val * 10}`; - expect(map(obj, mapFn)).to.deep.equal(_.map(obj, mapFn)) + expect(map(obj, mapFn)).to.deep.equal(_.map(obj, mapFn)); expect(map(obj, mapFn)).to.deep.equal([ 'one - 10', 'two - 20', 'three - 30', - ]) + ]); // ensure the object was not mutated expect(obj).to.deep.equal({ one: 1, two: 2, three: 3, - }) - }) - }) -}) + }); + }); + }); +}); diff --git a/webpack.config.js b/webpack.config.js index 24e4b37..01dc5a3 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,4 +1,4 @@ -const webpack = require('webpack') +const webpack = require('webpack'); module.exports = { entry: [ @@ -38,4 +38,4 @@ module.exports = { externals: [ { react: 'React' }, ], -} +};