@khanacademy/wonder-blocks-data 2.3.4 → 3.0.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/es/index.js +212 -446
  3. package/dist/index.js +230 -478
  4. package/docs.md +19 -13
  5. package/package.json +2 -3
  6. package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +40 -160
  7. package/src/__tests__/generated-snapshot.test.js +15 -195
  8. package/src/components/__tests__/data.test.js +159 -965
  9. package/src/components/__tests__/intercept-data.test.js +9 -66
  10. package/src/components/__tests__/track-data.test.js +6 -5
  11. package/src/components/data.js +9 -117
  12. package/src/components/data.md +38 -60
  13. package/src/components/intercept-data.js +2 -34
  14. package/src/components/intercept-data.md +7 -105
  15. package/src/hooks/__tests__/use-data.test.js +790 -0
  16. package/src/hooks/use-data.js +138 -0
  17. package/src/index.js +1 -3
  18. package/src/util/__tests__/memory-cache.test.js +134 -35
  19. package/src/util/__tests__/request-fulfillment.test.js +21 -36
  20. package/src/util/__tests__/request-handler.test.js +30 -30
  21. package/src/util/__tests__/request-tracking.test.js +29 -30
  22. package/src/util/__tests__/response-cache.test.js +521 -561
  23. package/src/util/__tests__/result-from-cache-entry.test.js +68 -0
  24. package/src/util/memory-cache.js +18 -14
  25. package/src/util/request-fulfillment.js +4 -0
  26. package/src/util/request-handler.js +2 -27
  27. package/src/util/request-handler.md +0 -32
  28. package/src/util/response-cache.js +50 -110
  29. package/src/util/result-from-cache-entry.js +38 -0
  30. package/src/util/types.js +14 -35
  31. package/LICENSE +0 -21
  32. package/src/components/__tests__/intercept-cache.test.js +0 -124
  33. package/src/components/__tests__/internal-data.test.js +0 -1030
  34. package/src/components/intercept-cache.js +0 -79
  35. package/src/components/intercept-cache.md +0 -103
  36. package/src/components/internal-data.js +0 -219
  37. package/src/util/__tests__/no-cache.test.js +0 -112
  38. package/src/util/no-cache.js +0 -67
  39. package/src/util/no-cache.md +0 -66
package/docs.md CHANGED
@@ -23,17 +23,17 @@ fulfillAllDataRequests(): Promise<$ReadOnly<ResponseCache>>;
23
23
 
24
24
  ## initializeCache
25
25
 
26
- Wonder Blocks Data caches data in its response cache. This cache can be
27
- initialized with data using the `initializeCache` method. The `initializeData`
28
- method can only be called once to populate the cache. Usually, the data to be
29
- passed to `initializeCache` will be obtained by calling `fulfillAllDataRequests`
30
- after tracking data requests (see [TrackData](#trackdata)) during server-side
31
- rendering.
32
-
33
- Though method could also be used to initialize the response cache for tests,
34
- it is recommended that the `InterceptCache` component be used instead, since
35
- it can be used repeatedly whereas `initializeCache`, by design, can only be
36
- called while the cache is uninitialized and unused.
26
+ Wonder Blocks Data caches data in its response cache for hydration. This cache
27
+ can be initialized with data using the `initializeCache` method.
28
+ The `initializeCache` method can only be called when the hydration cache is
29
+ empty.
30
+
31
+ Usually, the data to be passed to `initializeCache` will be obtained by
32
+ calling `fulfillAllDataRequests` after tracking data requests
33
+ (see [TrackData](#trackdata)) during server-side rendering.
34
+
35
+ Combine with `removeFromCache` or `removeAllFromCache` to support your testing
36
+ needs.
37
37
 
38
38
  ### Usage
39
39
 
@@ -49,10 +49,13 @@ initializeCache(sourceCache: $ReadOnly<ResponseCache>): void;
49
49
 
50
50
  ## removeFromCache
51
51
 
52
- Removes an entry from the framework cache and any custom cache associated to the given handler. The given handler and options identify the entry to be removed.
52
+ Removes an entry associated with the given handler from the hydration cache. The given handler and options identify the entry to be removed.
53
53
 
54
54
  If an item is removed, this returns `true`; otherwise, `false`.
55
55
 
56
+ This can be used after `initializeCache` to manipulate the cache prior to hydration.
57
+ This can be useful during testing.
58
+
56
59
  ### Usage
57
60
 
58
61
  ```js static
@@ -68,10 +71,13 @@ removeFromCache(handler: IRequestHandler<TOptions, TData>, options: TOptions): b
68
71
 
69
72
  ## removeAllFromCache
70
73
 
71
- Removes all entries that match a given predicate from the framework cache and any custom cache associated to the given handler. If no predicate is given, all cached entries for the given handler are removed.
74
+ Removes all entries associated to the given handler that match a given predicate from the hydration cache. If no predicate is given, all cached entries for the given handler are removed.
72
75
 
73
76
  This returns the count of entries removed.
74
77
 
78
+ This can be used after `initializeCache` to manipulate the cache prior to hydration.
79
+ This can be useful during testing (especially to clear the cache so that it can be initialized again).
80
+
75
81
  ### Usage
76
82
 
77
83
  ```js static
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-data",
3
- "version": "2.3.4",
3
+ "version": "3.0.0",
4
4
  "design": "v1",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -23,6 +23,5 @@
23
23
  "wb-dev-build-settings": "^0.2.0"
24
24
  },
25
25
  "author": "",
26
- "license": "MIT",
27
- "gitHead": "9ebea88533e702011165072f090a377e02fa3f0f"
26
+ "license": "MIT"
28
27
  }
@@ -55,111 +55,6 @@ exports[`wonder-blocks-data example 1 1`] = `
55
55
  >
56
56
  This request will succeed and give us data!
57
57
  </span>
58
- Loading...
59
- </div>
60
- <div
61
- aria-hidden="true"
62
- className=""
63
- style={
64
- Object {
65
- "MsFlexBasis": 12,
66
- "MsFlexPreferredSize": 12,
67
- "WebkitFlexBasis": 12,
68
- "alignItems": "stretch",
69
- "borderStyle": "solid",
70
- "borderWidth": 0,
71
- "boxSizing": "border-box",
72
- "display": "flex",
73
- "flexBasis": 12,
74
- "flexDirection": "column",
75
- "flexShrink": 0,
76
- "margin": 0,
77
- "minHeight": 0,
78
- "minWidth": 0,
79
- "padding": 0,
80
- "position": "relative",
81
- "width": 12,
82
- "zIndex": 0,
83
- }
84
- }
85
- />
86
- <div
87
- className=""
88
- style={
89
- Object {
90
- "alignItems": "stretch",
91
- "borderStyle": "solid",
92
- "borderWidth": 0,
93
- "boxSizing": "border-box",
94
- "display": "flex",
95
- "flexDirection": "column",
96
- "margin": 0,
97
- "minHeight": 0,
98
- "minWidth": 0,
99
- "padding": 0,
100
- "position": "relative",
101
- "zIndex": 0,
102
- }
103
- }
104
- >
105
- <span
106
- className=""
107
- style={
108
- Object {
109
- "MozOsxFontSmoothing": "grayscale",
110
- "WebkitFontSmoothing": "antialiased",
111
- "display": "block",
112
- "fontFamily": "Lato, \\"Noto Sans\\", sans-serif",
113
- "fontSize": 16,
114
- "fontWeight": 400,
115
- "lineHeight": "22px",
116
- }
117
- }
118
- >
119
- This request will go boom and give us an error!
120
- </span>
121
- Loading...
122
- </div>
123
- </div>
124
- `;
125
-
126
- exports[`wonder-blocks-data example 2 1`] = `
127
- Array [
128
- <div
129
- className=""
130
- style={
131
- Object {
132
- "alignItems": "stretch",
133
- "borderStyle": "solid",
134
- "borderWidth": 0,
135
- "boxSizing": "border-box",
136
- "display": "flex",
137
- "flexDirection": "column",
138
- "margin": 0,
139
- "minHeight": 0,
140
- "minWidth": 0,
141
- "padding": 0,
142
- "position": "relative",
143
- "zIndex": 0,
144
- }
145
- }
146
- >
147
- <span
148
- className=""
149
- style={
150
- Object {
151
- "MozOsxFontSmoothing": "grayscale",
152
- "WebkitFontSmoothing": "antialiased",
153
- "display": "block",
154
- "fontFamily": "Lato, \\"Noto Sans\\", sans-serif",
155
- "fontSize": 16,
156
- "fontWeight": 400,
157
- "lineHeight": "22px",
158
- }
159
- }
160
- >
161
- This cache has data!
162
- </span>
163
58
  <span
164
59
  className=""
165
60
  style={
@@ -173,10 +68,8 @@ Array [
173
68
  "lineHeight": "22px",
174
69
  }
175
70
  }
176
- >
177
- I'm DATA from the cache
178
- </span>
179
- </div>,
71
+ />
72
+ </div>
180
73
  <div
181
74
  aria-hidden="true"
182
75
  className=""
@@ -202,7 +95,7 @@ Array [
202
95
  "zIndex": 0,
203
96
  }
204
97
  }
205
- />,
98
+ />
206
99
  <div
207
100
  className=""
208
101
  style={
@@ -236,7 +129,7 @@ Array [
236
129
  }
237
130
  }
238
131
  >
239
- This cache has error!
132
+ This request will go boom and give us an error!
240
133
  </span>
241
134
  <span
242
135
  className=""
@@ -254,14 +147,31 @@ Array [
254
147
  }
255
148
  >
256
149
  ERROR:
257
- I'm an ERROR from the cache
258
150
  </span>
259
- </div>,
260
- ]
151
+ </div>
152
+ </div>
261
153
  `;
262
154
 
263
- exports[`wonder-blocks-data example 3 1`] = `
264
- Array [
155
+ exports[`wonder-blocks-data example 2 1`] = `
156
+ <div
157
+ className=""
158
+ style={
159
+ Object {
160
+ "alignItems": "stretch",
161
+ "borderStyle": "solid",
162
+ "borderWidth": 0,
163
+ "boxSizing": "border-box",
164
+ "display": "flex",
165
+ "flexDirection": "column",
166
+ "margin": 0,
167
+ "minHeight": 0,
168
+ "minWidth": 0,
169
+ "padding": 0,
170
+ "position": "relative",
171
+ "zIndex": 0,
172
+ }
173
+ }
174
+ >
265
175
  <div
266
176
  className=""
267
177
  style={
@@ -295,7 +205,7 @@ Array [
295
205
  }
296
206
  }
297
207
  >
298
- This intercepted cache has data!
208
+ This cache has data!
299
209
  </span>
300
210
  <span
301
211
  className=""
@@ -310,10 +220,8 @@ Array [
310
220
  "lineHeight": "22px",
311
221
  }
312
222
  }
313
- >
314
- I'm DATA from the cache interceptor
315
- </span>
316
- </div>,
223
+ />
224
+ </div>
317
225
  <div
318
226
  aria-hidden="true"
319
227
  className=""
@@ -339,7 +247,7 @@ Array [
339
247
  "zIndex": 0,
340
248
  }
341
249
  }
342
- />,
250
+ />
343
251
  <div
344
252
  className=""
345
253
  style={
@@ -373,7 +281,7 @@ Array [
373
281
  }
374
282
  }
375
283
  >
376
- This intercepted cache has error!
284
+ This cache has error!
377
285
  </span>
378
286
  <span
379
287
  className=""
@@ -391,13 +299,12 @@ Array [
391
299
  }
392
300
  >
393
301
  ERROR:
394
- I'm an ERROR from the cache interceptor
395
302
  </span>
396
- </div>,
397
- ]
303
+ </div>
304
+ </div>
398
305
  `;
399
306
 
400
- exports[`wonder-blocks-data example 4 1`] = `
307
+ exports[`wonder-blocks-data example 3 1`] = `
401
308
  <div
402
309
  className=""
403
310
  style={
@@ -433,30 +340,6 @@ exports[`wonder-blocks-data example 4 1`] = `
433
340
  >
434
341
  This received intercepted data!
435
342
  </span>
436
- If you see this, the example is broken!
437
- </div>
438
- `;
439
-
440
- exports[`wonder-blocks-data example 5 1`] = `
441
- <div
442
- className=""
443
- style={
444
- Object {
445
- "alignItems": "stretch",
446
- "borderStyle": "solid",
447
- "borderWidth": 0,
448
- "boxSizing": "border-box",
449
- "display": "flex",
450
- "flexDirection": "column",
451
- "margin": 0,
452
- "minHeight": 0,
453
- "minWidth": 0,
454
- "padding": 0,
455
- "position": "relative",
456
- "zIndex": 0,
457
- }
458
- }
459
- >
460
343
  <span
461
344
  className=""
462
345
  style={
@@ -464,22 +347,19 @@ exports[`wonder-blocks-data example 5 1`] = `
464
347
  "MozOsxFontSmoothing": "grayscale",
465
348
  "WebkitFontSmoothing": "antialiased",
466
349
  "display": "block",
467
- "fontFamily": "Lato, \\"Noto Sans\\", sans-serif",
468
- "fontSize": 16,
350
+ "fontFamily": "Inconsolata, monospace",
351
+ "fontSize": 17,
469
352
  "fontWeight": 400,
470
353
  "lineHeight": "22px",
471
354
  }
472
355
  }
473
- >
474
- This re-renders once a second. On the render, the cache is refreshed and so we see an update.
475
- </span>
476
- If you see this, the example is broken!
356
+ />
477
357
  </div>
478
358
  `;
479
359
 
480
- exports[`wonder-blocks-data example 6 1`] = `"Sorry, no snapshot for you"`;
360
+ exports[`wonder-blocks-data example 4 1`] = `"Sorry, no snapshot for you"`;
481
361
 
482
- exports[`wonder-blocks-data example 7 1`] = `
362
+ exports[`wonder-blocks-data example 5 1`] = `
483
363
  <div
484
364
  className=""
485
365
  style={
@@ -500,8 +380,8 @@ exports[`wonder-blocks-data example 7 1`] = `
500
380
  }
501
381
  >
502
382
  <button
383
+ aria-disabled={false}
503
384
  className=""
504
- disabled={false}
505
385
  onBlur={[Function]}
506
386
  onClick={[Function]}
507
387
  onDragStart={[Function]}
@@ -13,7 +13,7 @@ import {View, Server} from "@khanacademy/wonder-blocks-core";
13
13
  import {
14
14
  Data,
15
15
  RequestHandler,
16
- InterceptCache,
16
+ initializeCache,
17
17
  InterceptData,
18
18
  TrackData,
19
19
  fulfillAllDataRequests,
@@ -21,11 +21,8 @@ import {
21
21
  import {Strut} from "@khanacademy/wonder-blocks-layout";
22
22
  import Color from "@khanacademy/wonder-blocks-color";
23
23
  import Spacing from "@khanacademy/wonder-blocks-spacing";
24
- import {withActionScheduler} from "@khanacademy/wonder-blocks-timing";
25
24
  import Button from "@khanacademy/wonder-blocks-button";
26
25
 
27
- import NoCache from "./../util/no-cache.js";
28
-
29
26
  describe("wonder-blocks-data", () => {
30
27
  it("example 1", () => {
31
28
  class MyValidHandler extends RequestHandler {
@@ -122,31 +119,21 @@ describe("wonder-blocks-data", () => {
122
119
  "If you're seeing this error, the examples are broken and data isn't in the cache that should be.",
123
120
  );
124
121
  }
125
-
126
- shouldRefreshCache(options, cachedEntry) {
127
- /**
128
- * For our purposes, the cache never needs a refresh.
129
- */
130
- return false;
131
- }
132
122
  }
133
123
 
134
124
  const handler = new MyHandler();
135
-
136
- const getEntryInterceptor = function (options) {
137
- if (options === "DATA") {
138
- return {
139
- data: "I'm DATA from the cache",
140
- };
141
- }
142
-
143
- return {
144
- error: "I'm an ERROR from the cache",
145
- };
146
- };
147
-
125
+ initializeCache({
126
+ CACHE_HIT_HANDLER: {
127
+ DATA: {
128
+ data: "I'm DATA from the hydration cache",
129
+ },
130
+ ERROR: {
131
+ error: "I'm an ERROR from hydration cache",
132
+ },
133
+ },
134
+ });
148
135
  const example = (
149
- <InterceptCache handler={handler} getEntry={getEntryInterceptor}>
136
+ <View>
150
137
  <View>
151
138
  <Body>This cache has data!</Body>
152
139
  <Data handler={handler} options={"DATA"}>
@@ -180,91 +167,13 @@ describe("wonder-blocks-data", () => {
180
167
  }}
181
168
  </Data>
182
169
  </View>
183
- </InterceptCache>
170
+ </View>
184
171
  );
185
172
  const tree = renderer.create(example).toJSON();
186
173
  expect(tree).toMatchSnapshot();
187
174
  });
188
175
 
189
176
  it("example 3", () => {
190
- class MyHandler extends RequestHandler {
191
- constructor() {
192
- super("INTERCEPT_CACHE_HANDLER");
193
- }
194
- /**
195
- * fulfillRequest should not get called as we already have data cached.
196
- */
197
-
198
- fulfillRequest(options) {
199
- throw new Error(
200
- "If you're seeing this error, the examples are broken.",
201
- );
202
- }
203
-
204
- shouldRefreshCache(options, cachedEntry) {
205
- /**
206
- * For our purposes, the cache never needs a refresh.
207
- */
208
- return false;
209
- }
210
- }
211
-
212
- const handler = new MyHandler();
213
-
214
- const getEntryIntercept = function (options) {
215
- if (options === "ERROR") {
216
- return {
217
- error: "I'm an ERROR from the cache interceptor",
218
- };
219
- }
220
-
221
- return {
222
- data: "I'm DATA from the cache interceptor",
223
- };
224
- };
225
-
226
- const example = (
227
- <InterceptCache handler={handler} getEntry={getEntryIntercept}>
228
- <View>
229
- <Body>This intercepted cache has data!</Body>
230
- <Data handler={handler} options={"DATA"}>
231
- {({loading, data}) => {
232
- if (loading) {
233
- return "If you see this, the example is broken!";
234
- }
235
-
236
- return <BodyMonospace>{data}</BodyMonospace>;
237
- }}
238
- </Data>
239
- </View>
240
- <Strut size={Spacing.small_12} />
241
- <View>
242
- <Body>This intercepted cache has error!</Body>
243
- <Data handler={handler} options={"ERROR"}>
244
- {({loading, error}) => {
245
- if (loading) {
246
- return "If you see this, the example is broken!";
247
- }
248
-
249
- return (
250
- <BodyMonospace
251
- style={{
252
- color: Color.red,
253
- }}
254
- >
255
- ERROR: {error}
256
- </BodyMonospace>
257
- );
258
- }}
259
- </Data>
260
- </View>
261
- </InterceptCache>
262
- );
263
- const tree = renderer.create(example).toJSON();
264
- expect(tree).toMatchSnapshot();
265
- });
266
-
267
- it("example 4", () => {
268
177
  class MyHandler extends RequestHandler {
269
178
  constructor() {
270
179
  super("INTERCEPT_DATA_HANDLER1");
@@ -308,96 +217,7 @@ describe("wonder-blocks-data", () => {
308
217
  expect(tree).toMatchSnapshot();
309
218
  });
310
219
 
311
- it("example 5", () => {
312
- class MyHandler extends RequestHandler {
313
- constructor() {
314
- super("INTERCEPT_DATA_HANDLER2");
315
- let _counter = 0;
316
-
317
- this.fulfillRequest = function (options) {
318
- return Promise.resolve(`DATA ${_counter++}`);
319
- };
320
- }
321
-
322
- shouldRefreshData(options) {
323
- return false;
324
- }
325
- }
326
-
327
- const handler = new MyHandler();
328
-
329
- const shouldRefreshCacheInterceptor = function (options) {
330
- if (options === "DATA") {
331
- return true;
332
- }
333
-
334
- return null;
335
- };
336
-
337
- class SchedulableExample extends React.Component {
338
- constructor(props) {
339
- super(props);
340
- this.state = {
341
- stamp: Date.now(),
342
- };
343
- }
344
-
345
- componentDidMount() {
346
- this.props.schedule.interval(
347
- () =>
348
- this.setState({
349
- stamp: Date.now(),
350
- }),
351
- 1000,
352
- );
353
- }
354
-
355
- render() {
356
- /**
357
- * The key on the View is what causes the re-render.
358
- *
359
- * The re-render causes the `Data` component to render again.
360
- * That causes it to check if it should refresh the cache.
361
- * and that in turn causes a new data request that updates the value
362
- * being rendered.
363
- *
364
- * Without the key to cause the re-render, no additional request would
365
- * be made.
366
- */
367
- return (
368
- <InterceptData
369
- handler={handler}
370
- shouldRefreshCache={shouldRefreshCacheInterceptor}
371
- >
372
- <View key={this.state.stamp}>
373
- <Body>
374
- This re-renders once a second. On the render,
375
- the cache is refreshed and so we see an update.
376
- </Body>
377
- <Data handler={handler} options={"DATA"}>
378
- {({loading, data}) => {
379
- if (loading) {
380
- return "If you see this, the example is broken!";
381
- }
382
-
383
- return (
384
- <BodyMonospace>{data}</BodyMonospace>
385
- );
386
- }}
387
- </Data>
388
- </View>
389
- </InterceptData>
390
- );
391
- }
392
- }
393
-
394
- const Example = withActionScheduler(SchedulableExample);
395
- const example = <Example />;
396
- const tree = renderer.create(example).toJSON();
397
- expect(tree).toMatchSnapshot();
398
- });
399
-
400
- it("example 6", () => {
220
+ it("example 4", () => {
401
221
  class ErrorBoundary extends React.Component {
402
222
  constructor(props) {
403
223
  super(props);
@@ -444,7 +264,7 @@ describe("wonder-blocks-data", () => {
444
264
  expect(tree).toMatchSnapshot();
445
265
  });
446
266
 
447
- it("example 7", () => {
267
+ it("example 5", () => {
448
268
  class MyPretendHandler extends RequestHandler {
449
269
  constructor() {
450
270
  super("MY_PRETEND_HANDLER");