@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/CHANGELOG.md +11 -0
- package/dist/es/index.js +188 -4
- package/dist/index.js +462 -179
- package/docs.md +28 -18
- package/package.json +4 -5
- package/src/__tests__/generated-snapshot.test.js +2 -3
- package/src/components/__tests__/action-scheduler-provider.test.js +1 -0
- package/src/components/__tests__/with-action-scheduler.test.js +1 -0
- package/src/hooks/__tests__/use-interval.test.js +124 -0
- package/src/hooks/__tests__/use-scheduled-interval.test.js +453 -0
- package/src/hooks/__tests__/use-scheduled-timeout.test.js +469 -0
- package/src/hooks/__tests__/use-timeout.test.js +136 -0
- package/src/hooks/internal/use-updating-ref.js +20 -0
- package/src/hooks/use-interval.js +37 -0
- package/src/hooks/use-interval.stories.mdx +80 -0
- package/src/hooks/use-scheduled-interval.js +73 -0
- package/src/hooks/use-scheduled-interval.stories.mdx +147 -0
- package/src/hooks/use-scheduled-timeout.js +80 -0
- package/src/hooks/use-scheduled-timeout.stories.mdx +148 -0
- package/src/hooks/use-timeout.js +37 -0
- package/src/hooks/use-timeout.stories.mdx +80 -0
- package/src/index.js +4 -0
- package/src/util/__tests__/animation-frame.test.js +6 -8
- package/src/util/__tests__/interval.test.js +31 -14
- package/src/util/__tests__/timeout.test.js +18 -8
- package/LICENSE +0 -21
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
|
-
|
|
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
|
-
| `
|
|
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
|
-
|
|
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
|
-
| `
|
|
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
|
-
|
|
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
|
-
| `
|
|
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(
|
|
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` | `(
|
|
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(
|
|
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` | `(
|
|
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(
|
|
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` | `(
|
|
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.
|
|
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": "
|
|
17
|
+
"react": "16.14.0"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"wb-dev-build-settings": "^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 =
|
|
137
|
-
MyGoodComponent
|
|
138
|
-
);
|
|
136
|
+
const MyGoodComponentWithScheduler =
|
|
137
|
+
withActionScheduler(MyGoodComponent);
|
|
139
138
|
const example = (
|
|
140
139
|
<IDProvider>
|
|
141
140
|
{(id) => (
|
|
@@ -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
|
+
});
|