UNPKG

16.6 kBJavaScriptView Raw
1"use strict";
2
3require("core-js/modules/es.symbol");
4
5require("core-js/modules/es.symbol.description");
6
7require("core-js/modules/es.symbol.iterator");
8
9require("core-js/modules/es.array.concat");
10
11require("core-js/modules/es.array.every");
12
13require("core-js/modules/es.array.for-each");
14
15require("core-js/modules/es.array.from");
16
17require("core-js/modules/es.array.includes");
18
19require("core-js/modules/es.array.is-array");
20
21require("core-js/modules/es.array.iterator");
22
23require("core-js/modules/es.array.map");
24
25require("core-js/modules/es.date.to-string");
26
27require("core-js/modules/es.function.bind");
28
29require("core-js/modules/es.function.name");
30
31require("core-js/modules/es.object.define-property");
32
33require("core-js/modules/es.object.entries");
34
35require("core-js/modules/es.object.keys");
36
37require("core-js/modules/es.object.to-string");
38
39require("core-js/modules/es.regexp.to-string");
40
41require("core-js/modules/es.set");
42
43require("core-js/modules/es.string.includes");
44
45require("core-js/modules/es.string.iterator");
46
47require("core-js/modules/es.weak-map");
48
49require("core-js/modules/web.dom-collections.for-each");
50
51require("core-js/modules/web.dom-collections.iterator");
52
53Object.defineProperty(exports, "__esModule", {
54 value: true
55});
56exports.useMemo = useMemo;
57exports.useCallback = useCallback;
58exports.useRef = useRef;
59exports.useState = useState;
60exports.useReducer = useReducer;
61exports.useEffect = useEffect;
62exports.useChannel = useChannel;
63exports.useStoryContext = useStoryContext;
64exports.useParameter = useParameter;
65exports.applyHooks = exports.HooksContext = void 0;
66
67var _global = _interopRequireDefault(require("global"));
68
69var _clientLogger = require("@storybook/client-logger");
70
71var _coreEvents = require("@storybook/core-events");
72
73var _index = require("./index");
74
75function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
76
77function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
78
79function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
80
81function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
82
83function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
84
85function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }
86
87function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }
88
89function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }
90
91function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }
92
93function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
94
95function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
96
97function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
98
99var RenderEvents = [_coreEvents.STORY_RENDERED, _coreEvents.DOCS_RENDERED];
100
101var HooksContext =
102/*#__PURE__*/
103function () {
104 function HooksContext() {
105 var _this = this;
106
107 _classCallCheck(this, HooksContext);
108
109 this.hookListsMap = void 0;
110 this.mountedDecorators = void 0;
111 this.prevMountedDecorators = void 0;
112 this.currentHooks = void 0;
113 this.nextHookIndex = void 0;
114 this.currentPhase = void 0;
115 this.currentEffects = void 0;
116 this.prevEffects = void 0;
117 this.currentDecoratorName = void 0;
118 this.hasUpdates = void 0;
119 this.currentContext = void 0;
120
121 this.renderListener = function () {
122 _this.triggerEffects();
123
124 _this.currentContext = null;
125
126 _this.removeRenderListeners();
127 };
128
129 this.init();
130 }
131
132 _createClass(HooksContext, [{
133 key: "init",
134 value: function init() {
135 this.hookListsMap = new WeakMap();
136 this.mountedDecorators = new Set();
137 this.prevMountedDecorators = this.mountedDecorators;
138 this.currentHooks = [];
139 this.nextHookIndex = 0;
140 this.currentPhase = 'NONE';
141 this.currentEffects = [];
142 this.prevEffects = [];
143 this.currentDecoratorName = null;
144 this.hasUpdates = false;
145 this.currentContext = null;
146 }
147 }, {
148 key: "clean",
149 value: function clean() {
150 this.prevEffects.forEach(function (effect) {
151 if (effect.destroy) {
152 effect.destroy();
153 }
154 });
155 this.init();
156 this.removeRenderListeners();
157 }
158 }, {
159 key: "getNextHook",
160 value: function getNextHook() {
161 var hook = this.currentHooks[this.nextHookIndex];
162 this.nextHookIndex += 1;
163 return hook;
164 }
165 }, {
166 key: "triggerEffects",
167 value: function triggerEffects() {
168 var _this2 = this;
169
170 // destroy removed effects
171 this.prevEffects.forEach(function (effect) {
172 if (!_this2.currentEffects.includes(effect) && effect.destroy) {
173 effect.destroy();
174 }
175 }); // trigger added effects
176
177 this.currentEffects.forEach(function (effect) {
178 if (!_this2.prevEffects.includes(effect)) {
179 // eslint-disable-next-line no-param-reassign
180 effect.destroy = effect.create();
181 }
182 });
183 this.prevEffects = this.currentEffects;
184 this.currentEffects = [];
185 }
186 }, {
187 key: "addRenderListeners",
188 value: function addRenderListeners() {
189 var _this3 = this;
190
191 this.removeRenderListeners();
192
193 var channel = _index.addons.getChannel();
194
195 RenderEvents.forEach(function (e) {
196 return channel.on(e, _this3.renderListener);
197 });
198 }
199 }, {
200 key: "removeRenderListeners",
201 value: function removeRenderListeners() {
202 var _this4 = this;
203
204 var channel = _index.addons.getChannel();
205
206 RenderEvents.forEach(function (e) {
207 return channel.removeListener(e, _this4.renderListener);
208 });
209 }
210 }]);
211
212 return HooksContext;
213}();
214
215exports.HooksContext = HooksContext;
216
217var hookify = function hookify(fn) {
218 return function () {
219 var _ref = typeof (arguments.length <= 0 ? undefined : arguments[0]) === 'function' ? arguments.length <= 1 ? undefined : arguments[1] : arguments.length <= 0 ? undefined : arguments[0],
220 hooks = _ref.hooks;
221
222 var prevPhase = hooks.currentPhase;
223 var prevHooks = hooks.currentHooks;
224 var prevNextHookIndex = hooks.nextHookIndex;
225 var prevDecoratorName = hooks.currentDecoratorName;
226 hooks.currentDecoratorName = fn.name;
227
228 if (hooks.prevMountedDecorators.has(fn)) {
229 hooks.currentPhase = 'UPDATE';
230 hooks.currentHooks = hooks.hookListsMap.get(fn) || [];
231 } else {
232 hooks.currentPhase = 'MOUNT';
233 hooks.currentHooks = [];
234 hooks.hookListsMap.set(fn, hooks.currentHooks);
235 hooks.prevMountedDecorators.add(fn);
236 }
237
238 hooks.nextHookIndex = 0;
239 var prevContext = _global["default"].STORYBOOK_HOOKS_CONTEXT;
240 _global["default"].STORYBOOK_HOOKS_CONTEXT = hooks;
241 var result = fn.apply(void 0, arguments);
242 _global["default"].STORYBOOK_HOOKS_CONTEXT = prevContext;
243
244 if (hooks.currentPhase === 'UPDATE' && hooks.getNextHook() != null) {
245 throw new Error('Rendered fewer hooks than expected. This may be caused by an accidental early return statement.');
246 }
247
248 hooks.currentPhase = prevPhase;
249 hooks.currentHooks = prevHooks;
250 hooks.nextHookIndex = prevNextHookIndex;
251 hooks.currentDecoratorName = prevDecoratorName;
252 return result;
253 };
254}; // Counter to prevent infinite loops.
255
256
257var numberOfRenders = 0;
258var RENDER_LIMIT = 25;
259
260var applyHooks = function applyHooks(applyDecorators) {
261 return function (getStory, decorators) {
262 var decorated = applyDecorators(hookify(getStory), decorators.map(hookify));
263 return function (context) {
264 var hooks = context.hooks;
265 hooks.prevMountedDecorators = hooks.mountedDecorators;
266 hooks.mountedDecorators = new Set([getStory].concat(_toConsumableArray(decorators)));
267 hooks.currentContext = context;
268 hooks.hasUpdates = false;
269 var result = decorated(context);
270 numberOfRenders = 1;
271
272 while (hooks.hasUpdates) {
273 hooks.hasUpdates = false;
274 hooks.currentEffects = [];
275 result = decorated(context);
276 numberOfRenders += 1;
277
278 if (numberOfRenders > RENDER_LIMIT) {
279 throw new Error('Too many re-renders. Storybook limits the number of renders to prevent an infinite loop.');
280 }
281 }
282
283 hooks.addRenderListeners();
284 return result;
285 };
286 };
287};
288
289exports.applyHooks = applyHooks;
290
291var areDepsEqual = function areDepsEqual(deps, nextDeps) {
292 return deps.length === nextDeps.length && deps.every(function (dep, i) {
293 return dep === nextDeps[i];
294 });
295};
296
297var invalidHooksError = function invalidHooksError() {
298 return new Error('Storybook preview hooks can only be called inside decorators and story functions.');
299};
300
301function getHooksContextOrNull() {
302 return _global["default"].STORYBOOK_HOOKS_CONTEXT || null;
303}
304
305function getHooksContextOrThrow() {
306 var hooks = getHooksContextOrNull();
307
308 if (hooks == null) {
309 throw invalidHooksError();
310 }
311
312 return hooks;
313}
314
315function useHook(name, callback, deps) {
316 var hooks = getHooksContextOrThrow();
317
318 if (hooks.currentPhase === 'MOUNT') {
319 if (deps != null && !Array.isArray(deps)) {
320 _clientLogger.logger.warn("".concat(name, " received a final argument that is not an array (instead, received ").concat(deps, "). When specified, the final argument must be an array."));
321 }
322
323 var _hook = {
324 name: name,
325 deps: deps
326 };
327 hooks.currentHooks.push(_hook);
328 callback(_hook);
329 return _hook;
330 }
331
332 if (hooks.currentPhase === 'UPDATE') {
333 var _hook2 = hooks.getNextHook();
334
335 if (_hook2 == null) {
336 throw new Error('Rendered more hooks than during the previous render.');
337 }
338
339 if (_hook2.name !== name) {
340 _clientLogger.logger.warn("Storybook has detected a change in the order of Hooks".concat(hooks.currentDecoratorName ? " called by ".concat(hooks.currentDecoratorName) : '', ". This will lead to bugs and errors if not fixed."));
341 }
342
343 if (deps != null && _hook2.deps == null) {
344 _clientLogger.logger.warn("".concat(name, " received a final argument during this render, but not during the previous render. Even though the final argument is optional, its type cannot change between renders."));
345 }
346
347 if (deps != null && _hook2.deps != null && deps.length !== _hook2.deps.length) {
348 _clientLogger.logger.warn("The final argument passed to ".concat(name, " changed size between renders. The order and size of this array must remain constant.\nPrevious: ").concat(_hook2.deps, "\nIncoming: ").concat(deps));
349 }
350
351 if (deps == null || _hook2.deps == null || !areDepsEqual(deps, _hook2.deps)) {
352 callback(_hook2);
353 _hook2.deps = deps;
354 }
355
356 return _hook2;
357 }
358
359 throw invalidHooksError();
360}
361
362function useMemoLike(name, nextCreate, deps) {
363 var _useHook = useHook(name, function (hook) {
364 // eslint-disable-next-line no-param-reassign
365 hook.memoizedState = nextCreate();
366 }, deps),
367 memoizedState = _useHook.memoizedState;
368
369 return memoizedState;
370}
371/* Returns a memoized value, see https://reactjs.org/docs/hooks-reference.html#usememo */
372
373
374function useMemo(nextCreate, deps) {
375 return useMemoLike('useMemo', nextCreate, deps);
376}
377/* Returns a memoized callback, see https://reactjs.org/docs/hooks-reference.html#usecallback */
378
379
380function useCallback(callback, deps) {
381 return useMemoLike('useCallback', function () {
382 return callback;
383 }, deps);
384}
385
386function useRefLike(name, initialValue) {
387 return useMemoLike(name, function () {
388 return {
389 current: initialValue
390 };
391 }, []);
392}
393/* Returns a mutable ref object, see https://reactjs.org/docs/hooks-reference.html#useref */
394
395
396function useRef(initialValue) {
397 return useRefLike('useRef', initialValue);
398}
399
400function triggerUpdate() {
401 var hooks = getHooksContextOrNull(); // Rerun getStory if updates were triggered synchronously, force rerender otherwise
402
403 if (hooks != null && hooks.currentPhase !== 'NONE') {
404 hooks.hasUpdates = true;
405 } else {
406 try {
407 _index.addons.getChannel().emit(_coreEvents.FORCE_RE_RENDER);
408 } catch (e) {
409 _clientLogger.logger.warn('State updates of Storybook preview hooks work only in browser');
410 }
411 }
412}
413
414function useStateLike(name, initialState) {
415 var stateRef = useRefLike(name, // @ts-ignore S type should never be function, but there's no way to tell that to TypeScript
416 typeof initialState === 'function' ? initialState() : initialState);
417
418 var setState = function setState(update) {
419 // @ts-ignore S type should never be function, but there's no way to tell that to TypeScript
420 stateRef.current = typeof update === 'function' ? update(stateRef.current) : update;
421 triggerUpdate();
422 };
423
424 return [stateRef.current, setState];
425}
426/* Returns a stateful value, and a function to update it, see https://reactjs.org/docs/hooks-reference.html#usestate */
427
428
429function useState(initialState) {
430 return useStateLike('useState', initialState);
431}
432/* A redux-like alternative to useState, see https://reactjs.org/docs/hooks-reference.html#usereducer */
433
434
435function useReducer(reducer, initialArg, init) {
436 var initialState = init != null ? function () {
437 return init(initialArg);
438 } : initialArg;
439
440 var _useStateLike = useStateLike('useReducer', initialState),
441 _useStateLike2 = _slicedToArray(_useStateLike, 2),
442 state = _useStateLike2[0],
443 setState = _useStateLike2[1];
444
445 var dispatch = function dispatch(action) {
446 return setState(function (prevState) {
447 return reducer(prevState, action);
448 });
449 };
450
451 return [state, dispatch];
452}
453/*
454 Triggers a side effect, see https://reactjs.org/docs/hooks-reference.html#usestate
455 Effects are triggered synchronously after rendering the story
456*/
457
458
459function useEffect(create, deps) {
460 var hooks = getHooksContextOrThrow();
461 var effect = useMemoLike('useEffect', function () {
462 return {
463 create: create
464 };
465 }, deps);
466
467 if (!hooks.currentEffects.includes(effect)) {
468 hooks.currentEffects.push(effect);
469 }
470}
471
472/* Accepts a map of Storybook channel event listeners, returns an emit function */
473function useChannel(eventMap) {
474 var deps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
475
476 var channel = _index.addons.getChannel();
477
478 useEffect(function () {
479 Object.entries(eventMap).forEach(function (_ref2) {
480 var _ref3 = _slicedToArray(_ref2, 2),
481 type = _ref3[0],
482 listener = _ref3[1];
483
484 return channel.on(type, listener);
485 });
486 return function () {
487 Object.entries(eventMap).forEach(function (_ref4) {
488 var _ref5 = _slicedToArray(_ref4, 2),
489 type = _ref5[0],
490 listener = _ref5[1];
491
492 return channel.removeListener(type, listener);
493 });
494 };
495 }, [].concat(_toConsumableArray(Object.keys(eventMap)), _toConsumableArray(deps)));
496 return channel.emit.bind(channel);
497}
498/* Returns current story context */
499
500
501function useStoryContext() {
502 var _getHooksContextOrThr = getHooksContextOrThrow(),
503 currentContext = _getHooksContextOrThr.currentContext;
504
505 if (currentContext == null) {
506 throw invalidHooksError();
507 }
508
509 return currentContext;
510}
511/* Returns current value of a story parameter */
512
513
514function useParameter(parameterKey, defaultValue) {
515 var _useStoryContext = useStoryContext(),
516 parameters = _useStoryContext.parameters;
517
518 if (parameterKey) {
519 return parameters[parameterKey] || defaultValue;
520 }
521
522 return undefined;
523}
\No newline at end of file