@khanacademy/wonder-blocks-data 3.0.1 → 3.1.3

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/dist/index.js CHANGED
@@ -82,7 +82,7 @@ module.exports =
82
82
  /******/
83
83
  /******/
84
84
  /******/ // Load entry module and return exports
85
- /******/ return __webpack_require__(__webpack_require__.s = 13);
85
+ /******/ return __webpack_require__(__webpack_require__.s = 19);
86
86
  /******/ })
87
87
  /************************************************************************/
88
88
  /******/ ([
@@ -93,19 +93,57 @@ module.exports = require("react");
93
93
 
94
94
  /***/ }),
95
95
  /* 1 */
96
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
97
+
98
+ "use strict";
99
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return GqlErrors; });
100
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return GqlError; });
101
+ /* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(10);
102
+ /* harmony import */ var _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__);
103
+
104
+
105
+ /**
106
+ * Error kinds for GqlError.
107
+ */
108
+ const GqlErrors = Object.freeze({ ..._khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["Errors"],
109
+ Network: "Network",
110
+ Parse: "Parse",
111
+ BadResponse: "BadResponse",
112
+ ErrorResult: "ErrorResult"
113
+ });
114
+ /**
115
+ * An error from the GQL API.
116
+ */
117
+
118
+ class GqlError extends _khanacademy_wonder_stuff_core__WEBPACK_IMPORTED_MODULE_0__["KindError"] {
119
+ constructor(message, kind, {
120
+ metadata,
121
+ cause
122
+ } = {}) {
123
+ super(message, kind, {
124
+ metadata,
125
+ cause,
126
+ prefix: "Gql"
127
+ });
128
+ }
129
+
130
+ }
131
+
132
+ /***/ }),
133
+ /* 2 */
96
134
  /***/ (function(module, exports) {
97
135
 
98
136
  module.exports = require("@khanacademy/wonder-blocks-core");
99
137
 
100
138
  /***/ }),
101
- /* 2 */
139
+ /* 3 */
102
140
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
103
141
 
104
142
  "use strict";
105
143
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return ResponseCache; });
106
- /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
144
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
107
145
  /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
108
- /* harmony import */ var _memory_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7);
146
+ /* harmony import */ var _memory_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9);
109
147
 
110
148
 
111
149
 
@@ -227,7 +265,7 @@ class ResponseCache {
227
265
  }
228
266
 
229
267
  /***/ }),
230
- /* 3 */
268
+ /* 4 */
231
269
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
232
270
 
233
271
  "use strict";
@@ -235,8 +273,8 @@ class ResponseCache {
235
273
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return RequestTracker; });
236
274
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
237
275
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
238
- /* harmony import */ var _response_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
239
- /* harmony import */ var _request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6);
276
+ /* harmony import */ var _response_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
277
+ /* harmony import */ var _request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7);
240
278
 
241
279
 
242
280
 
@@ -379,7 +417,7 @@ class RequestTracker {
379
417
  }
380
418
 
381
419
  /***/ }),
382
- /* 4 */
420
+ /* 5 */
383
421
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
384
422
 
385
423
  "use strict";
@@ -396,20 +434,20 @@ const InterceptContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["create
396
434
  /* harmony default export */ __webpack_exports__["a"] = (InterceptContext);
397
435
 
398
436
  /***/ }),
399
- /* 5 */
437
+ /* 6 */
400
438
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
401
439
 
402
440
  "use strict";
403
441
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useData; });
404
- /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
442
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
405
443
  /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
406
444
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(0);
407
445
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
408
- /* harmony import */ var _util_request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6);
409
- /* harmony import */ var _components_intercept_context_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4);
410
- /* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(3);
411
- /* harmony import */ var _util_result_from_cache_entry_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(12);
412
- /* harmony import */ var _util_response_cache_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(2);
446
+ /* harmony import */ var _util_request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7);
447
+ /* harmony import */ var _components_intercept_context_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5);
448
+ /* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(4);
449
+ /* harmony import */ var _util_result_from_cache_entry_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(17);
450
+ /* harmony import */ var _util_response_cache_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(3);
413
451
 
414
452
 
415
453
 
@@ -534,12 +572,12 @@ const useData = (handler, options) => {
534
572
  };
535
573
 
536
574
  /***/ }),
537
- /* 6 */
575
+ /* 7 */
538
576
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
539
577
 
540
578
  "use strict";
541
579
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return RequestFulfillment; });
542
- /* harmony import */ var _response_cache_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
580
+ /* harmony import */ var _response_cache_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3);
543
581
 
544
582
 
545
583
  let _default;
@@ -624,7 +662,18 @@ class RequestFulfillment {
624
662
  }
625
663
 
626
664
  /***/ }),
627
- /* 7 */
665
+ /* 8 */
666
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
667
+
668
+ "use strict";
669
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return GqlRouterContext; });
670
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
671
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
672
+
673
+ const GqlRouterContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createContext"](null);
674
+
675
+ /***/ }),
676
+ /* 9 */
628
677
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
629
678
 
630
679
  "use strict";
@@ -773,7 +822,13 @@ class MemoryCache {
773
822
  }
774
823
 
775
824
  /***/ }),
776
- /* 8 */
825
+ /* 10 */
826
+ /***/ (function(module, exports) {
827
+
828
+ module.exports = require("@khanacademy/wonder-stuff-core");
829
+
830
+ /***/ }),
831
+ /* 11 */
777
832
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
778
833
 
779
834
  "use strict";
@@ -813,16 +868,16 @@ class RequestHandler {
813
868
  }
814
869
 
815
870
  /***/ }),
816
- /* 9 */
871
+ /* 12 */
817
872
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
818
873
 
819
874
  "use strict";
820
875
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return TrackData; });
821
876
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
822
877
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
823
- /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1);
878
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
824
879
  /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__);
825
- /* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
880
+ /* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4);
826
881
 
827
882
 
828
883
 
@@ -844,13 +899,13 @@ class TrackData extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
844
899
  }
845
900
 
846
901
  /***/ }),
847
- /* 10 */
902
+ /* 13 */
848
903
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
849
904
 
850
905
  "use strict";
851
906
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
852
907
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
853
- /* harmony import */ var _hooks_use_data_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
908
+ /* harmony import */ var _hooks_use_data_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6);
854
909
 
855
910
 
856
911
 
@@ -867,14 +922,14 @@ const Data = props => {
867
922
  /* harmony default export */ __webpack_exports__["a"] = (Data);
868
923
 
869
924
  /***/ }),
870
- /* 11 */
925
+ /* 14 */
871
926
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
872
927
 
873
928
  "use strict";
874
929
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return InterceptData; });
875
930
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
876
931
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
877
- /* harmony import */ var _intercept_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
932
+ /* harmony import */ var _intercept_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
878
933
 
879
934
 
880
935
 
@@ -913,7 +968,115 @@ class InterceptData extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
913
968
  }
914
969
 
915
970
  /***/ }),
916
- /* 12 */
971
+ /* 15 */
972
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
973
+
974
+ "use strict";
975
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return GqlRouter; });
976
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
977
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
978
+ /* harmony import */ var _util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8);
979
+
980
+
981
+
982
+ /**
983
+ * Configure GraphQL routing for GraphQL hooks and components.
984
+ *
985
+ * These can be nested. Components and hooks relying on the GraphQL routing
986
+ * will use the configuration from their closest ancestral GqlRouter.
987
+ */
988
+ const GqlRouter = ({
989
+ defaultContext: thisDefaultContext,
990
+ fetch: thisFetch,
991
+ children
992
+ }) => {
993
+ // We don't care if we're nested. We always force our callers to define
994
+ // everything. It makes for a clearer API and requires less error checking
995
+ // code (assuming our flow types are correct). We also don't default fetch
996
+ // to anything - our callers can tell us what function to use quite easily.
997
+ // If code that consumes this wants more nuanced nesting, it can implement
998
+ // it within its own GqlRouter than then defers to this one.
999
+ // We want to always use the same object if things haven't changed to avoid
1000
+ // over-rendering consumers of our context, let's memoize the configuration.
1001
+ // By doing this, if a component under children that uses this context
1002
+ // uses React.memo, we won't force it to re-render every time we render
1003
+ // because we'll only change the context value if something has actually
1004
+ // changed.
1005
+ const configuration = react__WEBPACK_IMPORTED_MODULE_0__["useMemo"](() => ({
1006
+ fetch: thisFetch,
1007
+ defaultContext: thisDefaultContext
1008
+ }), [thisDefaultContext, thisFetch]);
1009
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_1__[/* GqlRouterContext */ "a"].Provider, {
1010
+ value: configuration
1011
+ }, children);
1012
+ };
1013
+
1014
+ /***/ }),
1015
+ /* 16 */
1016
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1017
+
1018
+ "use strict";
1019
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useGql; });
1020
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
1021
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
1022
+ /* harmony import */ var _util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8);
1023
+ /* harmony import */ var _util_get_gql_data_from_response_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(18);
1024
+ /* harmony import */ var _util_gql_error_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(1);
1025
+
1026
+
1027
+
1028
+
1029
+
1030
+ /**
1031
+ * Hook to obtain a gqlFetch function for performing GraphQL requests.
1032
+ *
1033
+ * The fetch function will resolve null if the request was aborted, otherwise
1034
+ * it will resolve the data returned by the GraphQL server.
1035
+ */
1036
+ const useGql = () => {
1037
+ // This hook only works if the `GqlRouter` has been used to setup context.
1038
+ const gqlRouterContext = Object(react__WEBPACK_IMPORTED_MODULE_0__["useContext"])(_util_gql_router_context_js__WEBPACK_IMPORTED_MODULE_1__[/* GqlRouterContext */ "a"]);
1039
+
1040
+ if (gqlRouterContext == null) {
1041
+ throw new _util_gql_error_js__WEBPACK_IMPORTED_MODULE_3__[/* GqlError */ "a"]("No GqlRouter", _util_gql_error_js__WEBPACK_IMPORTED_MODULE_3__[/* GqlErrors */ "b"].Internal);
1042
+ }
1043
+
1044
+ const {
1045
+ fetch,
1046
+ defaultContext
1047
+ } = gqlRouterContext; // Let's memoize the gqlFetch function we create based off our context.
1048
+ // That way, even if the context happens to change, if its values don't
1049
+ // we give the same function instance back to our callers instead of
1050
+ // making a new one. That then means they can safely use the return value
1051
+ // in hooks deps without fear of it triggering extra renders.
1052
+
1053
+ const gqlFetch = Object(react__WEBPACK_IMPORTED_MODULE_0__["useMemo"])(() => (operation, options = Object.freeze({})) => {
1054
+ const {
1055
+ variables,
1056
+ context
1057
+ } = options; // Invoke the fetch and extract the data.
1058
+
1059
+ return fetch(operation, variables, { ...defaultContext,
1060
+ ...context
1061
+ }).then(_util_get_gql_data_from_response_js__WEBPACK_IMPORTED_MODULE_2__[/* getGqlDataFromResponse */ "a"], error => {
1062
+ // Return null if the request was aborted.
1063
+ // The only way to detect this reliably, it seems, is to
1064
+ // check the error name and see if it's "AbortError" (this
1065
+ // is also what Apollo does).
1066
+ // Even then, it's reliant on the fetch supporting aborts.
1067
+ if (error.name === "AbortError") {
1068
+ return null;
1069
+ } // Need to make sure we pass other errors along.
1070
+
1071
+
1072
+ throw error;
1073
+ });
1074
+ }, [fetch, defaultContext]);
1075
+ return gqlFetch;
1076
+ };
1077
+
1078
+ /***/ }),
1079
+ /* 17 */
917
1080
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
918
1081
 
919
1082
  "use strict";
@@ -956,7 +1119,75 @@ const resultFromCacheEntry = cacheEntry => {
956
1119
  };
957
1120
 
958
1121
  /***/ }),
959
- /* 13 */
1122
+ /* 18 */
1123
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1124
+
1125
+ "use strict";
1126
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return getGqlDataFromResponse; });
1127
+ /* harmony import */ var _gql_error_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
1128
+
1129
+ /**
1130
+ * Validate a GQL operation response and extract the data.
1131
+ */
1132
+
1133
+ const getGqlDataFromResponse = async response => {
1134
+ // Get the response as text, that way we can use the text in error
1135
+ // messaging, should our parsing fail.
1136
+ const bodyText = await response.text();
1137
+ let result;
1138
+
1139
+ try {
1140
+ result = JSON.parse(bodyText);
1141
+ } catch (e) {
1142
+ throw new _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlError */ "a"]("Failed to parse response", _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlErrors */ "b"].Parse, {
1143
+ metadata: {
1144
+ statusCode: response.status,
1145
+ bodyText
1146
+ },
1147
+ cause: e
1148
+ });
1149
+ } // Check for a bad status code.
1150
+
1151
+
1152
+ if (response.status >= 300) {
1153
+ throw new _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlError */ "a"]("Response unsuccessful", _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlErrors */ "b"].Network, {
1154
+ metadata: {
1155
+ statusCode: response.status,
1156
+ result
1157
+ }
1158
+ });
1159
+ } // Check that we have a valid result payload.
1160
+
1161
+
1162
+ if ( // Flow shouldn't be warning about this.
1163
+ // $FlowIgnore[method-unbinding]
1164
+ !Object.prototype.hasOwnProperty.call(result, "data") && // Flow shouldn't be warning about this.
1165
+ // $FlowIgnore[method-unbinding]
1166
+ !Object.prototype.hasOwnProperty.call(result, "errors")) {
1167
+ throw new _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlError */ "a"]("Server response missing", _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlErrors */ "b"].BadResponse, {
1168
+ metadata: {
1169
+ statusCode: response.status,
1170
+ result
1171
+ }
1172
+ });
1173
+ } // If the response payload has errors, throw an error.
1174
+
1175
+
1176
+ if (result.errors != null && Array.isArray(result.errors) && result.errors.length > 0) {
1177
+ throw new _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlError */ "a"]("GraphQL errors", _gql_error_js__WEBPACK_IMPORTED_MODULE_0__[/* GqlErrors */ "b"].ErrorResult, {
1178
+ metadata: {
1179
+ statusCode: response.status,
1180
+ result
1181
+ }
1182
+ });
1183
+ } // We got here, so return the data.
1184
+
1185
+
1186
+ return result.data;
1187
+ };
1188
+
1189
+ /***/ }),
1190
+ /* 19 */
960
1191
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
961
1192
 
962
1193
  "use strict";
@@ -966,25 +1197,36 @@ __webpack_require__.r(__webpack_exports__);
966
1197
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "hasUnfulfilledRequests", function() { return hasUnfulfilledRequests; });
967
1198
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "removeFromCache", function() { return removeFromCache; });
968
1199
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "removeAllFromCache", function() { return removeAllFromCache; });
969
- /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
1200
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
970
1201
  /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
971
- /* harmony import */ var _util_response_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
972
- /* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
973
- /* harmony import */ var _util_request_handler_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8);
1202
+ /* harmony import */ var _util_response_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
1203
+ /* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4);
1204
+ /* harmony import */ var _util_request_handler_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11);
974
1205
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "RequestHandler", function() { return _util_request_handler_js__WEBPACK_IMPORTED_MODULE_3__["a"]; });
975
1206
 
976
- /* harmony import */ var _components_track_data_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(9);
1207
+ /* harmony import */ var _components_track_data_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(12);
977
1208
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "TrackData", function() { return _components_track_data_js__WEBPACK_IMPORTED_MODULE_4__["a"]; });
978
1209
 
979
- /* harmony import */ var _components_data_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(10);
1210
+ /* harmony import */ var _components_data_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(13);
980
1211
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Data", function() { return _components_data_js__WEBPACK_IMPORTED_MODULE_5__["a"]; });
981
1212
 
982
- /* harmony import */ var _components_intercept_data_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(11);
1213
+ /* harmony import */ var _components_intercept_data_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(14);
983
1214
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "InterceptData", function() { return _components_intercept_data_js__WEBPACK_IMPORTED_MODULE_6__["a"]; });
984
1215
 
985
- /* harmony import */ var _hooks_use_data_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(5);
1216
+ /* harmony import */ var _hooks_use_data_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(6);
986
1217
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useData", function() { return _hooks_use_data_js__WEBPACK_IMPORTED_MODULE_7__["a"]; });
987
1218
 
1219
+ /* harmony import */ var _components_gql_router_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(15);
1220
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlRouter", function() { return _components_gql_router_js__WEBPACK_IMPORTED_MODULE_8__["a"]; });
1221
+
1222
+ /* harmony import */ var _hooks_use_gql_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(16);
1223
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useGql", function() { return _hooks_use_gql_js__WEBPACK_IMPORTED_MODULE_9__["a"]; });
1224
+
1225
+ /* harmony import */ var _util_gql_error_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(1);
1226
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlErrors", function() { return _util_gql_error_js__WEBPACK_IMPORTED_MODULE_10__["b"]; });
1227
+
1228
+ /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "GqlError", function() { return _util_gql_error_js__WEBPACK_IMPORTED_MODULE_10__["a"]; });
1229
+
988
1230
 
989
1231
 
990
1232
 
@@ -1005,15 +1247,13 @@ const hasUnfulfilledRequests = () => {
1005
1247
  };
1006
1248
  const removeFromCache = (handler, options) => _util_response_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* ResponseCache */ "a"].Default.remove(handler, options);
1007
1249
  const removeAllFromCache = (handler, predicate) => _util_response_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* ResponseCache */ "a"].Default.removeAll(handler, predicate);
1008
- /**
1009
- * TODO(somewhatabstract): Export each cache type we implement.
1010
- *
1011
- * Is there a base type we export, like we do for RequestHandler?
1012
- */
1013
1250
 
1014
1251
 
1015
1252
 
1016
1253
 
1254
+ // GraphQL
1255
+
1256
+
1017
1257
 
1018
1258
 
1019
1259
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-data",
3
- "version": "3.0.1",
3
+ "version": "3.1.3",
4
4
  "design": "v1",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -14,13 +14,14 @@
14
14
  },
15
15
  "dependencies": {
16
16
  "@babel/runtime": "^7.16.3",
17
- "@khanacademy/wonder-blocks-core": "^4.0.0"
17
+ "@khanacademy/wonder-blocks-core": "^4.2.1"
18
18
  },
19
19
  "peerDependencies": {
20
+ "@khanacademy/wonder-stuff-core": "^0.1.2",
20
21
  "react": "16.14.0"
21
22
  },
22
23
  "devDependencies": {
23
- "wb-dev-build-settings": "^0.2.0"
24
+ "wb-dev-build-settings": "^0.3.0"
24
25
  },
25
26
  "author": "",
26
27
  "license": "MIT"
@@ -0,0 +1,64 @@
1
+ // @flow
2
+ import * as React from "react";
3
+ import {render} from "@testing-library/react";
4
+
5
+ import {GqlRouterContext} from "../../util/gql-router-context.js";
6
+ import {GqlRouter} from "../gql-router.js";
7
+
8
+ describe("GqlRouter", () => {
9
+ it("should provide the GqlRouterContext as configured", async () => {
10
+ // Arrange
11
+ const defaultContext = {
12
+ foo: "bar",
13
+ };
14
+ const fetch = jest.fn();
15
+ const CaptureContext = ({captureFn}) => {
16
+ captureFn(React.useContext(GqlRouterContext));
17
+ return null;
18
+ };
19
+
20
+ // Act
21
+ const result = await new Promise((resolve, reject) => {
22
+ render(
23
+ <GqlRouter defaultContext={defaultContext} fetch={fetch}>
24
+ <CaptureContext captureFn={resolve} />
25
+ </GqlRouter>,
26
+ );
27
+ });
28
+
29
+ // Assert
30
+ expect(result).toStrictEqual({
31
+ defaultContext,
32
+ fetch,
33
+ });
34
+ });
35
+
36
+ it("should not render React.memo-ized children if props remain the same", () => {
37
+ // Arrange
38
+ const defaultContext = {
39
+ foo: "bar",
40
+ };
41
+ const fetch = jest.fn();
42
+ let renderCount = 0;
43
+ const Child = React.memo(() => {
44
+ const context = React.useContext(GqlRouterContext);
45
+ renderCount++;
46
+ return <div>{JSON.stringify(context)}</div>;
47
+ });
48
+
49
+ // Act
50
+ const {rerender} = render(
51
+ <GqlRouter defaultContext={defaultContext} fetch={fetch}>
52
+ <Child />
53
+ </GqlRouter>,
54
+ );
55
+ rerender(
56
+ <GqlRouter defaultContext={defaultContext} fetch={fetch}>
57
+ <Child />
58
+ </GqlRouter>,
59
+ );
60
+
61
+ // Assert
62
+ expect(renderCount).toBe(1);
63
+ });
64
+ });
@@ -0,0 +1,66 @@
1
+ // @flow
2
+ import * as React from "react";
3
+
4
+ import {GqlRouterContext} from "../util/gql-router-context.js";
5
+
6
+ import type {
7
+ GqlContext,
8
+ GqlFetchFn,
9
+ GqlRouterConfiguration,
10
+ } from "../util/gql-types.js";
11
+
12
+ type Props<TContext: GqlContext> = {|
13
+ /**
14
+ * The default context to be used by operations when no context is provided.
15
+ */
16
+ defaultContext: TContext,
17
+
18
+ /**
19
+ * The function to use when fetching requests.
20
+ */
21
+ fetch: GqlFetchFn<any, any, TContext>,
22
+
23
+ /**
24
+ * The children to be rendered inside the router.
25
+ */
26
+ children: React.Node,
27
+ |};
28
+
29
+ /**
30
+ * Configure GraphQL routing for GraphQL hooks and components.
31
+ *
32
+ * These can be nested. Components and hooks relying on the GraphQL routing
33
+ * will use the configuration from their closest ancestral GqlRouter.
34
+ */
35
+ export const GqlRouter = <TContext: GqlContext>({
36
+ defaultContext: thisDefaultContext,
37
+ fetch: thisFetch,
38
+ children,
39
+ }: Props<TContext>): React.Node => {
40
+ // We don't care if we're nested. We always force our callers to define
41
+ // everything. It makes for a clearer API and requires less error checking
42
+ // code (assuming our flow types are correct). We also don't default fetch
43
+ // to anything - our callers can tell us what function to use quite easily.
44
+ // If code that consumes this wants more nuanced nesting, it can implement
45
+ // it within its own GqlRouter than then defers to this one.
46
+
47
+ // We want to always use the same object if things haven't changed to avoid
48
+ // over-rendering consumers of our context, let's memoize the configuration.
49
+ // By doing this, if a component under children that uses this context
50
+ // uses React.memo, we won't force it to re-render every time we render
51
+ // because we'll only change the context value if something has actually
52
+ // changed.
53
+ const configuration: GqlRouterConfiguration<TContext> = React.useMemo(
54
+ () => ({
55
+ fetch: thisFetch,
56
+ defaultContext: thisDefaultContext,
57
+ }),
58
+ [thisDefaultContext, thisFetch],
59
+ );
60
+
61
+ return (
62
+ <GqlRouterContext.Provider value={configuration}>
63
+ {children}
64
+ </GqlRouterContext.Provider>
65
+ );
66
+ };