@khanacademy/wonder-blocks-testing 5.0.1 → 6.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 CHANGED
@@ -1,5 +1,25 @@
1
1
  # @khanacademy/wonder-blocks-testing
2
2
 
3
+ ## 6.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - b79979b8: Add `logHandler` to GetPropsOptions to simplify generic event logging for common use cases
8
+
9
+ ## 6.0.0
10
+
11
+ ### Major Changes
12
+
13
+ - af459222: Improve typing for fixtures call
14
+
15
+ ## 5.0.2
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies [5f4a4297]
20
+ - Updated dependencies [2b96fd59]
21
+ - @khanacademy/wonder-blocks-data@8.0.3
22
+
3
23
  ## 5.0.1
4
24
 
5
25
  ### Patch Changes
package/dist/es/index.js CHANGED
@@ -80,9 +80,10 @@ const getAdapter = (MountingComponent = null) => new Adapter("storybook", ({
80
80
  getDefaultTitle: _
81
81
  }, adapterOptions, declaredFixtures) => {
82
82
  const templateMap = new WeakMap();
83
-
84
- const log = (message, ...args) => action(message).apply(void 0, args);
85
-
83
+ const getPropsOptions = {
84
+ log: (message, ...args) => action(message).apply(void 0, args),
85
+ logHandler: action
86
+ };
86
87
  const exports = declaredFixtures.reduce((acc, {
87
88
  description,
88
89
  getProps,
@@ -96,15 +97,13 @@ const getAdapter = (MountingComponent = null) => new Adapter("storybook", ({
96
97
  Template = MountingComponent ? args => React.createElement(MountingComponent, {
97
98
  component: Component,
98
99
  props: args,
99
- log: log
100
+ log: getPropsOptions.log
100
101
  }) : args => React.createElement(Component, args);
101
102
  templateMap.set(Component, Template);
102
103
  }
103
104
 
104
105
  acc[exportName] = Template.bind({});
105
- acc[exportName].args = getProps({
106
- log
107
- });
106
+ acc[exportName].args = getProps(getPropsOptions);
108
107
  acc[exportName].storyName = storyName;
109
108
  return acc;
110
109
  }, {
package/dist/index.js CHANGED
@@ -99,10 +99,10 @@ module.exports = require("react");
99
99
  __webpack_require__.r(__webpack_exports__);
100
100
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DefaultAdapters", function() { return DefaultAdapters; });
101
101
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DefaultConfigs", function() { return DefaultConfigs; });
102
- /* harmony import */ var _css_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(8);
103
- /* harmony import */ var _data_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9);
104
- /* harmony import */ var _portal_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(10);
105
- /* harmony import */ var _router_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11);
102
+ /* harmony import */ var _css_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9);
103
+ /* harmony import */ var _data_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(10);
104
+ /* harmony import */ var _portal_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(11);
105
+ /* harmony import */ var _router_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(12);
106
106
 
107
107
 
108
108
 
@@ -451,6 +451,12 @@ const mockRequester = (operationMatcher, operationToString) => {
451
451
 
452
452
  /***/ }),
453
453
  /* 8 */
454
+ /***/ (function(module, exports) {
455
+
456
+ module.exports = require("@storybook/addon-actions");
457
+
458
+ /***/ }),
459
+ /* 9 */
454
460
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
455
461
 
456
462
  "use strict";
@@ -514,7 +520,7 @@ const adapter = (children, config) => {
514
520
  };
515
521
 
516
522
  /***/ }),
517
- /* 9 */
523
+ /* 10 */
518
524
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
519
525
 
520
526
  "use strict";
@@ -560,7 +566,7 @@ const adapter = (children, config) => {
560
566
  };
561
567
 
562
568
  /***/ }),
563
- /* 10 */
569
+ /* 11 */
564
570
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
565
571
 
566
572
  "use strict";
@@ -585,7 +591,7 @@ const adapter = (children, config) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODUL
585
591
  }), children);
586
592
 
587
593
  /***/ }),
588
- /* 11 */
594
+ /* 12 */
589
595
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
590
596
 
591
597
  "use strict";
@@ -703,25 +709,25 @@ const adapter = (children, config) => {
703
709
  };
704
710
 
705
711
  /***/ }),
706
- /* 12 */
712
+ /* 13 */
707
713
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
708
714
 
709
715
  "use strict";
710
716
  __webpack_require__.r(__webpack_exports__);
711
- /* harmony import */ var _storybook_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13);
717
+ /* harmony import */ var _storybook_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(14);
712
718
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "storybook", function() { return _storybook_js__WEBPACK_IMPORTED_MODULE_0__["a"]; });
713
719
 
714
720
 
715
721
 
716
722
  /***/ }),
717
- /* 13 */
723
+ /* 14 */
718
724
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
719
725
 
720
726
  "use strict";
721
727
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return getAdapter; });
722
728
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
723
729
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
724
- /* harmony import */ var _storybook_addon_actions__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(20);
730
+ /* harmony import */ var _storybook_addon_actions__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8);
725
731
  /* harmony import */ var _storybook_addon_actions__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_storybook_addon_actions__WEBPACK_IMPORTED_MODULE_1__);
726
732
  /* harmony import */ var _adapter_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(21);
727
733
 
@@ -739,9 +745,10 @@ const getAdapter = (MountingComponent = null) => new _adapter_js__WEBPACK_IMPORT
739
745
  getDefaultTitle: _
740
746
  }, adapterOptions, declaredFixtures) => {
741
747
  const templateMap = new WeakMap();
742
-
743
- const log = (message, ...args) => Object(_storybook_addon_actions__WEBPACK_IMPORTED_MODULE_1__["action"])(message).apply(void 0, args);
744
-
748
+ const getPropsOptions = {
749
+ log: (message, ...args) => Object(_storybook_addon_actions__WEBPACK_IMPORTED_MODULE_1__["action"])(message).apply(void 0, args),
750
+ logHandler: _storybook_addon_actions__WEBPACK_IMPORTED_MODULE_1__["action"]
751
+ };
745
752
  const exports = declaredFixtures.reduce((acc, {
746
753
  description,
747
754
  getProps,
@@ -770,7 +777,7 @@ const getAdapter = (MountingComponent = null) => new _adapter_js__WEBPACK_IMPORT
770
777
  Template = MountingComponent ? args => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](MountingComponent, {
771
778
  component: Component,
772
779
  props: args,
773
- log: log
780
+ log: getPropsOptions.log
774
781
  }) : args => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](Component, args);
775
782
  templateMap.set(Component, Template);
776
783
  } // Each story that shares that component then reuses that
@@ -778,9 +785,7 @@ const getAdapter = (MountingComponent = null) => new _adapter_js__WEBPACK_IMPORT
778
785
 
779
786
 
780
787
  acc[exportName] = Template.bind({});
781
- acc[exportName].args = getProps({
782
- log
783
- }); // Adding a story name here means that we don't have to
788
+ acc[exportName].args = getProps(getPropsOptions); // Adding a story name here means that we don't have to
784
789
  // care about naming the exports correctly, if we don't
785
790
  // want (useful if we need to autogenerate or manually
786
791
  // expose ESM exports).
@@ -799,7 +804,7 @@ const getAdapter = (MountingComponent = null) => new _adapter_js__WEBPACK_IMPORT
799
804
  });
800
805
 
801
806
  /***/ }),
802
- /* 14 */
807
+ /* 15 */
803
808
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
804
809
 
805
810
  "use strict";
@@ -905,7 +910,7 @@ const fixtures = (componentOrOptions, fn) => {
905
910
  };
906
911
 
907
912
  /***/ }),
908
- /* 15 */
913
+ /* 16 */
909
914
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
910
915
 
911
916
  "use strict";
@@ -922,7 +927,7 @@ const mockFetch = () => Object(_mock_requester_js__WEBPACK_IMPORTED_MODULE_1__[/
922
927
  Options: ${init == null ? "None" : JSON.stringify(init, null, 2)}`);
923
928
 
924
929
  /***/ }),
925
- /* 16 */
930
+ /* 17 */
926
931
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
927
932
 
928
933
  "use strict";
@@ -940,7 +945,7 @@ const mockGqlFetch = () => Object(_mock_requester_js__WEBPACK_IMPORTED_MODULE_1_
940
945
  Context: ${JSON.stringify(context, null, 2)}`);
941
946
 
942
947
  /***/ }),
943
- /* 17 */
948
+ /* 18 */
944
949
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
945
950
 
946
951
  "use strict";
@@ -952,7 +957,7 @@ const mockGqlFetch = () => Object(_mock_requester_js__WEBPACK_IMPORTED_MODULE_1_
952
957
  */
953
958
 
954
959
  /***/ }),
955
- /* 18 */
960
+ /* 19 */
956
961
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
957
962
 
958
963
  "use strict";
@@ -977,7 +982,7 @@ const mockGqlFetch = () => Object(_mock_requester_js__WEBPACK_IMPORTED_MODULE_1_
977
982
  const hookHarness = Object(_make_hook_harness_js__WEBPACK_IMPORTED_MODULE_1__[/* makeHookHarness */ "a"])(_adapters_adapters_js__WEBPACK_IMPORTED_MODULE_2__["DefaultAdapters"], _adapters_adapters_js__WEBPACK_IMPORTED_MODULE_2__["DefaultConfigs"]);
978
983
 
979
984
  /***/ }),
980
- /* 19 */
985
+ /* 20 */
981
986
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
982
987
 
983
988
  "use strict";
@@ -1001,12 +1006,6 @@ const hookHarness = Object(_make_hook_harness_js__WEBPACK_IMPORTED_MODULE_1__[/*
1001
1006
  */
1002
1007
  const testHarness = Object(_make_test_harness_js__WEBPACK_IMPORTED_MODULE_1__[/* makeTestHarness */ "a"])(_adapters_adapters_js__WEBPACK_IMPORTED_MODULE_2__["DefaultAdapters"], _adapters_adapters_js__WEBPACK_IMPORTED_MODULE_2__["DefaultConfigs"]);
1003
1008
 
1004
- /***/ }),
1005
- /* 20 */
1006
- /***/ (function(module, exports) {
1007
-
1008
- module.exports = require("@storybook/addon-actions");
1009
-
1010
1009
  /***/ }),
1011
1010
  /* 21 */
1012
1011
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
@@ -1389,24 +1388,24 @@ const renderAdapters = (adapters, configs, children) => {
1389
1388
 
1390
1389
  "use strict";
1391
1390
  __webpack_require__.r(__webpack_exports__);
1392
- /* harmony import */ var _fixtures_adapters_adapters_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12);
1391
+ /* harmony import */ var _fixtures_adapters_adapters_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13);
1393
1392
  /* harmony reexport (module object) */ __webpack_require__.d(__webpack_exports__, "fixtureAdapters", function() { return _fixtures_adapters_adapters_js__WEBPACK_IMPORTED_MODULE_0__; });
1394
- /* harmony import */ var _fixtures_fixtures_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(14);
1393
+ /* harmony import */ var _fixtures_fixtures_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(15);
1395
1394
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "fixtures", function() { return _fixtures_fixtures_js__WEBPACK_IMPORTED_MODULE_1__["a"]; });
1396
1395
 
1397
1396
  /* harmony import */ var _fixtures_setup_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4);
1398
1397
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "setupFixtures", function() { return _fixtures_setup_js__WEBPACK_IMPORTED_MODULE_2__["b"]; });
1399
1398
 
1400
- /* harmony import */ var _fetch_mock_fetch_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(15);
1399
+ /* harmony import */ var _fetch_mock_fetch_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(16);
1401
1400
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mockFetch", function() { return _fetch_mock_fetch_js__WEBPACK_IMPORTED_MODULE_3__["a"]; });
1402
1401
 
1403
- /* harmony import */ var _gql_mock_gql_fetch_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(16);
1402
+ /* harmony import */ var _gql_mock_gql_fetch_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(17);
1404
1403
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mockGqlFetch", function() { return _gql_mock_gql_fetch_js__WEBPACK_IMPORTED_MODULE_4__["a"]; });
1405
1404
 
1406
1405
  /* harmony import */ var _make_mock_response_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(5);
1407
1406
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "RespondWith", function() { return _make_mock_response_js__WEBPACK_IMPORTED_MODULE_5__["a"]; });
1408
1407
 
1409
- /* harmony import */ var _harness_types_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(17);
1408
+ /* harmony import */ var _harness_types_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(18);
1410
1409
  /* harmony import */ var _harness_adapters_adapters_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(1);
1411
1410
  /* harmony reexport (module object) */ __webpack_require__.d(__webpack_exports__, "harnessAdapters", function() { return _harness_adapters_adapters_js__WEBPACK_IMPORTED_MODULE_7__; });
1412
1411
  /* harmony import */ var _harness_make_hook_harness_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(6);
@@ -1415,10 +1414,10 @@ __webpack_require__.r(__webpack_exports__);
1415
1414
  /* harmony import */ var _harness_make_test_harness_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(3);
1416
1415
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "makeTestHarness", function() { return _harness_make_test_harness_js__WEBPACK_IMPORTED_MODULE_9__["a"]; });
1417
1416
 
1418
- /* harmony import */ var _harness_hook_harness_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(18);
1417
+ /* harmony import */ var _harness_hook_harness_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(19);
1419
1418
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "hookHarness", function() { return _harness_hook_harness_js__WEBPACK_IMPORTED_MODULE_10__["a"]; });
1420
1419
 
1421
- /* harmony import */ var _harness_test_harness_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(19);
1420
+ /* harmony import */ var _harness_test_harness_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(20);
1422
1421
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "testHarness", function() { return _harness_test_harness_js__WEBPACK_IMPORTED_MODULE_11__["a"]; });
1423
1422
 
1424
1423
  // Fixtures framework
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-testing",
3
- "version": "5.0.1",
3
+ "version": "6.1.0",
4
4
  "design": "v1",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "dependencies": {
16
16
  "@babel/runtime": "^7.16.3",
17
- "@khanacademy/wonder-blocks-data": "^8.0.2"
17
+ "@khanacademy/wonder-blocks-data": "^8.0.3"
18
18
  },
19
19
  "peerDependencies": {
20
20
  "@khanacademy/wonder-stuff-core": "^0.1.2",
@@ -17,9 +17,36 @@ type GetPropsOptions = {|
17
17
  * A function to call that will log output.
18
18
  */
19
19
  log: (message: string, ...args: Array<any>) => void,
20
+
21
+ /**
22
+ * A function to make a handler that will log all arguments with the given
23
+ * name or message. Useful for logging events as it avoids the boilerplate
24
+ * of the `log` function.
25
+ */
26
+ logHandler: (name: string) => (...args: Array<any>) => void,
20
27
  |};
21
28
  ```
22
29
 
23
30
  A fixture can provide a callback that the framework invokes to obtain the props for the fixture component. This callback takes a single argument of type `GetPropsOptions`.
24
31
 
25
- This has a single `log` property; a callback for logging output in the context of the fixture. This can be useful for logging event handler invocations, for example.
32
+ This has two calls available.
33
+
34
+ The `log` callback is for logging output in the context of the fixture. This can be useful for logging information during your fixture. However, in many situations, it is easier to use the `logHandler` callback. The `logHandler` callback takes a single argument of type `string` and returns a function that logs all arguments with the given name or message, allowing easy creation of event handlers that will log that event and its arguments.
35
+
36
+ For example:
37
+
38
+ ```ts
39
+ fixture("My fixture that does logging", ({logHandler}) => ({
40
+ onClick: logHandler("onClick"),
41
+ }));
42
+ ```
43
+
44
+ is equivalent to:
45
+
46
+ ```ts
47
+ fixture("My fixture that does logging", ({log}) => ({
48
+ onClick: (...args) => log("onClick", ...args),
49
+ }));
50
+ ```
51
+
52
+
@@ -48,7 +48,7 @@ describe("#fixtures", () => {
48
48
  });
49
49
 
50
50
  // Act
51
- fixtures(
51
+ fixtures<any, _>(
52
52
  {
53
53
  title: "TITLE",
54
54
  description: "DESCRIPTION",
@@ -81,7 +81,7 @@ describe("#fixtures", () => {
81
81
  component.displayName = "DISPLAYNAME";
82
82
 
83
83
  // Act
84
- fixtures(
84
+ fixtures<any, _>(
85
85
  {
86
86
  component,
87
87
  },
@@ -157,7 +157,7 @@ describe("#fixtures", () => {
157
157
  const fn = jest.fn();
158
158
 
159
159
  // Act
160
- fixtures(
160
+ fixtures<any, _>(
161
161
  {
162
162
  title: "GROUP_TITLE",
163
163
  description: "GROUP_DESCRIPTION",
@@ -196,7 +196,7 @@ describe("#fixtures", () => {
196
196
  };
197
197
 
198
198
  // Act
199
- fixtures(
199
+ fixtures<any, _>(
200
200
  {
201
201
  component: () => "COMPONENT",
202
202
  additionalAdapterOptions: {
@@ -241,7 +241,7 @@ describe("#fixtures", () => {
241
241
  };
242
242
 
243
243
  // Act
244
- fixtures(
244
+ fixtures<any, _>(
245
245
  {
246
246
  component: () => "COMPONENT",
247
247
  additionalAdapterOptions: {
@@ -273,7 +273,7 @@ describe("#fixtures", () => {
273
273
  });
274
274
 
275
275
  // Act
276
- const result = fixtures(
276
+ const result = fixtures<any, _>(
277
277
  {
278
278
  component: () => "COMPONENT",
279
279
  },
@@ -301,7 +301,7 @@ describe("#fixtures", () => {
301
301
  const component = () => "COMPONENT";
302
302
 
303
303
  // Act
304
- fixtures(
304
+ fixtures<any, _>(
305
305
  {
306
306
  title: "GROUP_TITLE",
307
307
  description: "GROUP_DESCRIPTION",
@@ -367,7 +367,7 @@ describe("#fixtures", () => {
367
367
  const wrapper = () => "WRAPPER";
368
368
 
369
369
  // Act
370
- fixtures(
370
+ fixtures<any, _>(
371
371
  {
372
372
  title: "GROUP_TITLE",
373
373
  description: "GROUP_DESCRIPTION",
@@ -407,7 +407,7 @@ describe("#fixtures", () => {
407
407
  const defaultWrapper = () => "DEFAULT_WRAPPER";
408
408
 
409
409
  // Act
410
- fixtures(
410
+ fixtures<any, _>(
411
411
  {
412
412
  title: "GROUP_TITLE",
413
413
  description: "GROUP_DESCRIPTION",
@@ -443,7 +443,7 @@ describe("#fixtures", () => {
443
443
  });
444
444
 
445
445
  // Act
446
- fixtures(
446
+ fixtures<any, _>(
447
447
  {
448
448
  component: () => "COMPONENT",
449
449
  },
@@ -475,7 +475,7 @@ describe("#fixtures", () => {
475
475
  const props = jest.fn().mockReturnValue({these: "areProps"});
476
476
 
477
477
  // Act
478
- fixtures(
478
+ fixtures<any, _>(
479
479
  {
480
480
  component: () => "COMPONENT",
481
481
  },
@@ -507,7 +507,7 @@ describe("#fixtures", () => {
507
507
  const props = jest.fn().mockReturnValue({these: "areProps"});
508
508
 
509
509
  // Act
510
- fixtures(
510
+ fixtures<any, _>(
511
511
  {
512
512
  component: () => "COMPONENT",
513
513
  },
@@ -121,6 +121,7 @@ describe("Storybook Adapter", () => {
121
121
  // Assert
122
122
  expect(getPropsStub).toHaveBeenCalledWith({
123
123
  log: expect.any(Function),
124
+ logHandler: expect.any(Function),
124
125
  });
125
126
  });
126
127
 
@@ -161,6 +162,45 @@ describe("Storybook Adapter", () => {
161
162
  expect(actionReturnFn).toHaveBeenCalledWith("ARG1", "ARG2");
162
163
  });
163
164
 
165
+ it("should inject logHandler function that logs to storybook actions", () => {
166
+ // Arrange
167
+ const adapterSpy = jest
168
+ .spyOn(AdapterModule, "Adapter")
169
+ .mockImplementation(() => {});
170
+ getAdapter();
171
+ const actionReturnFn = jest.fn();
172
+ const actionSpy = jest
173
+ .spyOn(AddonActionsModule, "action")
174
+ .mockReturnValue(actionReturnFn);
175
+ const closeGroupFn = adapterSpy.mock.calls[0][1];
176
+ const getPropsStub = jest.fn();
177
+ closeGroupFn(
178
+ {
179
+ title: "TITLE",
180
+ description: "DESCRIPTION",
181
+ },
182
+ null,
183
+ [
184
+ {
185
+ description:
186
+ "🎉 This is a story with legal and illegal characters",
187
+ component: () => "I AM A COMPONENT",
188
+ getProps: getPropsStub,
189
+ },
190
+ ],
191
+ );
192
+ const {logHandler: logHandlerFn} =
193
+ getPropsStub.mock.calls[0][0];
194
+
195
+ // Act
196
+ const logFn = logHandlerFn("MESSAGE");
197
+ logFn("ARG1", "ARG2");
198
+
199
+ // Assert
200
+ expect(actionSpy).toHaveBeenCalledWith("MESSAGE");
201
+ expect(actionReturnFn).toHaveBeenCalledWith("ARG1", "ARG2");
202
+ });
203
+
164
204
  it("should have args attached", () => {
165
205
  // Arrange
166
206
  const adapterSpy = jest
@@ -59,7 +59,10 @@ export const getAdapter: FixturesAdapterFactory<
59
59
  ): ?$ReadOnly<Exports<TProps>> => {
60
60
  const templateMap = new WeakMap();
61
61
 
62
- const log = (message, ...args) => action(message)(...args);
62
+ const getPropsOptions = {
63
+ log: (message, ...args) => action(message)(...args),
64
+ logHandler: action,
65
+ };
63
66
 
64
67
  const exports = declaredFixtures.reduce(
65
68
  (acc, {description, getProps, component: Component}, i) => {
@@ -78,7 +81,7 @@ export const getAdapter: FixturesAdapterFactory<
78
81
  // component. We don't use decorators for the wrapper
79
82
  // because we may not be in a storybook context and it
80
83
  // keeps the framework API simpler this way.
81
- let Template = templateMap.get(Component);
84
+ let Template = templateMap.get((Component: any));
82
85
  if (Template == null) {
83
86
  // The MountingComponent is a bit different than just a
84
87
  // Storybook decorator. It's a React component that
@@ -91,17 +94,17 @@ export const getAdapter: FixturesAdapterFactory<
91
94
  <MountingComponent
92
95
  component={Component}
93
96
  props={args}
94
- log={log}
97
+ log={getPropsOptions.log}
95
98
  />
96
99
  )
97
100
  : (args) => <Component {...args} />;
98
- templateMap.set(Component, Template);
101
+ templateMap.set((Component: any), Template);
99
102
  }
100
103
 
101
104
  // Each story that shares that component then reuses that
102
105
  // template.
103
106
  acc[exportName] = Template.bind({});
104
- acc[exportName].args = getProps({log});
107
+ acc[exportName].args = getProps(getPropsOptions);
105
108
  // Adding a story name here means that we don't have to
106
109
  // care about naming the exports correctly, if we don't
107
110
  // want (useful if we need to autogenerate or manually
@@ -12,7 +12,12 @@ setupFixtures({
12
12
  adapter: adapters.storybook(),
13
13
  });
14
14
 
15
- const MyComponent = (props) =>
15
+ type Props = {|
16
+ propA: string,
17
+ propB?: string,
18
+ |};
19
+
20
+ const MyComponent = (props: Props) =>
16
21
  `I am a component. Here are my props: ${JSON.stringify(props, null, 2)}`;
17
22
 
18
23
  const Wrapper = (props) => (
@@ -24,15 +29,15 @@ const Wrapper = (props) => (
24
29
  );
25
30
 
26
31
  const stories: Array<mixed> = Object.values(
27
- fixtures(
32
+ fixtures<typeof MyComponent, _>(
28
33
  {
29
34
  component: MyComponent,
30
35
  title: "Testing / Fixtures / Basic",
31
36
  },
32
37
  (fixture) => {
33
38
  fixture("This is a fixture with some regular props", {
34
- see: "this is a prop",
35
- and: "this is another",
39
+ propA: "this is a prop",
40
+ propB: "this is another",
36
41
  });
37
42
 
38
43
  fixture(
@@ -45,7 +50,7 @@ const stories: Array<mixed> = Object.values(
45
50
  },
46
51
  );
47
52
  return {
48
- this: "prop was made from a function",
53
+ propA: "prop was made from a function",
49
54
  };
50
55
  },
51
56
  );
@@ -53,8 +58,8 @@ const stories: Array<mixed> = Object.values(
53
58
  fixture(
54
59
  "This fixture uses a custom wrapper",
55
60
  {
56
- just: "some props again",
57
- like: "this one",
61
+ propA: "some props again",
62
+ propB: "this one",
58
63
  },
59
64
  Wrapper,
60
65
  );
@@ -31,7 +31,7 @@ const DefaultWrapper = (props) => (
31
31
  );
32
32
 
33
33
  const stories: Array<mixed> = Object.values(
34
- fixtures(
34
+ fixtures<typeof MyComponent, _>(
35
35
  {
36
36
  component: MyComponent,
37
37
  title: "Testing / Fixtures / DefaultWrapper",
@@ -9,11 +9,14 @@ type FixtureProps<TProps: {...}> =
9
9
  | $ReadOnly<TProps>
10
10
  | ((options: $ReadOnly<GetPropsOptions>) => $ReadOnly<TProps>);
11
11
 
12
- const normalizeOptions = <TProps: {...}>(
12
+ const normalizeOptions = <
13
+ TComponent: React.ComponentType<any>,
14
+ TProps: React.ElementConfig<TComponent>,
15
+ >(
13
16
  componentOrOptions:
14
- | React.ComponentType<TProps>
15
- | $ReadOnly<FixturesOptions<TProps>>,
16
- ): $ReadOnly<FixturesOptions<TProps>> => {
17
+ | TComponent
18
+ | $ReadOnly<FixturesOptions<TComponent, TProps>>,
19
+ ): $ReadOnly<FixturesOptions<TComponent, TProps>> => {
17
20
  // To differentiate between a React component and a FixturesOptions object,
18
21
  // we have to do some type checking.
19
22
  //
@@ -66,10 +69,13 @@ const normalizeOptions = <TProps: {...}>(
66
69
  * storybook, the popular framework, uses both default and named exports for
67
70
  * its interface.
68
71
  */
69
- export const fixtures = <TProps: {...}>(
72
+ export const fixtures = <
73
+ TComponent: React.ComponentType<any>,
74
+ TProps: React.ElementConfig<TComponent>,
75
+ >(
70
76
  componentOrOptions:
71
- | React.ComponentType<TProps>
72
- | $ReadOnly<FixturesOptions<TProps>>,
77
+ | TComponent
78
+ | $ReadOnly<FixturesOptions<TComponent, TProps>>,
73
79
  fn: (
74
80
  fixture: (
75
81
  description: string,
@@ -86,7 +92,7 @@ export const fixtures = <TProps: {...}>(
86
92
  description: groupDescription,
87
93
  defaultWrapper,
88
94
  additionalAdapterOptions,
89
- } = normalizeOptions(componentOrOptions);
95
+ } = normalizeOptions<TComponent, TProps>(componentOrOptions);
90
96
 
91
97
  // 1. Create a new adapter group.
92
98
  const group = adapter.declareGroup<TProps>({
@@ -10,6 +10,13 @@ export type GetPropsOptions = {|
10
10
  * A function to call that will log output.
11
11
  */
12
12
  log: (message: string, ...args: Array<any>) => void,
13
+
14
+ /**
15
+ * A function to make a handler that will log all arguments with the given
16
+ * name or message. Useful for logging events as it avoids the boilerplate
17
+ * of the `log` function.
18
+ */
19
+ logHandler: (name: string) => (...args: Array<any>) => void,
13
20
  |};
14
21
 
15
22
  /**
@@ -23,11 +30,14 @@ export type FixturesAdapterOptions = {|
23
30
  /**
24
31
  * Options to describe a collection of fixtures.
25
32
  */
26
- export type FixturesOptions<TProps: {...}> = {|
33
+ export type FixturesOptions<
34
+ TComponent: React.ComponentType<any>,
35
+ TProps: React.ElementConfig<TComponent>,
36
+ > = {|
27
37
  /**
28
38
  * The component being tested by the fixtures.
29
39
  */
30
- component: React.ComponentType<TProps>,
40
+ component: TComponent,
31
41
 
32
42
  /**
33
43
  * Optional title of the fixture collection.