1 | import "core-js/modules/es.array.reduce.js";
|
2 |
|
3 |
|
4 | import memoize from 'memoizerific';
|
5 | import dedent from 'ts-dedent';
|
6 | import stable from 'stable';
|
7 | import mapValues from 'lodash/mapValues';
|
8 | import pick from 'lodash/pick';
|
9 | import deprecate from 'util-deprecate';
|
10 | import Events from '@storybook/core-events';
|
11 | import { logger } from '@storybook/client-logger';
|
12 | import { sanitize, toId } from '@storybook/csf';
|
13 | import { combineArgs, mapArgsToTypes, validateOptions } from './args';
|
14 | import { HooksContext } from './hooks';
|
15 | import { storySort } from './storySort';
|
16 | import { combineParameters } from './parameters';
|
17 | import { ensureArgTypes } from './ensureArgTypes';
|
18 | import { inferArgTypes } from './inferArgTypes';
|
19 | import { inferControls } from './inferControls';
|
20 |
|
21 | function extractSanitizedKindNameFromStorySpecifier(storySpecifier) {
|
22 | if (typeof storySpecifier === 'string') {
|
23 | return storySpecifier.split('--').shift();
|
24 | }
|
25 |
|
26 | return sanitize(storySpecifier.kind);
|
27 | }
|
28 |
|
29 | function extractIdFromStorySpecifier(storySpecifier) {
|
30 | if (typeof storySpecifier === 'string') {
|
31 | return storySpecifier;
|
32 | }
|
33 |
|
34 | return toId(storySpecifier.kind, storySpecifier.name);
|
35 | }
|
36 |
|
37 | const isStoryDocsOnly = parameters => {
|
38 | return parameters && parameters.docsOnly;
|
39 | };
|
40 |
|
41 | const includeStory = (story, options = {
|
42 | includeDocsOnly: false
|
43 | }) => {
|
44 | if (options.includeDocsOnly) {
|
45 | return true;
|
46 | }
|
47 |
|
48 | return !isStoryDocsOnly(story.parameters);
|
49 | };
|
50 |
|
51 | const checkGlobals = parameters => {
|
52 | const {
|
53 | globals,
|
54 | globalTypes
|
55 | } = parameters;
|
56 |
|
57 | if (globals || globalTypes) {
|
58 | logger.error('Global args/argTypes can only be set globally', JSON.stringify({
|
59 | globals,
|
60 | globalTypes
|
61 | }));
|
62 | }
|
63 | };
|
64 |
|
65 | const checkStorySort = parameters => {
|
66 | const {
|
67 | options
|
68 | } = parameters;
|
69 | if (options !== null && options !== void 0 && options.storySort) logger.error('The storySort option parameter can only be set globally');
|
70 | };
|
71 |
|
72 | const storyFnWarning = deprecate(() => {}, dedent`
|
73 | \`storyFn\` is deprecated and will be removed in Storybook 7.0.
|
74 |
|
75 | https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#deprecated-storyfn`);
|
76 | const argTypeDefaultValueWarning = deprecate(() => {}, dedent`
|
77 | \`argType.defaultValue\` is deprecated and will be removed in Storybook 7.0.
|
78 |
|
79 | https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#deprecated-argtype-defaultValue`);
|
80 |
|
81 | const toExtracted = obj => Object.entries(obj).reduce((acc, [key, value]) => {
|
82 | if (typeof value === 'function') {
|
83 | return acc;
|
84 | }
|
85 |
|
86 |
|
87 |
|
88 |
|
89 | if (['hooks', 'argTypes'].includes(key)) {
|
90 | return acc;
|
91 | }
|
92 |
|
93 | if (Array.isArray(value)) {
|
94 | return Object.assign(acc, {
|
95 | [key]: value.slice().sort()
|
96 | });
|
97 | }
|
98 |
|
99 | return Object.assign(acc, {
|
100 | [key]: value
|
101 | });
|
102 | }, {});
|
103 |
|
104 | export default class StoryStore {
|
105 |
|
106 |
|
107 | constructor(params) {
|
108 | this._error = void 0;
|
109 | this._channel = void 0;
|
110 | this._configuring = void 0;
|
111 | this._globals = void 0;
|
112 | this._initialGlobals = void 0;
|
113 | this._defaultGlobals = void 0;
|
114 | this._globalMetadata = void 0;
|
115 | this._kinds = void 0;
|
116 | this._stories = void 0;
|
117 | this._argsEnhancers = void 0;
|
118 | this._argTypesEnhancers = void 0;
|
119 | this._selectionSpecifier = void 0;
|
120 | this._selection = void 0;
|
121 |
|
122 | this.remove = (id, {
|
123 | allowUnsafe = false
|
124 | } = {}) => {
|
125 | if (!this._configuring && !allowUnsafe) throw new Error('Cannot remove a story when not configuring, see https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#story-store-immutable-outside-of-configuration');
|
126 | const {
|
127 | _stories
|
128 | } = this;
|
129 | const story = _stories[id];
|
130 | delete _stories[id];
|
131 | if (story) story.hooks.clean();
|
132 | };
|
133 |
|
134 | this.fromId = id => {
|
135 | try {
|
136 | const data = this._stories[id];
|
137 |
|
138 | if (!data || !data.getDecorated) {
|
139 | return null;
|
140 | }
|
141 |
|
142 | return this.mergeAdditionalDataToStory(data);
|
143 | } catch (e) {
|
144 | logger.warn('failed to get story:', this._stories);
|
145 | logger.error(e);
|
146 | return null;
|
147 | }
|
148 | };
|
149 |
|
150 | this.setError = err => {
|
151 | this._error = err;
|
152 | };
|
153 |
|
154 | this.getError = () => this._error;
|
155 |
|
156 | this.getSelection = () => this._selection;
|
157 |
|
158 | this.getDataForManager = () => {
|
159 | return {
|
160 | v: 2,
|
161 | globalParameters: this._globalMetadata.parameters,
|
162 | globals: this._globals,
|
163 | error: this.getError(),
|
164 | kindParameters: mapValues(this._kinds, metadata => metadata.parameters),
|
165 | stories: this.extract({
|
166 | includeDocsOnly: true,
|
167 | normalizeParameters: true
|
168 | })
|
169 | };
|
170 | };
|
171 |
|
172 | this.getStoriesJsonData = () => {
|
173 | const value = this.getDataForManager();
|
174 | const allowed = ['fileName', 'docsOnly', 'framework', '__id', '__isArgsStory'];
|
175 | return {
|
176 | v: 2,
|
177 | globalParameters: pick(value.globalParameters, allowed),
|
178 | kindParameters: mapValues(value.kindParameters, v => pick(v, allowed)),
|
179 | stories: mapValues(value.stories, v => Object.assign({}, pick(v, ['id', 'name', 'kind', 'story']), {
|
180 | parameters: pick(v.parameters, allowed)
|
181 | }))
|
182 | };
|
183 | };
|
184 |
|
185 | this.pushToManager = () => {
|
186 | if (this._channel) {
|
187 |
|
188 | this._channel.emit(Events.SET_STORIES, this.getDataForManager());
|
189 | }
|
190 | };
|
191 |
|
192 | this.getStoriesForKind = kind => this.raw().filter(story => story.kind === kind);
|
193 |
|
194 |
|
195 | this._configuring = true;
|
196 | this._globals = {};
|
197 | this._defaultGlobals = {};
|
198 | this._initialGlobals = {};
|
199 | this._globalMetadata = {
|
200 | parameters: {},
|
201 | decorators: [],
|
202 | loaders: []
|
203 | };
|
204 | this._kinds = {};
|
205 | this._stories = {};
|
206 | this._argsEnhancers = [];
|
207 | this._argTypesEnhancers = [ensureArgTypes];
|
208 | this._error = undefined;
|
209 | this._channel = params.channel;
|
210 | this.setupListeners();
|
211 | }
|
212 |
|
213 | setupListeners() {
|
214 |
|
215 | if (!this._channel) return;
|
216 |
|
217 | this._channel.on(Events.SET_CURRENT_STORY, ({
|
218 | storyId,
|
219 | viewMode
|
220 | }) => this.setSelection({
|
221 | storyId,
|
222 | viewMode
|
223 | }));
|
224 |
|
225 | this._channel.on(Events.UPDATE_STORY_ARGS, ({
|
226 | storyId,
|
227 | updatedArgs
|
228 | }) => this.updateStoryArgs(storyId, updatedArgs));
|
229 |
|
230 | this._channel.on(Events.RESET_STORY_ARGS, ({
|
231 | storyId,
|
232 | argNames
|
233 | }) => this.resetStoryArgs(storyId, argNames));
|
234 |
|
235 | this._channel.on(Events.UPDATE_GLOBALS, ({
|
236 | globals
|
237 | }) => this.updateGlobals(globals));
|
238 | }
|
239 |
|
240 | startConfiguring() {
|
241 | this._configuring = true;
|
242 |
|
243 | const safePush = (enhancer, enhancers) => {
|
244 | if (!enhancers.includes(enhancer)) enhancers.push(enhancer);
|
245 | };
|
246 |
|
247 |
|
248 | safePush(inferArgTypes, this._argTypesEnhancers);
|
249 | safePush(inferControls, this._argTypesEnhancers);
|
250 | }
|
251 |
|
252 | finishConfiguring() {
|
253 | this._configuring = false;
|
254 | const {
|
255 | globals = {},
|
256 | globalTypes = {}
|
257 | } = this._globalMetadata.parameters;
|
258 | const allowedGlobals = new Set([...Object.keys(globals), ...Object.keys(globalTypes)]);
|
259 | const defaultGlobals = Object.entries(globalTypes).reduce((acc, [arg, {
|
260 | defaultValue
|
261 | }]) => {
|
262 | if (defaultValue) acc[arg] = defaultValue;
|
263 | return acc;
|
264 | }, {});
|
265 | this._initialGlobals = Object.assign({}, defaultGlobals, globals);
|
266 |
|
267 |
|
268 |
|
269 |
|
270 | this._globals = Object.entries(this._globals || {}).reduce((acc, [key, previousValue]) => {
|
271 | if (allowedGlobals.has(key)) acc[key] = previousValue;
|
272 | return acc;
|
273 | }, Object.assign({}, this._initialGlobals));
|
274 |
|
275 | const stories = this.sortedStories();
|
276 | let foundStory;
|
277 |
|
278 | if (this._selectionSpecifier && !this._selection) {
|
279 | const {
|
280 | storySpecifier,
|
281 | viewMode,
|
282 | args: urlArgs,
|
283 | globals: urlGlobals
|
284 | } = this._selectionSpecifier;
|
285 |
|
286 | if (urlGlobals) {
|
287 | const allowedUrlGlobals = Object.entries(urlGlobals).reduce((acc, [key, value]) => {
|
288 | if (allowedGlobals.has(key)) acc[key] = value;
|
289 | return acc;
|
290 | }, {});
|
291 | this._globals = combineParameters(this._globals, allowedUrlGlobals);
|
292 | }
|
293 |
|
294 | if (storySpecifier === '*') {
|
295 |
|
296 | [foundStory] = stories;
|
297 | } else if (typeof storySpecifier === 'string') {
|
298 |
|
299 | foundStory = Object.values(stories).find(s => s.id === storySpecifier);
|
300 |
|
301 | if (!foundStory) {
|
302 |
|
303 | foundStory = Object.values(stories).find(s => s.id.startsWith(storySpecifier));
|
304 | }
|
305 | } else {
|
306 |
|
307 | const {
|
308 | name,
|
309 | kind
|
310 | } = storySpecifier;
|
311 | foundStory = this.getRawStory(kind, name);
|
312 | }
|
313 |
|
314 | if (foundStory) {
|
315 | if (urlArgs) {
|
316 | const mappedUrlArgs = mapArgsToTypes(urlArgs, foundStory.argTypes);
|
317 | foundStory.args = combineArgs(foundStory.args, mappedUrlArgs);
|
318 | }
|
319 |
|
320 | foundStory.args = validateOptions(foundStory.args, foundStory.argTypes);
|
321 | this.setSelection({
|
322 | storyId: foundStory.id,
|
323 | viewMode
|
324 | });
|
325 |
|
326 | this._channel.emit(Events.STORY_SPECIFIED, {
|
327 | storyId: foundStory.id,
|
328 | viewMode
|
329 | });
|
330 | }
|
331 | }
|
332 |
|
333 |
|
334 |
|
335 | if (!foundStory && this._channel) {
|
336 | this._channel.emit(Events.CURRENT_STORY_WAS_SET, this._selection);
|
337 | }
|
338 |
|
339 | this.pushToManager();
|
340 | }
|
341 |
|
342 | addGlobalMetadata({
|
343 | parameters = {},
|
344 | decorators = [],
|
345 | loaders = []
|
346 | }) {
|
347 | if (parameters) {
|
348 | const {
|
349 | args,
|
350 | argTypes
|
351 | } = parameters;
|
352 | if (args || argTypes) logger.warn('Found args/argTypes in global parameters.', JSON.stringify({
|
353 | args,
|
354 | argTypes
|
355 | }));
|
356 | }
|
357 |
|
358 | const globalParameters = this._globalMetadata.parameters;
|
359 | this._globalMetadata.parameters = combineParameters(globalParameters, parameters);
|
360 |
|
361 | function _safeAdd(items, collection, caption) {
|
362 | items.forEach(item => {
|
363 | if (collection.includes(item)) {
|
364 | logger.warn(`You tried to add a duplicate ${caption}, this is not expected`, item);
|
365 | } else {
|
366 | collection.push(item);
|
367 | }
|
368 | });
|
369 | }
|
370 |
|
371 | _safeAdd(decorators, this._globalMetadata.decorators, 'decorator');
|
372 |
|
373 | _safeAdd(loaders, this._globalMetadata.loaders, 'loader');
|
374 | }
|
375 |
|
376 | clearGlobalDecorators() {
|
377 | this._globalMetadata.decorators = [];
|
378 | }
|
379 |
|
380 | ensureKind(kind) {
|
381 | if (!this._kinds[kind]) {
|
382 | this._kinds[kind] = {
|
383 | order: Object.keys(this._kinds).length,
|
384 | parameters: {},
|
385 | decorators: [],
|
386 | loaders: []
|
387 | };
|
388 | }
|
389 | }
|
390 |
|
391 | addKindMetadata(kind, {
|
392 | parameters = {},
|
393 | decorators = [],
|
394 | loaders = []
|
395 | }) {
|
396 | if (this.shouldBlockAddingKindMetadata(kind)) {
|
397 | return;
|
398 | }
|
399 |
|
400 | this.ensureKind(kind);
|
401 |
|
402 | if (parameters) {
|
403 | checkGlobals(parameters);
|
404 | checkStorySort(parameters);
|
405 | }
|
406 |
|
407 | this._kinds[kind].parameters = combineParameters(this._kinds[kind].parameters, parameters);
|
408 |
|
409 | this._kinds[kind].decorators.push(...decorators);
|
410 |
|
411 | this._kinds[kind].loaders.push(...loaders);
|
412 | }
|
413 |
|
414 | addArgsEnhancer(argsEnhancer) {
|
415 | if (Object.keys(this._stories).length > 0) throw new Error('Cannot add an args enhancer to the store after a story has been added.');
|
416 |
|
417 | this._argsEnhancers.push(argsEnhancer);
|
418 | }
|
419 |
|
420 | addArgTypesEnhancer(argTypesEnhancer) {
|
421 | if (Object.keys(this._stories).length > 0) throw new Error('Cannot add an argTypes enhancer to the store after a story has been added.');
|
422 |
|
423 | this._argTypesEnhancers.push(argTypesEnhancer);
|
424 | }
|
425 |
|
426 |
|
427 | combineStoryParameters(parameters, kind) {
|
428 | return combineParameters(this._globalMetadata.parameters, this._kinds[kind].parameters, parameters);
|
429 | }
|
430 |
|
431 | shouldBlockAddingStory(id) {
|
432 | return this.isSingleStoryMode() && id !== extractIdFromStorySpecifier(this._selectionSpecifier.storySpecifier);
|
433 | }
|
434 |
|
435 | shouldBlockAddingKindMetadata(kind) {
|
436 | return this.isSingleStoryMode() && sanitize(kind) !== extractSanitizedKindNameFromStorySpecifier(this._selectionSpecifier.storySpecifier);
|
437 | }
|
438 |
|
439 | addStory({
|
440 | id,
|
441 | kind,
|
442 | name,
|
443 | storyFn: original,
|
444 | parameters: storyParameters = {},
|
445 | decorators: storyDecorators = [],
|
446 | loaders: storyLoaders = []
|
447 | }, {
|
448 | applyDecorators,
|
449 | allowUnsafe = false
|
450 | }) {
|
451 | if (!this._configuring && !allowUnsafe) throw new Error('Cannot add a story when not configuring, see https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#story-store-immutable-outside-of-configuration');
|
452 |
|
453 | if (this.shouldBlockAddingStory(id)) {
|
454 | return;
|
455 | }
|
456 |
|
457 | checkGlobals(storyParameters);
|
458 | checkStorySort(storyParameters);
|
459 | const {
|
460 | _stories
|
461 | } = this;
|
462 |
|
463 | if (_stories[id]) {
|
464 | logger.warn(dedent`
|
465 | Story with id ${id} already exists in the store!
|
466 |
|
467 | Perhaps you added the same story twice, or you have a name collision?
|
468 | Story ids need to be unique -- ensure you aren't using the same names modulo url-sanitization.
|
469 | `);
|
470 | }
|
471 |
|
472 | const identification = {
|
473 | id,
|
474 | kind,
|
475 | name,
|
476 | story: name
|
477 |
|
478 | };
|
479 |
|
480 | const getOriginal = () => original;
|
481 |
|
482 | this.ensureKind(kind);
|
483 | const kindMetadata = this._kinds[kind];
|
484 | const decorators = [...storyDecorators, ...kindMetadata.decorators, ...this._globalMetadata.decorators];
|
485 | const loaders = [...this._globalMetadata.loaders, ...kindMetadata.loaders, ...storyLoaders];
|
486 |
|
487 | const finalStoryFn = context => {
|
488 | const {
|
489 | args = {},
|
490 | argTypes = {},
|
491 | parameters
|
492 | } = context;
|
493 | const {
|
494 | passArgsFirst = true
|
495 | } = parameters;
|
496 | const mapped = Object.assign({}, context, {
|
497 | args: Object.entries(args).reduce((acc, [key, val]) => {
|
498 | const {
|
499 | mapping
|
500 | } = argTypes[key] || {};
|
501 | acc[key] = mapping && val in mapping ? mapping[val] : val;
|
502 | return acc;
|
503 | }, {})
|
504 | });
|
505 | return passArgsFirst ? original(mapped.args, mapped) : original(mapped);
|
506 | };
|
507 |
|
508 |
|
509 | const getDecorated = memoize(1)(() => applyDecorators(finalStoryFn, decorators));
|
510 | const hooks = new HooksContext();
|
511 |
|
512 | const combinedParameters = this.combineStoryParameters(storyParameters, kind);
|
513 |
|
514 |
|
515 |
|
516 |
|
517 |
|
518 | const {
|
519 | passArgsFirst = true
|
520 | } = combinedParameters;
|
521 |
|
522 | const __isArgsStory = passArgsFirst && original.length > 0;
|
523 |
|
524 | const {
|
525 | argTypes = {}
|
526 | } = this._argTypesEnhancers.reduce((accumulatedParameters, enhancer) => Object.assign({}, accumulatedParameters, {
|
527 | argTypes: enhancer(Object.assign({}, identification, {
|
528 | storyFn: original,
|
529 | parameters: accumulatedParameters,
|
530 | args: {},
|
531 | argTypes: {},
|
532 | globals: {},
|
533 | originalStoryFn: getOriginal()
|
534 | }))
|
535 | }), Object.assign({
|
536 | __isArgsStory
|
537 | }, combinedParameters));
|
538 |
|
539 | const storyParametersWithArgTypes = Object.assign({}, storyParameters, {
|
540 | argTypes,
|
541 | __isArgsStory
|
542 | });
|
543 |
|
544 | const storyFn = runtimeContext => {
|
545 | var _this$_selection;
|
546 |
|
547 | storyFnWarning();
|
548 | return getDecorated()(Object.assign({}, identification, runtimeContext, {
|
549 |
|
550 | parameters: this.combineStoryParameters(storyParametersWithArgTypes, kind),
|
551 | hooks,
|
552 | args: _stories[id].args,
|
553 | argTypes,
|
554 | globals: this._globals,
|
555 | viewMode: (_this$_selection = this._selection) === null || _this$_selection === void 0 ? void 0 : _this$_selection.viewMode,
|
556 | originalStoryFn: getOriginal()
|
557 | }));
|
558 | };
|
559 |
|
560 | const unboundStoryFn = context => getDecorated()(context);
|
561 |
|
562 | const applyLoaders = async () => {
|
563 | var _this$_selection2;
|
564 |
|
565 | const context = Object.assign({}, identification, {
|
566 |
|
567 | parameters: this.combineStoryParameters(storyParametersWithArgTypes, kind),
|
568 | hooks,
|
569 | args: _stories[id].args,
|
570 | argTypes,
|
571 | globals: this._globals,
|
572 | viewMode: (_this$_selection2 = this._selection) === null || _this$_selection2 === void 0 ? void 0 : _this$_selection2.viewMode,
|
573 | originalStoryFn: getOriginal()
|
574 | });
|
575 | const loadResults = await Promise.all(loaders.map(loader => loader(context)));
|
576 | const loaded = Object.assign({}, ...loadResults);
|
577 | return Object.assign({}, context, {
|
578 | loaded
|
579 | });
|
580 | };
|
581 |
|
582 |
|
583 | const passedArgs = Object.assign({}, this._kinds[kind].parameters.args, storyParameters.args);
|
584 | const defaultArgs = Object.entries(argTypes).reduce((acc, [arg, {
|
585 | defaultValue
|
586 | }]) => {
|
587 | if (typeof defaultValue !== 'undefined') {
|
588 | acc[arg] = defaultValue;
|
589 | }
|
590 |
|
591 | return acc;
|
592 | }, {});
|
593 |
|
594 | if (Object.keys(defaultArgs).length > 0) {
|
595 | argTypeDefaultValueWarning();
|
596 | }
|
597 |
|
598 | const initialArgsBeforeEnhancers = Object.assign({}, defaultArgs, passedArgs);
|
599 |
|
600 | const initialArgs = this._argsEnhancers.reduce((accumulatedArgs, enhancer) => Object.assign({}, accumulatedArgs, enhancer(Object.assign({}, identification, {
|
601 | parameters: combinedParameters,
|
602 | args: initialArgsBeforeEnhancers,
|
603 | argTypes,
|
604 | globals: {},
|
605 | originalStoryFn: getOriginal()
|
606 | }))), initialArgsBeforeEnhancers);
|
607 |
|
608 | const runPlayFunction = async () => {
|
609 | const {
|
610 | play
|
611 | } = combinedParameters;
|
612 | return play ? play() : undefined;
|
613 | };
|
614 |
|
615 | _stories[id] = Object.assign({}, identification, {
|
616 | hooks,
|
617 | getDecorated,
|
618 | getOriginal,
|
619 | applyLoaders,
|
620 | runPlayFunction,
|
621 | storyFn,
|
622 | unboundStoryFn,
|
623 | parameters: storyParametersWithArgTypes,
|
624 | args: initialArgs,
|
625 | argTypes,
|
626 | initialArgs
|
627 | });
|
628 | }
|
629 |
|
630 | removeStoryKind(kind, {
|
631 | allowUnsafe = false
|
632 | } = {}) {
|
633 | if (!this._configuring && !allowUnsafe) throw new Error('Cannot remove a kind when not configuring, see https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#story-store-immutable-outside-of-configuration');
|
634 | if (!this._kinds[kind]) return;
|
635 | this._kinds[kind].parameters = {};
|
636 | this._kinds[kind].decorators = [];
|
637 | this.cleanHooksForKind(kind);
|
638 | this._stories = Object.entries(this._stories).reduce((acc, [id, story]) => {
|
639 | if (story.kind !== kind) acc[id] = story;
|
640 | return acc;
|
641 | }, {});
|
642 | }
|
643 |
|
644 | updateGlobals(newGlobals) {
|
645 | this._globals = Object.assign({}, this._globals, newGlobals);
|
646 |
|
647 | this._channel.emit(Events.GLOBALS_UPDATED, {
|
648 | globals: this._globals,
|
649 | initialGlobals: this._initialGlobals
|
650 | });
|
651 | }
|
652 |
|
653 | updateStoryArgs(id, newArgs) {
|
654 | if (!this._stories[id]) throw new Error(`No story for id ${id}`);
|
655 | const {
|
656 | args
|
657 | } = this._stories[id];
|
658 | this._stories[id].args = Object.assign({}, args, newArgs);
|
659 |
|
660 | this._channel.emit(Events.STORY_ARGS_UPDATED, {
|
661 | storyId: id,
|
662 | args: this._stories[id].args
|
663 | });
|
664 | }
|
665 |
|
666 | resetStoryArgs(id, argNames) {
|
667 | if (!this._stories[id]) throw new Error(`No story for id ${id}`);
|
668 | const {
|
669 | args,
|
670 | initialArgs
|
671 | } = this._stories[id];
|
672 | this._stories[id].args = Object.assign({}, args);
|
673 |
|
674 | (argNames || Object.keys(args)).forEach(name => {
|
675 |
|
676 | this._stories[id].args[name] = initialArgs[name];
|
677 | });
|
678 |
|
679 | this._channel.emit(Events.STORY_ARGS_UPDATED, {
|
680 | storyId: id,
|
681 | args: this._stories[id].args
|
682 | });
|
683 | }
|
684 |
|
685 | raw(options) {
|
686 | return Object.values(this._stories).filter(i => !!i.getDecorated).filter(i => includeStory(i, options)).map(i => this.mergeAdditionalDataToStory(i));
|
687 | }
|
688 |
|
689 | sortedStories() {
|
690 | var _this$_globalMetadata, _this$_globalMetadata2;
|
691 |
|
692 |
|
693 |
|
694 | const kindOrder = mapValues(this._kinds, ({
|
695 | order
|
696 | }) => order);
|
697 | const storySortParameter = (_this$_globalMetadata = this._globalMetadata.parameters) === null || _this$_globalMetadata === void 0 ? void 0 : (_this$_globalMetadata2 = _this$_globalMetadata.options) === null || _this$_globalMetadata2 === void 0 ? void 0 : _this$_globalMetadata2.storySort;
|
698 | const storyEntries = Object.entries(this._stories);
|
699 |
|
700 | const stories = storyEntries.map(([id, story]) => [id, story, this._kinds[story.kind].parameters, this._globalMetadata.parameters]);
|
701 |
|
702 | if (storySortParameter) {
|
703 | let sortFn;
|
704 |
|
705 | if (typeof storySortParameter === 'function') {
|
706 | sortFn = storySortParameter;
|
707 | } else {
|
708 | sortFn = storySort(storySortParameter);
|
709 | }
|
710 |
|
711 | stable.inplace(stories, sortFn);
|
712 | } else {
|
713 | stable.inplace(stories, (s1, s2) => kindOrder[s1[1].kind] - kindOrder[s2[1].kind]);
|
714 | }
|
715 |
|
716 | return stories.map(([id, s]) => s);
|
717 | }
|
718 |
|
719 | extract(options = {}) {
|
720 | const stories = this.sortedStories();
|
721 |
|
722 | return stories.reduce((acc, story) => {
|
723 | if (!includeStory(story, options)) return acc;
|
724 | const extracted = toExtracted(story);
|
725 | if (options.normalizeParameters) return Object.assign(acc, {
|
726 | [story.id]: extracted
|
727 | });
|
728 | const {
|
729 | parameters,
|
730 | kind
|
731 | } = extracted;
|
732 | return Object.assign(acc, {
|
733 | [story.id]: Object.assign(extracted, {
|
734 | parameters: this.combineStoryParameters(parameters, kind)
|
735 | })
|
736 | });
|
737 | }, {});
|
738 | }
|
739 |
|
740 | clearError() {
|
741 | this._error = null;
|
742 | }
|
743 |
|
744 | setSelectionSpecifier(selectionSpecifier) {
|
745 | this._selectionSpecifier = selectionSpecifier;
|
746 | }
|
747 |
|
748 | setSelection(selection) {
|
749 | this._selection = selection;
|
750 |
|
751 | if (this._channel) {
|
752 | this._channel.emit(Events.CURRENT_STORY_WAS_SET, this._selection);
|
753 | }
|
754 | }
|
755 |
|
756 | isSingleStoryMode() {
|
757 | if (!this._selectionSpecifier) {
|
758 | return false;
|
759 | }
|
760 |
|
761 | const {
|
762 | singleStory,
|
763 | storySpecifier
|
764 | } = this._selectionSpecifier;
|
765 | return storySpecifier && storySpecifier !== '*' && singleStory;
|
766 | }
|
767 |
|
768 | getStoryKinds() {
|
769 | return Array.from(new Set(this.raw().map(s => s.kind)));
|
770 | }
|
771 |
|
772 | getRawStory(kind, name) {
|
773 | return this.getStoriesForKind(kind).find(s => s.name === name);
|
774 | }
|
775 |
|
776 | cleanHooks(id) {
|
777 | if (this._stories[id]) {
|
778 | this._stories[id].hooks.clean();
|
779 | }
|
780 | }
|
781 |
|
782 | cleanHooksForKind(kind) {
|
783 | this.getStoriesForKind(kind).map(story => this.cleanHooks(story.id));
|
784 | }
|
785 |
|
786 |
|
787 |
|
788 |
|
789 |
|
790 | getStorybook() {
|
791 | return Object.values(this.raw().reduce((kinds, story) => {
|
792 | if (!includeStory(story)) return kinds;
|
793 | const {
|
794 | kind,
|
795 | name,
|
796 | storyFn,
|
797 | parameters: {
|
798 | fileName
|
799 | }
|
800 | } = story;
|
801 |
|
802 | if (!kinds[kind]) kinds[kind] = {
|
803 | kind,
|
804 | fileName,
|
805 | stories: []
|
806 | };
|
807 | kinds[kind].stories.push({
|
808 | name,
|
809 | render: storyFn
|
810 | });
|
811 | return kinds;
|
812 | }, {})).sort((s1, s2) => this._kinds[s1.kind].order - this._kinds[s2.kind].order);
|
813 | }
|
814 |
|
815 | mergeAdditionalDataToStory(story) {
|
816 | return Object.assign({}, story, {
|
817 | parameters: this.combineStoryParameters(story.parameters, story.kind),
|
818 | globals: this._globals
|
819 | });
|
820 | }
|
821 |
|
822 | } |
\ | No newline at end of file |