@khanacademy/wonder-blocks-timing 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs.md CHANGED
@@ -173,8 +173,7 @@ The `IScheduleActions` interface provides 4 (four) different functions:
173
173
  timeout(
174
174
  action: () => mixed,
175
175
  period: number,
176
- autoSchedule?: boolean,
177
- resolveOnClear?: boolean,
176
+ options?: Options,
178
177
  ): ITimeout;
179
178
  ```
180
179
 
@@ -185,8 +184,7 @@ in the standard timer API.
185
184
  | --- | --- | --- | --- |
186
185
  | `action` | `()` `=>` `void` | _Required_ | The action to be invoked when the timeout period is reached. |
187
186
  | `period` | `number` | _Required_ | The timeout period in milliseconds. The action will be invoked after this period has passed since the timeout was set. This value must be greater than or equal to zero. |
188
- | `autoSchedule` | `boolean` | `true` | Whether or not to set the timeout as soon as this call is made, or wait until `set` is explicitly called. Defaults to `true`. |
189
- | `resolveOnClear` | `boolean` | `false` | Whether or not the associated action will be invoked if it is still pending at the point the timeout is cleared. Defaults to `false`. This only takes effect when the [clearAll](#clearall) is invoked on parent the `IScheduleActions` instance, such as when unmounting; this does not affect calls to the `clear` method on the returned `ITimeout` interface. |
187
+ | `options` | `Options` | `{schedulePolicy: SchedulePolicy.Immediately, clearPolicy: ClearPolicy.Cancel}` | Options to control various aspects of the timeout such as whether it starts immediately or not, and whether the scheduled action is invoked on clear or not. The clear policy only takes effect when the [clearAll](#clearall) is invoked on parent the `IScheduleActions` instance, such as when unmounting; this does not affect calls to the `clear` method on the returned `ITimeout` interface. For more on policies, see [Policies](#policies). |
190
188
  | _returns_ | [`ITimeout`](#itimeout) | | An interface for manipulating the created timeout. |
191
189
 
192
190
  ##### interval
@@ -195,8 +193,7 @@ in the standard timer API.
195
193
  interval(
196
194
  action: () => mixed,
197
195
  period: number,
198
- autoSchedule?: boolean,
199
- resolveOnClear?: boolean,
196
+ options?: Options,
200
197
  ): IInterval;
201
198
  ```
202
199
 
@@ -207,8 +204,7 @@ in the standard timer API.
207
204
  | --- | --- | --- | --- |
208
205
  | `action` | `()` `=>` `void` | _Required_ | The action to be invoked when the interval period occurs. |
209
206
  | `period` | `number` | _Required_ | The interval period in milliseconds. The action will be invoked each time this period has passed since the interval was set or last occurred. This value must be greater than zero. |
210
- | `autoSchedule` | `boolean` | `true` | Whether or not to set the interval as soon as this call is made, or wait until `set` is explicitly called. Defaults to `true`. |
211
- | `resolveOnClear` | `boolean` | `false` | Whether or not the associated action will be invoked at the point the interval is cleared if the interval is running at that time. Defaults to `false`. This only takes effect when the [clearAll](#clearall) is invoked on parent the `IScheduleActions` instance, such as when unmounting; this does not affect calls to the `clear` method on the returned `IInterval` interface. |
207
+ | `options` | `Options` | `{schedulePolicy: SchedulePolicy.Immediately, clearPolicy: ClearPolicy.Cancel}` | Options to control various aspects of the interval such as whether it starts immediately or not, and whether the scheduled action is invoked on clear or not. The clear policy only takes effect when the [clearAll](#clearall) is invoked on parent the `IScheduleActions` instance, such as when unmounting; this does not affect calls to the `clear` method on the returned `IInterval` interface. For more on policies, see [Policies](#policies). |
212
208
  | _returns_ | [`IInterval`](#iinterval) | | An interface for manipulating the created interval. |
213
209
 
214
210
  ##### animationFrame
@@ -216,8 +212,7 @@ in the standard timer API.
216
212
  ```js static
217
213
  animationFrame(
218
214
  action: () => void,
219
- autoSchedule?: boolean,
220
- resolveOnClear?: boolean,
215
+ options?: Options,
221
216
  ): IAnimationFrame;
222
217
  ```
223
218
 
@@ -227,8 +222,7 @@ in the standard timer API.
227
222
  | | Flow Type | Default | Description |
228
223
  | --- | --- | --- | --- |
229
224
  | `action` | `()` `=>` `void` | _Required_ | The action to be invoked before the repaint. |
230
- | `autoSchedule` | `boolean` | `true` | Whether or not to make the request as soon as this call is made, or wait until `set` is explicitly called. Defaults to `true`. |
231
- | `resolveOnClear` | `boolean` | `false` | Whether or not the associated action will be invoked at the point the request is cleared if it has not yet executed. Defaults to `false`. This only takes effect when the [clearAll](#clearall) is invoked on parent the `IScheduleActions` instance, such as when unmounting; this does not affect calls to the `clear` method on the returned `IAnimationFrame` interface. |
225
+ | `options` | `Options` | `{schedulePolicy: SchedulePolicy.Immediately, clearPolicy: ClearPolicy.Cancel}` | Options to control various aspects of the animation frame such as whether it starts immediately or not, and whether the scheduled action is invoked on clear or not. The clear policy only takes effect when the [clearAll](#clearall) is invoked on parent the `IScheduleActions` instance, such as when unmounting; this does not affect calls to the `clear` method on the returned `IAnimationFrame` interface. For more on policies, see [Policies](#policies). |
232
226
  | _returns_ | [`IAnimationFrame`](#ianimationframe) | | An interface for manipulating the created request. |
233
227
 
234
228
  ##### clearAll
@@ -241,13 +235,29 @@ Clears all timeouts, intervals, and animation frame requests that were made with
241
235
 
242
236
  #### Types
243
237
 
238
+ ##### Policies
239
+
240
+ ###### SchedulePolicy
241
+
242
+ | Policy | Value | Description |
243
+ | --- | --- | --- |
244
+ | `OnDemand` | `"schedule-on-demand"` | The scheduled action's timing will begin when `set` is called. |
245
+ | `Immediately` | `"schedule-immediately"` | The scheduled action's timing will begin immediately. |
246
+
247
+ ###### ClearPolicy
248
+
249
+ | Policy | Value | Description |
250
+ | --- | --- | --- |
251
+ | `Cancel` | `"cancel-on-clear"` | The action, if set at the time of applying the policy, will be cancelled without being invoked. |
252
+ | `Resolve` | `"resolve-on-clear"` | The action, if set at the time of applying the policy, will be invoked as if the scheduled time had occurred. |
253
+
244
254
  ##### ITimeout
245
255
 
246
256
  ```js static
247
257
  interface ITimeout {
248
258
  get isSet(): boolean;
249
259
  set(): void;
250
- clear(resolve?: boolean): void;
260
+ clear(clearPolicy?: ClearPolicy = ClearPolicies.Cancel): void;
251
261
  }
252
262
  ```
253
263
 
@@ -257,7 +267,7 @@ The `ITimeout` interface provides additional calls to manipulate a timeout, if s
257
267
  | --- | --- | --- |
258
268
  | `isSet` | `boolean` | A read-only property for determining if the timeout is set or not. Returns `true` if the timeout is set (aka pending), otherwise `false`. |
259
269
  | `set` | `()` `=>` `void` | If the timeout is pending, this cancels that pending timeout and starts the timeout afresh. If the timeout is not pending, this starts the timeout. Can be used to re-schedule an already invoked or cleared timeout. |
260
- | `clear` | `(resolve?:` `boolean)` `=>` `void` | If the timeout is pending, this cancels that pending timeout. If no timeout is pending, this does nothing. When the optional `resolve` argument is `true`, and the timeout was in the set state when called, the timeout action is invoked after cancelling the timeout. The `resolve` parameter defaults to `false`. This call does nothing if there was no pending timeout (i.e. when `isSet` is `false`). |
270
+ | `clear` | `(clearPolicy?:` `ClearPolicy)` `=>` `void` | If the timeout is pending, this cancels that pending timeout. If no timeout is pending, this does nothing. When the optional `clearPolicy` argument is `ClearPolicy.Resolve`, and the timeout was in the set state when called, the timeout action is invoked after cancelling the timeout. The `clearPolicy` parameter defaults to `ClearPolicy.Cancel`. This call does nothing if there was no pending timeout (i.e. when `isSet` is `false`). |
261
271
 
262
272
  ##### IInterval
263
273
 
@@ -265,7 +275,7 @@ The `ITimeout` interface provides additional calls to manipulate a timeout, if s
265
275
  interface IInterval {
266
276
  get isSet(): boolean;
267
277
  set(): void;
268
- clear(resolve?: boolean): void;
278
+ clear(clearPolicy?: ClearPolicy = ClearPolicies.Cancel): void;
269
279
  }
270
280
  ```
271
281
 
@@ -275,7 +285,7 @@ The `IInterval` interface provides additional calls to manipulate an interval, i
275
285
  | --- | --- | --- |
276
286
  | `isSet` | `boolean` | A read-only property for determining if the interval is active or not. Returns `true` if the interval is active, otherwise `false`. |
277
287
  | `set` | `()` `=>` `void` | If the interval is active, this cancels that interval and restarts it afresh. If the interval is not active, this starts the interval. Can be used to re-schedule a cleared interval. |
278
- | `clear` | `(resolve?:` `boolean)` `=>` `void` | If the interval is active, this cancels that interval. If the interval is not active, this does nothing. When the optional `resolve` argument is `true`, and the interval was in the active state when called, the associated action is invoked after cancelling the interval. The `resolve` parameter defaults to `false`. This call does nothing if there was no pending interval (i.e. when `isSet` is `false`). |
288
+ | `clear` | `(clearPolicy?:` `ClearPolicy)` `=>` `void` | If the interval is active, this cancels that interval. If the interval is not active, this does nothing. When the optional `clearPolicy` argument is `ClearPolicy.Resolve`, and the interval was in the active state when called, the associated action is invoked after cancelling the interval. The `clearPolicy` parameter defaults to `ClearPolicy.Cancel`. This call does nothing if there was no active interval (i.e. when `isSet` is `false`). |
279
289
 
280
290
  ##### IAnimationFrame
281
291
 
@@ -283,7 +293,7 @@ The `IInterval` interface provides additional calls to manipulate an interval, i
283
293
  interface IAnimationFrame {
284
294
  get isSet(): boolean;
285
295
  set(): void;
286
- clear(resolve?: boolean): void;
296
+ clear(clearPolicy?: ClearPolicy = ClearPolicies.Cancel): void;
287
297
  }
288
298
  ```
289
299
 
@@ -293,7 +303,7 @@ The `IAnimationFrame` interface provides additional calls to manipulate an anima
293
303
  | --- | --- | --- |
294
304
  | `isSet` | `boolean` | A read-only property for determining if the request is set (aka pending). Returns `true` if the animation frame is set, otherwise `false`. |
295
305
  | `set` | `()` `=>` `void` | If the request is pending, this cancels that pending request and starts a request afresh. If the request is not pending, this starts the request. Can be used to re-request an already invokd or cleared request. |
296
- | `clear` | `(resolve?:` `boolean)` `=>` `void` | If the request is pending, this cancels that pending request. If no request is pending, this does nothing. When the optional `resolve` argument is `true`, and the request was in the active state when called, the associated action is invoked after cancelling the request. The `resolve` parameter defaults to `false`. This call does nothing if there was no pending request (i.e. when `isSet` is `false`). |
306
+ | `clear` | `(clearPolicy?:` `ClearPolicy)` `=>` `void` | If the request is pending, this cancels that pending request. If no request is pending, this does nothing. When the optional `clearPolicy` argument is `ClearPolicy.Resolve`, and the request was in the set state when called, the associated action is invoked after cancelling the requst. The `clearPolicy` parameter defaults to `ClearPolicy.Cancel`. This call does nothing if there was no pending request (i.e. when `isSet` is `false`). |
297
307
 
298
308
  ### Migration from standard API
299
309
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-timing",
3
3
  "private": false,
4
- "version": "2.0.0",
4
+ "version": "2.1.0",
5
5
  "design": "v1",
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -14,12 +14,11 @@
14
14
  "test": "echo \"Error: no test specified\" && exit 1"
15
15
  },
16
16
  "peerDependencies": {
17
- "react": "^16.4.1"
17
+ "react": "16.14.0"
18
18
  },
19
19
  "devDependencies": {
20
- "wb-dev-build-settings": "^0.1.0"
20
+ "wb-dev-build-settings": "^0.2.0"
21
21
  },
22
22
  "author": "",
23
- "license": "MIT",
24
- "gitHead": "ddf9842bf1e9502a3287be19fe01544c3823d116"
23
+ "license": "MIT"
25
24
  }
@@ -133,9 +133,8 @@ describe("wonder-blocks-timing", () => {
133
133
  }
134
134
  }
135
135
 
136
- const MyGoodComponentWithScheduler = withActionScheduler(
137
- MyGoodComponent,
138
- );
136
+ const MyGoodComponentWithScheduler =
137
+ withActionScheduler(MyGoodComponent);
139
138
  const example = (
140
139
  <IDProvider>
141
140
  {(id) => (
@@ -1,6 +1,7 @@
1
1
  // @flow
2
2
  import * as React from "react";
3
3
  import {mount} from "enzyme";
4
+ import "jest-enzyme";
4
5
 
5
6
  import ActionSchedulerProvider from "../action-scheduler-provider.js";
6
7
  import ActionScheduler from "../../util/action-scheduler.js";
@@ -1,6 +1,7 @@
1
1
  // @flow
2
2
  import * as React from "react";
3
3
  import {mount} from "enzyme";
4
+ import "jest-enzyme";
4
5
 
5
6
  import withActionScheduler from "../with-action-scheduler.js";
6
7
 
@@ -0,0 +1,124 @@
1
+ // @flow
2
+ import {renderHook} from "@testing-library/react-hooks";
3
+
4
+ import {useInterval} from "../use-interval.js";
5
+
6
+ describe("useTimeout", () => {
7
+ beforeEach(() => {
8
+ jest.useFakeTimers();
9
+ });
10
+
11
+ it("should not fire if 'active' is false", () => {
12
+ // Arrange
13
+ const action = jest.fn();
14
+
15
+ // Act
16
+ renderHook(() => useInterval(action, 500, false));
17
+ jest.advanceTimersByTime(501);
18
+
19
+ // Assert
20
+ expect(action).not.toHaveBeenCalled();
21
+ });
22
+
23
+ it("should fire the action multiple times based on 'intervalMs'", () => {
24
+ // Arrange
25
+ const action = jest.fn();
26
+
27
+ // Act
28
+ renderHook(() => useInterval(action, 500, true));
29
+ jest.advanceTimersByTime(1001);
30
+
31
+ // Assert
32
+ expect(action).toHaveBeenCalledTimes(2);
33
+ });
34
+
35
+ it("should fire after time after 'active' changes to true", () => {
36
+ // Arrange
37
+ const action = jest.fn();
38
+
39
+ // Act
40
+ const {rerender} = renderHook(
41
+ ({active}) => useInterval(action, 500, active),
42
+ {initialProps: {active: false}},
43
+ );
44
+ rerender({active: true});
45
+ jest.advanceTimersByTime(501);
46
+
47
+ // Assert
48
+ expect(action).toHaveBeenCalled();
49
+ });
50
+
51
+ it("should not fire after 'intervalMs' if 'active' changes to false", () => {
52
+ // Arrange
53
+ const action = jest.fn();
54
+ const {rerender} = renderHook(
55
+ ({active}) => useInterval(action, 500, active),
56
+ {initialProps: {active: true}},
57
+ );
58
+
59
+ // Act
60
+ rerender({active: false});
61
+ jest.advanceTimersByTime(501);
62
+
63
+ // Assert
64
+ expect(action).not.toHaveBeenCalled();
65
+ });
66
+
67
+ it("should reset the interval if 'intervalMs' is changes", () => {
68
+ // Arrange
69
+ const action = jest.fn();
70
+
71
+ // Act
72
+ const {rerender} = renderHook(
73
+ ({timeoutMs}) => useInterval(action, timeoutMs, true),
74
+ {initialProps: {timeoutMs: 500}},
75
+ );
76
+ rerender({timeoutMs: 1000});
77
+ jest.advanceTimersByTime(501);
78
+
79
+ // Assert
80
+ expect(action).not.toHaveBeenCalled();
81
+ jest.advanceTimersByTime(1001);
82
+ expect(action).toHaveBeenCalled();
83
+ });
84
+
85
+ it("should not reset the interval if 'action' changes", () => {
86
+ // Arrange
87
+ const action1 = jest.fn();
88
+ const action2 = jest.fn();
89
+ const timeoutSpy = jest.spyOn(window, "setTimeout");
90
+
91
+ // Act
92
+ const {rerender} = renderHook(
93
+ ({action}) => useInterval(action, 500, true),
94
+ {initialProps: {action: action1}},
95
+ );
96
+ // NOTE: For some reason setTimeout is called twice by the time we get
97
+ // here. I've verified that it only gets called once inside the hook
98
+ // so something else must be calling it.
99
+ const callCount = timeoutSpy.mock.calls.length;
100
+ rerender({action: action2});
101
+ jest.advanceTimersByTime(501);
102
+
103
+ // Assert
104
+ expect(timeoutSpy).toHaveBeenCalledTimes(callCount);
105
+ });
106
+
107
+ it("should fire the current action if 'action' changes", () => {
108
+ // Arrange
109
+ const action1 = jest.fn();
110
+ const action2 = jest.fn();
111
+
112
+ // Act
113
+ const {rerender} = renderHook(
114
+ ({action}) => useInterval(action, 500, true),
115
+ {initialProps: {action: action1}},
116
+ );
117
+ rerender({action: action2});
118
+ jest.advanceTimersByTime(501);
119
+
120
+ // Assert
121
+ expect(action1).not.toHaveBeenCalledWith();
122
+ expect(action2).toHaveBeenCalledWith();
123
+ });
124
+ });