Skip to content

Commit

Permalink
Merge f6d960d into e69f2a1
Browse files Browse the repository at this point in the history
  • Loading branch information
EmaSuriano committed Jul 24, 2018
2 parents e69f2a1 + f6d960d commit e288806
Show file tree
Hide file tree
Showing 9 changed files with 652 additions and 16 deletions.
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,14 @@
"jest": {
"setupFiles": [
"raf/polyfill",
"<rootDir>/test-setup.js"
"<rootDir>/test-setup"
],
"transform": {
"^.+\\.js$": "<rootDir>/jest.transform.js"
},
"transformIgnorePatterns": [
"^.+\\.css$"
],
"moduleDirectories": [
"node_modules"
],
Expand Down
13 changes: 8 additions & 5 deletions src/Hotkey.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import PropTypes from 'prop-types';
import { Tooltip } from 'react-tippy';
import MouseTrap from 'mousetrap';
import { Consumer } from './HotkeyContext';
import { isFunction, ERROR_MESSAGES } from './utils';
import { isFunction } from './utils';

class HotkeyInner extends React.Component {
static propTypes = {
combination: PropTypes.string.isRequired,
onPress: PropTypes.oneOfType([PropTypes.func, PropTypes.string]).isRequired,
children: PropTypes.element.isRequired,

// from Context
disabled: PropTypes.bool.isRequired,
showTooltip: PropTypes.bool.isRequired,
tooltipOptions: PropTypes.object.isRequired,
Expand Down Expand Up @@ -43,9 +45,10 @@ class HotkeyInner extends React.Component {
if (isFunction(onPress)) return onPress(evt);

if (!isFunction(this.child.current[onPress])) {
return new Error(ERROR_MESSAGES.METHOD_NOT_FOUND_IN_CHILD);
throw new Error(
`ERROR: The method of ${onPress} is not present in the DOMNode of the child, please check render.`,
);
}

return this.child.current[onPress](evt);
};

Expand All @@ -57,12 +60,12 @@ class HotkeyInner extends React.Component {
showTooltip,
tooltipOptions,
} = this.props;

const open = showTooltip && !disabled;
return (
<Tooltip
{...tooltipOptions}
title={combination.toUpperCase()}
open={showTooltip && !disabled}
open={open}
trigger="manual"
multiple
>
Expand Down
198 changes: 198 additions & 0 deletions src/__tests__/Hotkey.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import React from 'react';
import { mount } from 'enzyme';
import Mousetrap from 'mousetrap';
import Hotkey from '../Hotkey';
import { Provider } from '../HotkeyContext';

describe('<Hotkey />', () => {
const renderWithProvider = (providerValue, hotkey) =>
mount(
<Provider
value={{
disabled: false,
showTooltip: false,
tooltipOptions: {},
...providerValue,
}}
>
{hotkey}
</Provider>,
);

it('should render children', () => {
const hotkey = mount(
<Hotkey combination="a" onPress="click">
<button type="button">a</button>
</Hotkey>,
);

expect(hotkey.find('button')).toHaveLength(1);
});

describe('on hotkey pressed', () => {
xit('should call dom function when onPress is string', () => {
const onClick = jest.fn();
mount(
<Hotkey combination="a" onPress="click">
<button type="button" onClick={onClick}>
a
</button>
</Hotkey>,
);

Mousetrap.trigger('a');

expect(onClick).toHaveBeenCalled();
});

it('should throw an error when onPress is string but it is not a function', () => {
expect(() => {
mount(
<Hotkey combination="a" onPress="thisIsNotAValidFunction">
<button type="button">a</button>
</Hotkey>,
);
Mousetrap.trigger('a');
}).toThrowError('ERROR');
});

it('should call function passed in onPress prop', () => {
const onPress = jest.fn();
mount(
<Hotkey combination="a" onPress={onPress}>
<button type="button">a</button>
</Hotkey>,
);

Mousetrap.trigger('a');

expect(onPress).toHaveBeenCalled();
});

it('should not call any function when is disabled', () => {
const onPress = jest.fn();
mount(
<Hotkey combination="a" onPress={onPress} disabled>
<button type="button">a</button>
</Hotkey>,
);

Mousetrap.trigger('a');

expect(onPress).not.toHaveBeenCalled();
});
});

describe('Changing combination', () => {
const onPress = jest.fn();
let hotkey;

beforeEach(() => {
onPress.mockClear();
hotkey = mount(
<Hotkey combination="a" onPress={onPress}>
<button type="button">a</button>
</Hotkey>,
);
hotkey.setProps({ combination: 'b' });
});

it('should disable previous tooltipCombination', () => {
Mousetrap.trigger('a');
expect(onPress).not.toHaveBeenCalled();
});

it('should enable new tooltipCombination', () => {
Mousetrap.trigger('b');
expect(onPress).toHaveBeenCalled();
});
});

it('should unbind hotkey when component unmount', () => {
const onPress = jest.fn();
const hotkey = mount(
<Hotkey combination="a" onPress={onPress}>
<button type="button">a</button>
</Hotkey>,
);
hotkey.unmount();

Mousetrap.trigger('a');

expect(onPress).not.toHaveBeenCalled();
});

it('should do not call any function when disabled by context', () => {
const onPress = jest.fn();
renderWithProvider(
{ disabled: true },
<Hotkey combination="a" onPress={onPress}>
<button type="button">a</button>
</Hotkey>,
);

Mousetrap.trigger('a');
expect(onPress).not.toHaveBeenCalled();
});

describe('render Tooltip', () => {
it('should send options provided by ConsumerContext', () => {
const hotkey = renderWithProvider(
{
tooltipOptions: {
theme: 'dark',
},
},
<Hotkey combination="a" onPress="click">
<button type="button">a</button>
</Hotkey>,
);
expect(hotkey.find('Tooltip').prop('theme')).toBe('dark');
});

it('should send combination from props', () => {
const hotkey = mount(
<Hotkey combination="a" onPress="click">
<button type="button">a</button>
</Hotkey>,
);

expect(hotkey.find('Tooltip').prop('title')).toBe('A');
});

it('should render with multiple, manual and open in false', () => {
const hotkey = mount(
<Hotkey combination="a" onPress="click">
<button type="button">a</button>
</Hotkey>,
);

const { trigger, multiple, open } = hotkey.find('Tooltip').props();

expect(trigger).toBe('manual');
expect(open).toBe(false);
expect(multiple).toBe(true);
});

it('should send open when showTooltip is true', () => {
const hotkey = renderWithProvider(
{ showTooltip: true },
<Hotkey combination="a" onPress="click">
<button type="button">a</button>
</Hotkey>,
);
expect(hotkey.find('Tooltip').prop('open')).toBe(true);
});

it('should do not send open when showTooltip is true and disabled is false', () => {
const hotkey = renderWithProvider(
{ showTooltip: true, disabled: true },
<Hotkey combination="a" onPress="click">
<button type="button">a</button>
</Hotkey>,
);

expect(hotkey.find('Tooltip').prop('open')).toBe(false);
});
});
});
93 changes: 93 additions & 0 deletions src/__tests__/HotkeyProvider.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import React from 'react';
import { mount } from 'enzyme';
import Mousetrap from 'mousetrap';
import Hotkey from '../Hotkey';
import HotkeyProvider from '../HotkeyProvider';

describe('<HotkeyProvider />', () => {
let hotkey;
let triggerKey;

beforeEach(() => {
hotkey = mount(
<HotkeyProvider tooltipCombination="enter">
<Hotkey combination="a" onPress="click">
<button type="button">Click me!</button>
</Hotkey>
</HotkeyProvider>,
);
triggerKey = (key, event) => {
Mousetrap.trigger(key, event);

hotkey.update();
};
});

it('should render HotkeyProvider with Hotkey', () => {
expect(hotkey.find(HotkeyProvider)).toHaveLength(1);
expect(hotkey.find(Hotkey)).toHaveLength(1);
});

it('should display tooltip when pressing tooltipCombination', () => {
triggerKey('enter', 'keydown');

expect(hotkey.find('Tooltip').props().open).toBe(true);
});

it('should hide tooltip after pressing tooltipCombination', () => {
triggerKey('enter', 'keydown');
triggerKey('enter', 'keyup');

expect(hotkey.find('Tooltip').props().open).toBe(false);
});

describe('Changing tooltipCombination', () => {
beforeEach(() => {
hotkey.setProps({ tooltipCombination: 'shift' });
});

it('should disable previous tooltipCombination', () => {
triggerKey('enter', 'keydown');
expect(hotkey.find('Tooltip').props().open).toBe(false);
});

it('should enable new tooltipCombination', () => {
triggerKey('shift', 'keydown');
expect(hotkey.find('Tooltip').props().open).toBe(true);
});
});

it('should do not display tooltip when is disabled', () => {
hotkey.setProps({ disabled: true });

triggerKey('enter', 'keydown');

expect(hotkey.find('Tooltip').props().open).toBe(false);
});

it('should send tooltipOptions to Tooltip', () => {
hotkey.setProps({
tooltipOptions: {
theme: 'light',
},
});

triggerKey('enter', 'keydown');

expect(hotkey.find('Tooltip').props().theme).toBe('light');
});

it('should unbind hotkey when component unmount', () => {
const unbindSpy = jest.spyOn(Mousetrap, 'unbind');
hotkey.unmount();

expect(unbindSpy).toHaveBeenCalledTimes(3);

// HotkeyProvider
expect(unbindSpy).toHaveBeenCalledWith('enter', 'keydown');
expect(unbindSpy).toHaveBeenCalledWith('enter', 'keyup');

// Hotkey
expect(unbindSpy).toHaveBeenCalledWith('a');
});
});
1 change: 0 additions & 1 deletion src/__tests__/notImplemented.js

This file was deleted.

5 changes: 0 additions & 5 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,3 @@ export const KEYBOARD_EVENT = {
KEY_DOWN: 'keydown',
KEY_UP: 'keyup',
};

export const ERROR_MESSAGES = {
METHOD_NOT_FOUND_IN_CHILD: method =>
`ERROR: The method of ${method} is not present in the DOMNode of the child, please check render.`,
};
4 changes: 0 additions & 4 deletions test-setup.js

This file was deleted.

Loading

0 comments on commit e288806

Please sign in to comment.