Skip to content

Commit cfbdf7c

Browse files
committed
feat: add ability to resume and change delay of a timer
BREAKING CHANGE: tick, delay, stops are now properties of a timer prop
1 parent 49f981e commit cfbdf7c

File tree

3 files changed

+77
-17
lines changed

3 files changed

+77
-17
lines changed

modules/timer.js

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,61 @@
11
import React from 'react';
22
import invariant from 'invariant';
33

4-
function timer(delay) {
4+
function checkDelay(delay) {
55
invariant(
66
typeof delay === 'number' && delay > 0,
77
'[react-timer-hoc] `delay` should be a number greater than 0.'
88
);
9+
}
10+
11+
function timer(delay) {
12+
checkDelay(delay);
913

1014
return function TimerHoc(TimedComponent) {
1115
class Timer extends React.Component {
1216
constructor(props) {
1317
super(props);
18+
this.delay = delay;
1419
this.state = { tick: 0 };
20+
1521
this.setTimeout = ::this.setTimeout;
1622
this.stop = ::this.stop;
23+
this.resume = ::this.resume;
24+
this.setDelay = ::this.setDelay;
1725
}
1826

1927
setTimeout() {
20-
const duration = delay - (this.startTime - Date.now()) % delay;
28+
const { delay, startTime } = this;
29+
const duration = delay - (startTime - Date.now()) % delay;
30+
2131
this.timer = setTimeout(() => {
2232
this.setState({ tick: this.state.tick + 1 });
2333
if (!this.stopped) this.setTimeout();
2434
}, delay);
2535
}
2636

37+
resume() {
38+
if (this.stopped) {
39+
this.stopped = false;
40+
this.startTime = Date.now();
41+
this.setTimeout();
42+
}
43+
}
44+
2745
stop() {
2846
this.stopped = true;
2947
clearTimeout(this.timer);
3048
}
3149

50+
setDelay(delay) {
51+
checkDelay(delay);
52+
this.delay = delay;
53+
if (!this.stopped) {
54+
this.stop();
55+
this.resume();
56+
}
57+
}
58+
3259
componentDidMount() {
3360
this.stopped = false;
3461
this.startTime = Date.now();
@@ -40,10 +67,12 @@ function timer(delay) {
4067
}
4168

4269
render() {
43-
const { props, stop } = this;
70+
const { props, stop, resume, setDelay } = this;
4471
const { tick } = this.state;
4572

46-
return React.createElement(TimedComponent, { ...props, tick, delay, stop });
73+
const timer = { delay, tick, stop, resume, setDelay };
74+
75+
return React.createElement(TimedComponent, { ...props, timer });
4776
}
4877
};
4978

modules/timer.test.js

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { createRenderer, renderIntoDocument, findRenderedComponentWithType } from 'react-addons-test-utils';
22
import { expect } from 'chai';
3+
import sinon from 'sinon';
34
import React, { Component } from 'react';
45
import h from 'react-hyperscript';
56
import timer from './timer';
@@ -11,27 +12,55 @@ const win = doc.defaultView;
1112
global.document = doc;
1213
global.window = win;
1314

15+
class Counter extends Component {
16+
render() {
17+
const { tick } = this.props;
18+
return h('div', { tick });
19+
}
20+
}
21+
1422
describe('Timer', function() {
15-
it('should run', function() {
16-
class Counter extends Component {
17-
render() {
18-
const { tick } = this.props;
19-
return h('div', { tick });
20-
}
21-
}
23+
let clock, wrappedCounter, counter;
24+
25+
before(() => clock = sinon.useFakeTimers());
26+
after(() => clock.restore());
2227

28+
it('should pass down a timer property alongside other props', function() {
2329
const WrappedCounter = timer(1000)(Counter);
2430
expect(WrappedCounter.displayName).to.equal('Timer@1000[Counter]');
2531

2632
const wrappedCounter = renderIntoDocument(h(WrappedCounter, { customProp: 1 }));
27-
const counter = findRenderedComponentWithType(wrappedCounter, Counter);
28-
expect(counter.props.tick).to.equal(0);
29-
expect(counter.props.delay).to.equal(1000);
30-
expect(counter.props.stop).to.be.a.function;
33+
34+
counter = findRenderedComponentWithType(wrappedCounter, Counter);
35+
36+
expect(counter.props.timer.tick).to.equal(0);
37+
expect(counter.props.timer.delay).to.equal(1000);
38+
expect(counter.props.timer.stop).to.be.a.function;
39+
expect(counter.props.timer.setDelay).to.be.a.function;
3140
expect(counter.props.customProp).to.equal(1);
41+
});
3242

43+
it('should increment a tick property', function() {
44+
clock.tick(1100);
45+
expect(counter.props.timer.tick).to.equal(1);
46+
clock.tick(1000);
47+
expect(counter.props.timer.tick).to.equal(2);
48+
});
49+
50+
it('should have the ability to be stopped and resumed', function() {
3351
expect(wrappedCounter.stopped).to.be.false;
34-
counter.props.stop();
52+
counter.props.timer.stop();
3553
expect(wrappedCounter.stopped).to.be.true;
54+
counter.props.timer.resume();
55+
expect(wrappedCounter.stopped).to.be.false;
56+
});
57+
58+
it('shoud give the ability to change its delay', function() {
59+
counter.props.timer.setDelay(60000);
60+
expect(wrappedCounter.delay).to.be.equal(60000);
61+
62+
clock.tick(60100);
63+
expect(counter.props.timer.tick).to.equal(3);
64+
counter.props.timer.stop();
3665
});
3766
});

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@
4141
"jsdom": "^7.1.0",
4242
"mocha": "^2.3.4",
4343
"react-addons-test-utils": "^0.14.3",
44-
"react-hyperscript": "^2.2.0"
44+
"react-hyperscript": "^2.2.0",
45+
"sinon": "^1.17.2",
46+
"sinon-chai": "^2.8.0"
4547
},
4648
"dependencies": {
4749
"invariant": "^2.2.0",

0 commit comments

Comments
 (0)