Permalink
Browse files

🐣

  • Loading branch information...
deepsweet committed Jul 31, 2017
0 parents commit 5fbee4e2d88eac923d220285f1d483e34a9515aa
@@ -0,0 +1,9 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
@@ -0,0 +1,7 @@
node_modules/
dist/
es/
lib/
coverage/
.DS_Store
*.log
@@ -0,0 +1,11 @@
sudo: false
language: node_js
node_js:
- node
env:
global:
- PATH=$HOME/.yarn/bin:$PATH
before_install:
- curl -o- -L https://yarnpkg.com/install.sh | bash
cache: yarn
script: yarn start ci
@@ -0,0 +1,8 @@
{
"lerna": "2.0.0",
"packages": [
"packages/*"
],
"version": "independent",
"npmClient": "yarn"
}
@@ -0,0 +1,21 @@
# The MIT License (MIT)
Copyright (c) 2017–present, Kir "deepsweet" Belevich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@@ -0,0 +1,12 @@
{
"private": "true",
"devDependencies": {
"lerna": "^2.0.0",
"start-babel-cli": "^4.0.2",
"start-deepsweet-react-components-monorepo-preset": "^0.0.1"
},
"scripts": {
"start": "start-runner --preset start-deepsweet-react-components-monorepo-preset",
"postinstall": "lerna bootstrap"
}
}
@@ -0,0 +1,19 @@
import React from 'react';
import { compose, withState, withHandlers } from 'recompose';
import debounceHandler from '../src/';
const Demo = ({ count, onButtonClick }) => (
<div>
<h1>{count}</h1>
<button onClick={onButtonClick}>CLICK ME FAST</button>
</div>
);
export default compose(
withState('count', 'setCount', 0),
withHandlers({
onButtonClick: ({ count, setCount }) => () => setCount(count + 1)
}),
debounceHandler('onButtonClick', 300, true)
)(Demo);
@@ -0,0 +1,28 @@
{
"name": "@hocs/debounce-handler",
"library": "debounceHandler",
"version": "0.1.0",
"description": "Debounce handler HOC for React",
"keywords": [
"react",
"hoc",
"recompose"
],
"main": "lib/index.js",
"module": "es/index.js",
"files": [
"dist/",
"es/",
"lib/"
],
"repository": "deepsweet/hocs",
"author": "Kir Belevich <kir@belevi.ch> (https://github.com/deepsweet)",
"license": "MIT",
"dependencies": {
"just-debounce-it": "^1.0.1"
},
"peerDependencies": {
"react": "^15.6.1",
"recompose": "^0.24.0"
}
}
@@ -0,0 +1,41 @@
import React, { Component } from 'react';
import { setDisplayName, wrapDisplayName } from 'recompose';
import debounce from 'just-debounce-it';
const debounceHandler = (handlerName, delay, leadingCall) => (Target) => {
class DebounceHandler extends Component {
constructor(props, context) {
super(props, context);
const debounced = debounce(props[handlerName], delay, leadingCall);
this[handlerName] = (e, ...rest) => {
if (e && typeof e.persist === 'function') {
e.persist();
}
return debounced(e, ...rest);
};
}
render() {
const newProps = {
...this.props,
[handlerName]: this[handlerName]
};
return (
<Target {...newProps}/>
);
}
}
if (process.env.NODE_ENV !== 'production') {
return setDisplayName(wrapDisplayName(Target, 'debounceHandler'))(DebounceHandler);
}
return DebounceHandler;
};
export default debounceHandler;
@@ -0,0 +1,143 @@
import React from 'react';
import { mount } from 'enzyme';
import debounceHandler from '../src/';
const DELAY = 20;
const HALF_DELAY = DELAY / 2;
const Target = () => null;
describe('debounceHandler', () => {
it('should pass handler arguments through', (done) => {
const EnchancedTarget = debounceHandler('testHandler')(Target);
const mockTestHandler = jest.fn();
const wrapper = mount(
<EnchancedTarget testHandler={mockTestHandler}/>
);
const testHandler = wrapper.find(Target).prop('testHandler');
testHandler('a', 'b', 'c');
setTimeout(() => {
expect(mockTestHandler).toHaveBeenCalledTimes(1);
expect(mockTestHandler).toHaveBeenCalledWith('a', 'b', 'c');
done();
});
});
it('should call `e.persist()` if it has been passed', (done) => {
const EnchancedTarget = debounceHandler('testHandler')(Target);
const mockTestHandler = jest.fn();
const mockPersist = jest.fn();
const wrapper = mount(
<EnchancedTarget testHandler={mockTestHandler}/>
);
const testHandler = wrapper.find(Target).prop('testHandler');
testHandler({ persist: mockPersist });
setTimeout(() => {
expect(mockTestHandler).toHaveBeenCalledTimes(1);
expect(mockPersist).toHaveBeenCalledTimes(1);
done();
}, 0);
});
it('should debounce handler with `delay` option', (done) => {
const EnchancedTarget = debounceHandler('testHandler', DELAY)(Target);
const mockTestHandler = jest.fn();
const wrapper = mount(
<EnchancedTarget testHandler={mockTestHandler}/>
);
const testHandler = wrapper.find(Target).prop('testHandler');
testHandler('a');
expect(mockTestHandler).toHaveBeenCalledTimes(0);
setTimeout(() => {
testHandler('b');
expect(mockTestHandler).toHaveBeenCalledTimes(0);
setTimeout(() => {
testHandler('c');
expect(mockTestHandler).toHaveBeenCalledTimes(0);
setTimeout(() => {
testHandler('d');
expect(mockTestHandler).toHaveBeenCalledTimes(0);
setTimeout(() => {
expect(mockTestHandler).toHaveBeenCalledTimes(1);
expect(mockTestHandler).toHaveBeenCalledWith('d');
done();
}, DELAY);
}, HALF_DELAY);
}, HALF_DELAY);
}, HALF_DELAY);
});
it('should debounce handler with `leadingCall` option', (done) => {
const EnchancedTarget = debounceHandler('testHandler', DELAY, true)(Target);
const mockTestHandler = jest.fn();
const wrapper = mount(
<EnchancedTarget testHandler={mockTestHandler}/>
);
const testHandler = wrapper.find(Target).prop('testHandler');
testHandler('a');
expect(mockTestHandler).toHaveBeenCalledTimes(1);
expect(mockTestHandler).toHaveBeenCalledWith('a');
setTimeout(() => {
testHandler('b');
expect(mockTestHandler).toHaveBeenCalledTimes(1);
expect(mockTestHandler).toHaveBeenCalledWith('a');
setTimeout(() => {
testHandler('c');
expect(mockTestHandler).toHaveBeenCalledTimes(1);
expect(mockTestHandler).toHaveBeenCalledWith('a');
setTimeout(() => {
testHandler('d');
expect(mockTestHandler).toHaveBeenCalledTimes(1);
expect(mockTestHandler).toHaveBeenCalledWith('a');
setTimeout(() => {
expect(mockTestHandler).toHaveBeenCalledTimes(2);
expect(mockTestHandler).toHaveBeenCalledWith('d');
done();
}, DELAY);
}, HALF_DELAY);
}, HALF_DELAY);
}, HALF_DELAY);
});
describe('display name', () => {
const origNodeEnv = process.env.NODE_ENV;
afterAll(() => {
process.env.NODE_ENV = origNodeEnv;
});
it('should wrap display name in non-production env', () => {
process.env.NODE_ENV = 'test';
const EnchancedTarget = debounceHandler()(Target);
const wrapper = mount(
<EnchancedTarget/>
);
expect(wrapper.name()).toBe('debounceHandler(Target)');
});
it('should not wrap display name in production env', () => {
process.env.NODE_ENV = 'production';
const EnchancedTarget = debounceHandler()(Target);
const wrapper = mount(
<EnchancedTarget/>
);
expect(wrapper.name()).toBe('DebounceHandler');
});
});
});
Oops, something went wrong.

0 comments on commit 5fbee4e

Please sign in to comment.