@khanacademy/wonder-blocks-timing 2.1.0 → 2.1.1

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.
Files changed (39) hide show
  1. package/CHANGELOG.md +13 -6
  2. package/dist/es/index.js +8 -261
  3. package/dist/index.js +246 -724
  4. package/dist/index.js.flow +1 -1
  5. package/package.json +2 -2
  6. package/src/__docs__/_overview_.stories.mdx +17 -0
  7. package/src/components/__docs__/migration.stories.mdx +112 -0
  8. package/{docs.md → src/components/__docs__/types.ischedule-actions.stories.mdx} +11 -260
  9. package/src/components/__docs__/with-action-scheduler-examples.js +80 -0
  10. package/src/components/__docs__/with-action-scheduler.stories.mdx +218 -0
  11. package/src/components/__tests__/action-scheduler-provider.test.js +7 -8
  12. package/src/components/__tests__/with-action-scheduler.test.js +6 -7
  13. package/src/components/action-scheduler-provider.js +2 -2
  14. package/src/components/with-action-scheduler.js +2 -2
  15. package/src/hooks/{use-interval.stories.mdx → __docs__/use-interval.stories.mdx} +1 -1
  16. package/src/hooks/{use-scheduled-interval.stories.mdx → __docs__/use-scheduled-interval.stories.mdx} +2 -2
  17. package/src/hooks/{use-scheduled-timeout.stories.mdx → __docs__/use-scheduled-timeout.stories.mdx} +2 -2
  18. package/src/hooks/{use-timeout.stories.mdx → __docs__/use-timeout.stories.mdx} +1 -1
  19. package/src/hooks/__tests__/use-interval.test.js +1 -1
  20. package/src/hooks/__tests__/use-scheduled-interval.test.js +10 -2
  21. package/src/hooks/__tests__/use-scheduled-timeout.test.js +13 -3
  22. package/src/hooks/__tests__/use-timeout.test.js +1 -1
  23. package/src/hooks/use-interval.js +1 -1
  24. package/src/hooks/use-scheduled-interval.js +4 -4
  25. package/src/hooks/use-scheduled-timeout.js +4 -4
  26. package/src/hooks/use-timeout.js +1 -1
  27. package/src/index.js +7 -7
  28. package/src/util/__tests__/action-scheduler.test.js +9 -9
  29. package/src/util/__tests__/animation-frame.test.js +2 -2
  30. package/src/util/__tests__/interval.test.js +2 -2
  31. package/src/util/__tests__/timeout.test.js +2 -2
  32. package/src/util/action-scheduler.js +4 -4
  33. package/src/util/animation-frame.js +2 -2
  34. package/src/util/interval.js +2 -2
  35. package/src/util/timeout.js +2 -2
  36. package/src/util/types.flowtest.js +2 -2
  37. package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +0 -353
  38. package/src/__tests__/generated-snapshot.test.js +0 -155
  39. package/src/components/with-action-scheduler.md +0 -18
package/CHANGELOG.md CHANGED
@@ -1,11 +1,18 @@
1
1
  # @khanacademy/wonder-blocks-timing
2
2
 
3
+ ## 2.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 91cb727c: Remove file extensions from imports
8
+
3
9
  ## 2.1.0
10
+
4
11
  ### Minor Changes
5
12
 
6
- - 029b4810: Adds `useInterval()` hook that mimics the behavior of `ActionScheduler`'s
7
- `interval()` method.
8
- - c57cd770: Rename `useInterval` and `useTimeout` to `useScheduledInterval`
9
- and `useScheduledTimeout` respectively.
10
- - 29766c8e: Add `useInterval` and `useTimeout` hooks to provide a simple API for
11
- using intervals and timeouts.
13
+ - 029b4810: Adds `useInterval()` hook that mimics the behavior of `ActionScheduler`'s
14
+ `interval()` method.
15
+ - c57cd770: Rename `useInterval` and `useTimeout` to `useScheduledInterval`
16
+ and `useScheduledTimeout` respectively.
17
+ - 29766c8e: Add `useInterval` and `useTimeout` hooks to provide an API for
18
+ using intervals and timeouts.
package/dist/es/index.js CHANGED
@@ -11,29 +11,7 @@ const ClearPolicy = {
11
11
  Cancel: "cancel-on-clear"
12
12
  };
13
13
 
14
- /**
15
- * Encapsulates everything associated with calling setTimeout/clearTimeout, and
16
- * managing the lifecycle of that timer, including the ability to resolve or
17
- * cancel a pending timeout action.
18
- *
19
- * @export
20
- * @class Timeout
21
- * @implements {ITimeout}
22
- */
23
14
  class Timeout {
24
- /**
25
- * Creates a timeout that will invoke the given action after
26
- * the given period. The timeout does not start until set is called.
27
- *
28
- * @param {() => mixed} action The action to be invoked when the timeout
29
- * period has passed.
30
- * @param {number} timeoutMs The timeout period.
31
- * @param {SchedulePolicy} [schedulePolicy] When SchedulePolicy.Immediately,
32
- * the timer is set immediately on instantiation; otherwise, `set` must be
33
- * called to set the timeout.
34
- * Defaults to `SchedulePolicy.Immediately`.
35
- * @memberof Timeout
36
- */
37
15
  constructor(action, timeoutMs, schedulePolicy = SchedulePolicy.Immediately) {
38
16
  if (typeof action !== "function") {
39
17
  throw new Error("Action must be a function");
@@ -50,28 +28,10 @@ class Timeout {
50
28
  this.set();
51
29
  }
52
30
  }
53
- /**
54
- * Determine if the timeout is set or not.
55
- *
56
- * @returns {boolean} true if the timeout is set (aka pending), otherwise
57
- * false.
58
- * @memberof Timeout
59
- */
60
-
61
31
 
62
32
  get isSet() {
63
33
  return this._timeoutId != null;
64
34
  }
65
- /**
66
- * Set the timeout.
67
- *
68
- * If the timeout is pending, this cancels that pending timeout and
69
- * sets the timeout afresh. If the timeout is not pending, this
70
- * sets a new timeout.
71
- *
72
- * @memberof Timeout
73
- */
74
-
75
35
 
76
36
  set() {
77
37
  if (this.isSet) {
@@ -80,21 +40,6 @@ class Timeout {
80
40
 
81
41
  this._timeoutId = setTimeout(() => this.clear(ClearPolicy.Resolve), this._timeoutMs);
82
42
  }
83
- /**
84
- * Clear the set timeout.
85
- *
86
- * If the timeout is pending, this cancels that pending timeout without
87
- * invoking the action. If no timeout is pending, this does nothing.
88
- *
89
- * @param {ClearPolicy} [policy] When ClearPolicy.Resolve, if the request
90
- * was set when called, the request action is invoked after cancelling
91
- * the request; otherwise, the pending action is cancelled.
92
- * Defaults to `ClearPolicy.Cancel`.
93
- *
94
- * @returns {void}
95
- * @memberof Timeout
96
- */
97
-
98
43
 
99
44
  clear(policy = ClearPolicy.Cancel) {
100
45
  const timeoutId = this._timeoutId;
@@ -113,29 +58,7 @@ class Timeout {
113
58
 
114
59
  }
115
60
 
116
- /**
117
- * Encapsulates everything associated with calling setInterval/clearInterval,
118
- * and managing the lifecycle of that interval. This includes the ability to
119
- * cancel the interval, and knowing if the interval is active.
120
- *
121
- * @export
122
- * @class Interval
123
- * @implements {IInterval}
124
- */
125
61
  class Interval {
126
- /**
127
- * Creates an interval that will invoke the given action after
128
- * the given period. The interval does not start until set is called.
129
- *
130
- * @param {() => mixed} action The action to be invoked each time the
131
- * interval period has passed.
132
- * @param {number} intervalMs The interval period.
133
- * @param {SchedulePolicy} [schedulePolicy] When SchedulePolicy.Immediately,
134
- * the interval is set immediately on instantiation; otherwise, `set` must be
135
- * called to set the interval.
136
- * Defaults to `SchedulePolicy.Immediately`.
137
- * @memberof Interval
138
- */
139
62
  constructor(action, intervalMs, schedulePolicy = SchedulePolicy.Immediately) {
140
63
  if (typeof action !== "function") {
141
64
  throw new Error("Action must be a function");
@@ -152,26 +75,10 @@ class Interval {
152
75
  this.set();
153
76
  }
154
77
  }
155
- /**
156
- * Determine if the interval is active or not.
157
- *
158
- * @returns {boolean} true if the interval is active, otherwise false.
159
- * @memberof Interval
160
- */
161
-
162
78
 
163
79
  get isSet() {
164
80
  return this._intervalId != null;
165
81
  }
166
- /**
167
- * Activate the interval.
168
- *
169
- * If the interval is active, this cancels that interval and starts the
170
- * interval afresh. If the interval is not active, this starts it.
171
- *
172
- * @memberof Interval
173
- */
174
-
175
82
 
176
83
  set() {
177
84
  if (this.isSet) {
@@ -180,21 +87,6 @@ class Interval {
180
87
 
181
88
  this._intervalId = setInterval(() => this._action(), this._intervalMs);
182
89
  }
183
- /**
184
- * Clear the active interval.
185
- *
186
- * If the interval is active, this cancels that interval. If no interval is
187
- * pending, this does nothing.
188
- *
189
- * @param {ClearPolicy} [policy] When ClearPolicy.Resolve, if the request
190
- * was set when called, the request action is invoked after cancelling
191
- * the request; otherwise, the pending action is cancelled.
192
- * Defaults to `ClearPolicy.Cancel`.
193
- *
194
- * @returns {void}
195
- * @memberof Interval
196
- */
197
-
198
90
 
199
91
  clear(policy = ClearPolicy.Cancel) {
200
92
  const intervalId = this._intervalId;
@@ -213,27 +105,7 @@ class Interval {
213
105
 
214
106
  }
215
107
 
216
- /**
217
- * Encapsulates everything associated with calling requestAnimationFrame/
218
- * cancelAnimationFrame, and managing the lifecycle of that request, including
219
- * the ability to resolve or cancel a pending request action.
220
- *
221
- * @export
222
- * @class AnimationFrame
223
- * @implements {IAnimationFrame}
224
- */
225
108
  class AnimationFrame {
226
- /**
227
- * Creates an animation frame request that will invoke the given action.
228
- * The request is not made until set is called.
229
- *
230
- * @param {DOMHighResTimeStamp => mixed} action The action to be invoked.
231
- * @param {SchedulePolicy} [schedulePolicy] When SchedulePolicy.Immediately,
232
- * the interval is set immediately on instantiation; otherwise, `set` must be
233
- * called to set the interval.
234
- * Defaults to `SchedulePolicy.Immediately`.
235
- * @memberof AnimationFrame
236
- */
237
109
  constructor(action, schedulePolicy = SchedulePolicy.Immediately) {
238
110
  if (typeof action !== "function") {
239
111
  throw new Error("Action must be a function");
@@ -245,28 +117,10 @@ class AnimationFrame {
245
117
  this.set();
246
118
  }
247
119
  }
248
- /**
249
- * Determine if the request is pending or not.
250
- *
251
- * @returns {boolean} true if the request is pending, otherwise
252
- * false.
253
- * @memberof AnimationFrame
254
- */
255
-
256
120
 
257
121
  get isSet() {
258
122
  return this._animationFrameId != null;
259
123
  }
260
- /**
261
- * Make the animation frame request.
262
- *
263
- * If the request is pending, this cancels that pending request and
264
- * makes the request afresh. If the request is not pending, this
265
- * makes a new request.
266
- *
267
- * @memberof AnimationFrame
268
- */
269
-
270
124
 
271
125
  set() {
272
126
  if (this.isSet) {
@@ -275,24 +129,6 @@ class AnimationFrame {
275
129
 
276
130
  this._animationFrameId = requestAnimationFrame(time => this.clear(ClearPolicy.Resolve, time));
277
131
  }
278
- /**
279
- * Clear the pending request.
280
- *
281
- * If the request is pending, this cancels that pending request without
282
- * invoking the action. If no request is pending, this does nothing.
283
- *
284
- * @param {ClearPolicy} [policy] When ClearPolicy.Resolve, if the request
285
- * was set when called, the request action is invoked after cancelling
286
- * the request; otherwise, the pending action is cancelled.
287
- * Defaults to `ClearPolicy.Cancel`.
288
- * @param {DOMHighResTimeStamp} [time] Timestamp to pass to the action when
289
- * ClearPolicy.Resolve is specified. Ignored when ClearPolicy.Cancel is
290
- * specified.
291
- *
292
- * @returns {void}
293
- * @memberof AnimationFrame
294
- */
295
-
296
132
 
297
133
  clear(policy = ClearPolicy.Cancel, time) {
298
134
  const animationFrameId = this._animationFrameId;
@@ -311,12 +147,6 @@ class AnimationFrame {
311
147
 
312
148
  }
313
149
 
314
- /**
315
- * Implements the `IScheduleActions` API to provide timeout, interval, and
316
- * animation frame support. This is not intended for direct use, but instead
317
- * is to be used solely by the `ActionSchedulerProvider` to provide an
318
- * `IScheduleActions` instance.
319
- */
320
150
  class ActionScheduler {
321
151
  constructor() {
322
152
  this._disabled = false;
@@ -364,11 +194,6 @@ class ActionScheduler {
364
194
  this._registeredActions = [];
365
195
  registered.forEach(clearFn => clearFn());
366
196
  }
367
- /**
368
- * Prevents this scheduler from creating any additional actions.
369
- * This also clears any pending actions.
370
- */
371
-
372
197
 
373
198
  disable() {
374
199
  this._disabled = true;
@@ -386,16 +211,6 @@ ActionScheduler.NoopAction = {
386
211
  clear: () => {}
387
212
  };
388
213
 
389
- /**
390
- * A provider component that passes our action scheduling API to its children
391
- * and ensures that all scheduled actions are cleared on unmount.
392
- *
393
- * ```jsx
394
- * <ActionSchedulerProvider>
395
- * {schedule => this.renderThingThatNeedsTimers(schedule)}
396
- * </ActionSchedulerProvider>
397
- * ```
398
- */
399
214
  class ActionSchedulerProvider extends React.Component {
400
215
  constructor(...args) {
401
216
  super(...args);
@@ -415,33 +230,13 @@ class ActionSchedulerProvider extends React.Component {
415
230
 
416
231
  }
417
232
 
418
- /**
419
- * A higher order component that attaches the given component to an
420
- * `IScheduleActions` instance. Any actions scheduled will automatically be
421
- * cleared on unmount.
422
- *
423
- * @template TOwnProps The own props of the component being rendered, without
424
- * the additional action scheduler prop. To attach the additional prop to
425
- * these props use the `WithActionScheduler` type.
426
- */
427
233
  function withActionScheduler(WrappedComponent) {
428
- return /*#__PURE__*/React.forwardRef((props, ref) => /*#__PURE__*/React.createElement(ActionSchedulerProvider, null, schedule => /*#__PURE__*/React.createElement(WrappedComponent, _extends({}, props, {
234
+ return React.forwardRef((props, ref) => React.createElement(ActionSchedulerProvider, null, schedule => React.createElement(WrappedComponent, _extends({}, props, {
429
235
  ref: ref,
430
236
  schedule: schedule
431
237
  }))));
432
238
  }
433
239
 
434
- /**
435
- * Returns a ref whose .current value is updated whenever
436
- * the `value` passed to this hook changes.
437
- *
438
- * this is great for values that you want to reference from
439
- * within a useCallback or useEffect event listener, without
440
- * re-triggering the effect when the value changes
441
- *
442
- * @returns {{current: T}}
443
- */
444
-
445
240
  const useUpdatingRef = value => {
446
241
  const ref = useRef(value);
447
242
  useEffect(() => {
@@ -450,17 +245,7 @@ const useUpdatingRef = value => {
450
245
  return ref;
451
246
  };
452
247
 
453
- /**
454
- * A simple hook for using `setInterval`.
455
- *
456
- * @param action called every `intervalMs` when `active` is true
457
- * @param intervalMs the duration between calls to `action`
458
- * @param active whether or not the interval is active
459
- */
460
-
461
248
  function useInterval(action, intervalMs, active) {
462
- // We using a ref instead of a callback for `action` to avoid resetting
463
- // the interval whenever the `action` changes.
464
249
  const actionRef = useUpdatingRef(action);
465
250
  useEffect(() => {
466
251
  if (active) {
@@ -470,25 +255,11 @@ function useInterval(action, intervalMs, active) {
470
255
  return () => {
471
256
  clearInterval(intervalId);
472
257
  };
473
- } // actionRef isn't actually required, but react-hooks/exhaustive-deps
474
- // doesn't recognize it as a ref and thus complains if it isn't in the
475
- // deps list. It isn't a big deal though since the value ofactionRef
476
- // never changes (only its contents do).
477
-
258
+ }
478
259
  }, [intervalMs, active, actionRef]);
479
260
  }
480
261
 
481
- /**
482
- * A simple hook for using `setTimeout`.
483
- *
484
- * @param action called after `timeoutMs` when `active` is true
485
- * @param timeoutMs the duration after which `action` is called
486
- * @param active whether or not the interval is active
487
- */
488
-
489
262
  function useTimeout(action, timeoutMs, active) {
490
- // We using a ref instead of a callback for `action` to avoid resetting
491
- // the interval whenever the `action` changes.
492
263
  const actionRef = useUpdatingRef(action);
493
264
  useEffect(() => {
494
265
  if (active) {
@@ -498,11 +269,7 @@ function useTimeout(action, timeoutMs, active) {
498
269
  return () => {
499
270
  clearTimeout(timeoutId);
500
271
  };
501
- } // actionRef isn't actually required, but react-hooks/exhaustive-deps
502
- // doesn't recognize it as a ref and thus complains if it isn't in the
503
- // deps list. It isn't a big deal though since the value ofactionRef
504
- // never changes (only its contents do).
505
-
272
+ }
506
273
  }, [timeoutMs, active, actionRef]);
507
274
  }
508
275
 
@@ -531,23 +298,14 @@ function useScheduledInterval(action, intervalMs, options) {
531
298
  }
532
299
 
533
300
  setIsSet(false);
534
- }, // react-hooks/exhaustive-deps doesn't require refs to be
535
- // listed in the deps array. Unfortunately, in this situation
536
- // it doesn't recognized actionRef as a ref.
537
- [actionRef, isSet, options == null ? void 0 : options.clearPolicy]);
301
+ }, [actionRef, isSet, options == null ? void 0 : options.clearPolicy]);
538
302
  const runOnUnmountRef = useUpdatingRef(isSet && (options == null ? void 0 : options.clearPolicy) === ClearPolicy.Resolve);
539
303
  useEffect(() => {
540
304
  return () => {
541
- // This code will only run with the component using this
542
- // hook is unmounted.
543
- // eslint-disable-next-line react-hooks/exhaustive-deps
544
305
  if (runOnUnmountRef.current) {
545
- // eslint-disable-next-line react-hooks/exhaustive-deps
546
306
  actionRef.current();
547
307
  }
548
- }; // This eslint rule doesn't realize actionRef and runOnUnmountRef
549
- // a both refs and thus do not have to be listed as deps.
550
- // eslint-disable-next-line react-hooks/exhaustive-deps
308
+ };
551
309
  }, []);
552
310
  useInterval(action, intervalMs, isSet);
553
311
  return {
@@ -570,9 +328,7 @@ function useScheduledTimeout(action, timeoutMs, options) {
570
328
 
571
329
  const schedulePolicy = (_options$schedulePoli = options == null ? void 0 : options.schedulePolicy) != null ? _options$schedulePoli : SchedulePolicy.Immediately;
572
330
  const [isSet, setIsSet] = useState(schedulePolicy === SchedulePolicy.Immediately);
573
- const set = useCallback(() => setIsSet(true), []); // This wrapper isn't present in useScheduledInterval because we
574
- // don't need to update `isSet` in that situations.
575
-
331
+ const set = useCallback(() => setIsSet(true), []);
576
332
  const wrappedAction = useCallback(() => {
577
333
  setIsSet(false);
578
334
  action();
@@ -588,23 +344,14 @@ function useScheduledTimeout(action, timeoutMs, options) {
588
344
  }
589
345
 
590
346
  setIsSet(false);
591
- }, // react-hooks/exhaustive-deps doesn't require refs to be
592
- // listed in the deps array. Unfortunately, in this situation
593
- // it doesn't recognized actionRef as a ref.
594
- [actionRef, isSet, options == null ? void 0 : options.clearPolicy]);
347
+ }, [actionRef, isSet, options == null ? void 0 : options.clearPolicy]);
595
348
  const runOnUnmountRef = useUpdatingRef(isSet && (options == null ? void 0 : options.clearPolicy) === ClearPolicy.Resolve);
596
349
  useEffect(() => {
597
350
  return () => {
598
- // This code will only run with the component using this
599
- // hook is unmounted.
600
- // eslint-disable-next-line react-hooks/exhaustive-deps
601
351
  if (runOnUnmountRef.current) {
602
- // eslint-disable-next-line react-hooks/exhaustive-deps
603
352
  actionRef.current();
604
353
  }
605
- }; // This eslint rule doesn't realize actionRef and runOnUnmountRef
606
- // a both refs and thus do not have to be listed as deps.
607
- // eslint-disable-next-line react-hooks/exhaustive-deps
354
+ };
608
355
  }, []);
609
356
  useTimeout(wrappedAction, timeoutMs, isSet);
610
357
  return {