Merge pull request #15056 from storybookjs/url-globals

Core: Add `globals` URL param and remove from sessionStorage
This commit is contained in:
Michael Shilman 2021-05-27 20:03:09 +08:00 committed by GitHub
commit 5ea8cdddeb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 320 additions and 267 deletions

View File

@ -1,5 +1,10 @@
import { navigate as navigateRouter, NavigateOptions } from '@reach/router';
import { NAVIGATE_URL, STORY_ARGS_UPDATED, SET_CURRENT_STORY } from '@storybook/core-events';
import {
NAVIGATE_URL,
STORY_ARGS_UPDATED,
SET_CURRENT_STORY,
GLOBALS_UPDATED,
} from '@storybook/core-events';
import { queryFromLocation, navigate as queryNavigate, buildArgsParam } from '@storybook/router';
import { toId, sanitize } from '@storybook/csf';
import deepEqual from 'fast-deep-equal';
@ -168,7 +173,7 @@ export const init: ModuleFn = ({ store, navigate, state, provider, fullAPI, ...r
fullAPI.on(SET_CURRENT_STORY, () => updateArgsParam());
let handleOrId: any;
fullAPI.on(STORY_ARGS_UPDATED, ({ args }) => {
fullAPI.on(STORY_ARGS_UPDATED, () => {
if ('requestIdleCallback' in globalWindow) {
if (handleOrId) globalWindow.cancelIdleCallback(handleOrId);
handleOrId = globalWindow.requestIdleCallback(updateArgsParam, { timeout: 1000 });
@ -178,6 +183,14 @@ export const init: ModuleFn = ({ store, navigate, state, provider, fullAPI, ...r
}
});
fullAPI.on(GLOBALS_UPDATED, ({ globals, initialGlobals }) => {
const { path } = fullAPI.getUrlState();
const argsString = buildArgsParam(initialGlobals, globals);
const globalsParam = argsString.length ? `&globals=${argsString}` : '';
queryNavigate(`${path}${globalsParam}`, { replace: true });
api.setQueryParams({ globals: argsString });
});
fullAPI.on(NAVIGATE_URL, (url: string, options: { [k: string]: any }) => {
fullAPI.navigateUrl(url, options);
});

View File

@ -3,7 +3,6 @@ import createChannel from '@storybook/channel-postmessage';
import { toId } from '@storybook/csf';
import addons, { mockChannel } from '@storybook/addons';
import Events from '@storybook/core-events';
import store2 from 'store2';
import StoryStore from './story_store';
import { defaultDecorateStory } from './decorators';
@ -16,8 +15,6 @@ jest.mock('@storybook/node-logger', () => ({
},
}));
jest.mock('store2');
let channel;
beforeEach(() => {
channel = createChannel({ page: 'preview' });
@ -433,14 +430,6 @@ describe('preview.story_store', () => {
});
});
it('sets session storage on initialization', () => {
(store2.session.set as any).mockClear();
const store = new StoryStore({ channel });
addStoryToStore(store, 'a', '1', () => 0);
store.finishConfiguring();
expect(store2.session.set).toHaveBeenCalled();
});
it('on HMR it sensibly re-initializes with memory', () => {
const store = new StoryStore({ channel });
addons.setChannel(channel);
@ -509,7 +498,10 @@ describe('preview.story_store', () => {
});
it('sensibly re-initializes with memory based on session storage', () => {
(store2.session.get as any).mockReturnValueOnce({
const store = new StoryStore({ channel });
store.setSelectionSpecifier({
storySpecifier: '*',
viewMode: 'story',
globals: {
arg1: 'arg1',
arg2: 2,
@ -517,8 +509,6 @@ describe('preview.story_store', () => {
arg4: 4,
},
});
const store = new StoryStore({ channel });
addons.setChannel(channel);
addStoryToStore(store, 'a', '1', () => 0);
@ -541,6 +531,7 @@ describe('preview.story_store', () => {
expect(store.getRawStory('a', '1').globals).toEqual({
// We should keep the previous values because we cannot tell if the user changed it or not in the UI
// and we don't want to revert to the defaults every HMR
// arg1 is missing because it's not one of allowedGlobals
arg2: 2,
arg3: { complex: { object: ['type'] } },
arg4: 4,
@ -561,15 +552,6 @@ describe('preview.story_store', () => {
expect(store.getRawStory('a', '1').globals).toEqual({ foo: 'bar', baz: 'bing' });
});
it('updateGlobals sets session storage', () => {
const store = new StoryStore({ channel });
addStoryToStore(store, 'a', '1', () => 0);
(store2.session.set as any).mockClear();
store.updateGlobals({ foo: 'bar' });
expect(store2.session.set).toHaveBeenCalled();
});
it('is passed to the story in the context', () => {
const storyFn = jest.fn();
const store = new StoryStore({ channel });
@ -601,13 +583,27 @@ describe('preview.story_store', () => {
const store = new StoryStore({ channel: testChannel });
addStoryToStore(store, 'a', '1', () => 0);
store.addGlobalMetadata({
parameters: {
globalTypes: {
foo: { defaultValue: 'Foo' },
bar: { defaultValue: 'Bar' },
},
globals: { baz: 'Baz', qux: 'Qux' },
},
});
store.finishConfiguring();
store.updateGlobals({ foo: 'bar' });
expect(onGlobalsChangedChannel).toHaveBeenCalledWith({ globals: { foo: 'bar' } });
store.updateGlobals({ baz: 'bing' });
store.updateGlobals({ foo: 'FUD' });
expect(onGlobalsChangedChannel).toHaveBeenCalledWith({
globals: { foo: 'bar', baz: 'bing' },
globals: { foo: 'FUD', bar: 'Bar', baz: 'Baz', qux: 'Qux' },
initialGlobals: { foo: 'Foo', bar: 'Bar', baz: 'Baz', qux: 'Qux' },
});
store.updateGlobals({ baz: 'BING' });
expect(onGlobalsChangedChannel).toHaveBeenCalledWith({
globals: { foo: 'FUD', bar: 'Bar', baz: 'BING', qux: 'Qux' },
initialGlobals: { foo: 'Foo', bar: 'Bar', baz: 'Baz', qux: 'Qux' },
});
});

View File

@ -4,7 +4,6 @@ import dedent from 'ts-dedent';
import stable from 'stable';
import mapValues from 'lodash/mapValues';
import pick from 'lodash/pick';
import store from 'store2';
import deprecate from 'util-deprecate';
import { Channel } from '@storybook/channels';
@ -50,8 +49,6 @@ interface StoryOptions {
type KindMetadata = StoryMetadata & { order: number };
const STORAGE_KEY = '@storybook/preview/store';
function extractSanitizedKindNameFromStorySpecifier(storySpecifier: StorySpecifier): string {
if (typeof storySpecifier === 'string') {
return storySpecifier.split('--').shift();
@ -143,6 +140,10 @@ export default class StoryStore {
_globals: Args;
_initialGlobals: Args;
_defaultGlobals: Args;
_globalMetadata: StoryMetadata;
// Keyed on kind name
@ -162,10 +163,9 @@ export default class StoryStore {
constructor(params: { channel: Channel }) {
// Assume we are configuring until we hear otherwise
this._configuring = true;
// We store global args in session storage. Note that when we finish
// configuring below we will ensure we only use values here that make sense
this._globals = store.session.get(STORAGE_KEY)?.globals || {};
this._globals = {};
this._defaultGlobals = {};
this._initialGlobals = {};
this._globalMetadata = { parameters: {}, decorators: [], loaders: [] };
this._kinds = {};
this._stories = {};
@ -213,24 +213,19 @@ export default class StoryStore {
safePush(inferControls, this._argTypesEnhancers);
}
storeGlobals() {
// Store the global args on the session
store.session.set(STORAGE_KEY, { globals: this._globals });
}
finishConfiguring() {
this._configuring = false;
const { globals: initialGlobals = {}, globalTypes = {} } = this._globalMetadata.parameters;
const defaultGlobals: Args = Object.entries(
const { globals = {}, globalTypes = {} } = this._globalMetadata.parameters;
const allowedGlobals = new Set([...Object.keys(globals), ...Object.keys(globalTypes)]);
const defaultGlobals = Object.entries(
globalTypes as Record<string, { defaultValue: any }>
).reduce((acc, [arg, { defaultValue }]) => {
if (defaultValue) acc[arg] = defaultValue;
return acc;
}, {} as Args);
const allowedGlobals = new Set([...Object.keys(initialGlobals), ...Object.keys(globalTypes)]);
this._initialGlobals = { ...defaultGlobals, ...globals };
// To deal with HMR & persistence, we consider the previous value of global args, and:
// 1. Remove any keys that are not in the new parameter
@ -242,15 +237,27 @@ export default class StoryStore {
return acc;
},
{ ...defaultGlobals, ...initialGlobals }
{ ...this._initialGlobals }
);
this.storeGlobals();
// Set the current selection based on the current selection specifier, if selection is not yet set
const stories = this.sortedStories();
let foundStory;
if (this._selectionSpecifier && !this._selection) {
const { storySpecifier, viewMode, args: urlArgs } = this._selectionSpecifier;
const {
storySpecifier,
viewMode,
args: urlArgs,
globals: urlGlobals,
} = this._selectionSpecifier;
if (urlGlobals) {
const allowedUrlGlobals = Object.entries(urlGlobals).reduce((acc, [key, value]) => {
if (allowedGlobals.has(key)) acc[key] = value;
return acc;
}, {} as Args);
this._globals = combineParameters(this._globals, allowedUrlGlobals);
}
if (storySpecifier === '*') {
// '*' means select the first story. If there is none, we have no selection.
@ -614,8 +621,10 @@ export default class StoryStore {
updateGlobals(newGlobals: Args) {
this._globals = { ...this._globals, ...newGlobals };
this.storeGlobals();
this._channel.emit(Events.GLOBALS_UPDATED, { globals: this._globals });
this._channel.emit(Events.GLOBALS_UPDATED, {
globals: this._globals,
initialGlobals: this._initialGlobals,
});
}
updateStoryArgs(id: string, newArgs: Args) {

View File

@ -39,6 +39,7 @@ export interface StoreSelectionSpecifier {
viewMode: ViewMode;
singleStory?: boolean;
args?: Args;
globals?: Args;
}
export interface StoreSelection {

View File

@ -68,6 +68,7 @@ See https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#new-url-stru
export const getSelectionSpecifierFromPath: () => StoreSelectionSpecifier = () => {
const query = qs.parse(document.location.search, { ignoreQueryPrefix: true });
const args = typeof query.args === 'string' ? parseArgsParam(query.args) : undefined;
const globals = typeof query.globals === 'string' ? parseArgsParam(query.globals) : undefined;
let viewMode = getFirstString(query.viewMode) as ViewMode;
if (typeof viewMode !== 'string' || !viewMode.match(/docs|story/)) {
@ -79,7 +80,7 @@ export const getSelectionSpecifierFromPath: () => StoreSelectionSpecifier = () =
const storyId = path ? pathToId(path) : getFirstString(query.id);
if (storyId) {
return { storySpecifier: storyId, args, viewMode, singleStory };
return { storySpecifier: storyId, args, globals, viewMode, singleStory };
}
// Legacy URL format
@ -88,7 +89,7 @@ export const getSelectionSpecifierFromPath: () => StoreSelectionSpecifier = () =
if (kind && name) {
deprecatedLegacyQuery();
return { storySpecifier: { kind, name }, args, viewMode, singleStory };
return { storySpecifier: { kind, name }, args, globals, viewMode, singleStory };
}
return null;
};

View File

@ -5,9 +5,10 @@ const { plugins } = babelConfig();
const nodeModulesThatNeedToBeParsedBecauseTheyExposeES6 = [
'@storybook/node_logger',
'node_modules/acorn-jsx',
'node_modules/highlight.js',
'node_modules/json5',
'node_modules/semver',
'node_modules/highlight.js',
];
export const es6Transpiler: () => RuleSetRule = () => {
@ -17,7 +18,7 @@ export const es6Transpiler: () => RuleSetRule = () => {
return (
!!nodeModulesThatNeedToBeParsedBecauseTheyExposeES6.find((p) => input.includes(p)) ||
!!input.match(
/[\\/]node_modules[\\/](@storybook\/node-logger|are-you-es5|better-opn|boxen|chalk|commander|find-cache-dir|find-up|fs-extra|json5|node-fetch|pkg-dir|prettier|resolve-from|semver|highlight.js)/
/[\\/]node_modules[\\/](@storybook\/node-logger|are-you-es5|better-opn|boxen|chalk|commander|find-cache-dir|find-up|fs-extra|json5|node-fetch|pkg-dir|prettier|resolve-from|semver|highlight\.js|acorn-jsx)/
)
);
};

View File

@ -5,12 +5,10 @@ Object {
"entry": Array [
"ROOT/lib/core-client/dist/esm/globals/polyfills.js",
"ROOT/lib/core-client/dist/esm/manager/index.js",
"ROOT/addons/links/dist/esm/register.js",
"ROOT/addons/docs/dist/esm/register.js",
"ROOT/addons/controls/dist/esm/register.js",
"ROOT/addons/actions/dist/esm/register.js",
"ROOT/addons/backgrounds/dist/esm/register.js",
"ROOT/addons/viewport/dist/esm/register.js",
"ROOT/addons/toolbars/dist/esm/register.js",
"ROOT/examples/cra-ts-essentials/.storybook/generated-refs.js",
],

View File

@ -5,12 +5,10 @@ Object {
"entry": Array [
"ROOT/lib/core-client/dist/esm/globals/polyfills.js",
"ROOT/lib/core-client/dist/esm/manager/index.js",
"ROOT/addons/links/dist/esm/register.js",
"ROOT/addons/docs/dist/esm/register.js",
"ROOT/addons/controls/dist/esm/register.js",
"ROOT/addons/actions/dist/esm/register.js",
"ROOT/addons/backgrounds/dist/esm/register.js",
"ROOT/addons/viewport/dist/esm/register.js",
"ROOT/addons/toolbars/dist/esm/register.js",
"ROOT/examples/cra-ts-essentials/.storybook/generated-refs.js",
],

View File

@ -8,7 +8,6 @@ Object {
"ROOT/examples/cra-ts-essentials/.storybook/storybook-init-framework-entry.js",
"ROOT/addons/docs/dist/esm/frameworks/common/config.js-generated-other-entry.js",
"ROOT/addons/docs/dist/esm/frameworks/react/config.js-generated-other-entry.js",
"ROOT/addons/links/dist/esm/preset/addDecorator.js-generated-other-entry.js",
"ROOT/addons/actions/dist/esm/preset/addDecorator.js-generated-other-entry.js",
"ROOT/addons/actions/dist/esm/preset/addArgs.js-generated-other-entry.js",
"ROOT/addons/backgrounds/dist/esm/preset/addDecorator.js-generated-other-entry.js",
@ -42,109 +41,6 @@ Object {
},
],
},
Object {
"test": "/(stories|story)\\\\.mdx$/",
"use": Array [
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
"babelrc": false,
"cacheDirectory": "NODE_MODULES/.cache/storybook/babel",
"configFile": false,
"overrides": Array [
Object {
"plugins": Array [
Array [
"NODE_MODULES/babel-plugin-react-docgen/lib/index.js",
Object {
"DOC_GEN_COLLECTION_NAME": "STORYBOOK_REACT_CLASSES",
},
],
],
"test": "/\\\\.(mjs|jsx?)$/",
},
],
"plugins": Array [
Array [
"NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js",
Object {
"pragma": "React.createElement",
"pragmaFrag": "React.Fragment",
},
],
],
"presets": Array [],
},
},
Object {
"loader": "NODE_MODULES/@mdx-js/loader/index.js",
"options": Object {
"compilers": Array [
[Function],
],
"remarkPlugins": Array [
[Function],
[Function],
],
},
},
],
},
Object {
"exclude": "/(stories|story)\\\\.mdx$/",
"test": "/\\\\.mdx$/",
"use": Array [
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
"babelrc": false,
"cacheDirectory": "NODE_MODULES/.cache/storybook/babel",
"configFile": false,
"overrides": Array [
Object {
"plugins": Array [
Array [
"NODE_MODULES/babel-plugin-react-docgen/lib/index.js",
Object {
"DOC_GEN_COLLECTION_NAME": "STORYBOOK_REACT_CLASSES",
},
],
],
"test": "/\\\\.(mjs|jsx?)$/",
},
],
"plugins": Array [
Array [
"NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js",
Object {
"pragma": "React.createElement",
"pragmaFrag": "React.Fragment",
},
],
],
"presets": Array [],
},
},
Object {
"loader": "NODE_MODULES/@mdx-js/loader/index.js",
"options": Object {
"remarkPlugins": Array [
[Function],
[Function],
],
},
},
],
},
Object {
"enforce": "pre",
"loader": "ROOT/lib/source-loader/dist/cjs/index.js",
"options": Object {
"injectStoryParameters": true,
"inspectLocalDependencies": true,
},
"test": "/\\\\.(stories|story)\\\\.[tj]sx?$/",
},
Object {
"parser": Object {
"requireEnsure": false,
@ -392,6 +288,128 @@ Object {
},
],
},
Object {
"include": "NODE_MODULES\\\\/acorn-jsx/",
"test": "/\\\\.js$/",
"use": Array [
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
"presets": Array [
Array [
"NODE_MODULES/@babel/preset-env/lib/index.js",
Object {
"modules": "commonjs",
},
],
],
},
},
],
},
Object {
"test": "/(stories|story)\\\\.mdx$/",
"use": Array [
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
"babelrc": false,
"cacheDirectory": "NODE_MODULES/.cache/storybook/babel",
"configFile": false,
"overrides": Array [
Object {
"plugins": Array [
Array [
"NODE_MODULES/babel-plugin-react-docgen/lib/index.js",
Object {
"DOC_GEN_COLLECTION_NAME": "STORYBOOK_REACT_CLASSES",
},
],
],
"test": "/\\\\.(mjs|jsx?)$/",
},
],
"plugins": Array [
Array [
"NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js",
Object {
"pragma": "React.createElement",
"pragmaFrag": "React.Fragment",
},
],
],
"presets": Array [],
},
},
Object {
"loader": "NODE_MODULES/@mdx-js/loader/index.js",
"options": Object {
"compilers": Array [
[Function],
],
"remarkPlugins": Array [
[Function],
[Function],
],
},
},
],
},
Object {
"exclude": "/(stories|story)\\\\.mdx$/",
"test": "/\\\\.mdx$/",
"use": Array [
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
"babelrc": false,
"cacheDirectory": "NODE_MODULES/.cache/storybook/babel",
"configFile": false,
"overrides": Array [
Object {
"plugins": Array [
Array [
"NODE_MODULES/babel-plugin-react-docgen/lib/index.js",
Object {
"DOC_GEN_COLLECTION_NAME": "STORYBOOK_REACT_CLASSES",
},
],
],
"test": "/\\\\.(mjs|jsx?)$/",
},
],
"plugins": Array [
Array [
"NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js",
Object {
"pragma": "React.createElement",
"pragmaFrag": "React.Fragment",
},
],
],
"presets": Array [],
},
},
Object {
"loader": "NODE_MODULES/@mdx-js/loader/index.js",
"options": Object {
"remarkPlugins": Array [
[Function],
[Function],
],
},
},
],
},
Object {
"enforce": "pre",
"loader": "ROOT/lib/source-loader/dist/cjs/index.js",
"options": Object {
"injectStoryParameters": true,
"inspectLocalDependencies": true,
},
"test": "/\\\\.(stories|story)\\\\.[tj]sx?$/",
},
],
},
"plugins": Array [

View File

@ -8,7 +8,6 @@ Object {
"ROOT/examples/cra-ts-essentials/.storybook/storybook-init-framework-entry.js",
"ROOT/addons/docs/dist/esm/frameworks/common/config.js-generated-other-entry.js",
"ROOT/addons/docs/dist/esm/frameworks/react/config.js-generated-other-entry.js",
"ROOT/addons/links/dist/esm/preset/addDecorator.js-generated-other-entry.js",
"ROOT/addons/actions/dist/esm/preset/addDecorator.js-generated-other-entry.js",
"ROOT/addons/actions/dist/esm/preset/addArgs.js-generated-other-entry.js",
"ROOT/addons/backgrounds/dist/esm/preset/addDecorator.js-generated-other-entry.js",
@ -41,109 +40,6 @@ Object {
},
],
},
Object {
"test": "/(stories|story)\\\\.mdx$/",
"use": Array [
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
"babelrc": false,
"cacheDirectory": "NODE_MODULES/.cache/storybook/babel",
"configFile": false,
"overrides": Array [
Object {
"plugins": Array [
Array [
"NODE_MODULES/babel-plugin-react-docgen/lib/index.js",
Object {
"DOC_GEN_COLLECTION_NAME": "STORYBOOK_REACT_CLASSES",
},
],
],
"test": "/\\\\.(mjs|jsx?)$/",
},
],
"plugins": Array [
Array [
"NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js",
Object {
"pragma": "React.createElement",
"pragmaFrag": "React.Fragment",
},
],
],
"presets": Array [],
},
},
Object {
"loader": "NODE_MODULES/@mdx-js/loader/index.js",
"options": Object {
"compilers": Array [
[Function],
],
"remarkPlugins": Array [
[Function],
[Function],
],
},
},
],
},
Object {
"exclude": "/(stories|story)\\\\.mdx$/",
"test": "/\\\\.mdx$/",
"use": Array [
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
"babelrc": false,
"cacheDirectory": "NODE_MODULES/.cache/storybook/babel",
"configFile": false,
"overrides": Array [
Object {
"plugins": Array [
Array [
"NODE_MODULES/babel-plugin-react-docgen/lib/index.js",
Object {
"DOC_GEN_COLLECTION_NAME": "STORYBOOK_REACT_CLASSES",
},
],
],
"test": "/\\\\.(mjs|jsx?)$/",
},
],
"plugins": Array [
Array [
"NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js",
Object {
"pragma": "React.createElement",
"pragmaFrag": "React.Fragment",
},
],
],
"presets": Array [],
},
},
Object {
"loader": "NODE_MODULES/@mdx-js/loader/index.js",
"options": Object {
"remarkPlugins": Array [
[Function],
[Function],
],
},
},
],
},
Object {
"enforce": "pre",
"loader": "ROOT/lib/source-loader/dist/cjs/index.js",
"options": Object {
"injectStoryParameters": true,
"inspectLocalDependencies": true,
},
"test": "/\\\\.(stories|story)\\\\.[tj]sx?$/",
},
Object {
"parser": Object {
"requireEnsure": false,
@ -410,6 +306,128 @@ Object {
},
],
},
Object {
"include": "NODE_MODULES\\\\/acorn-jsx/",
"test": "/\\\\.js$/",
"use": Array [
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
"presets": Array [
Array [
"NODE_MODULES/@babel/preset-env/lib/index.js",
Object {
"modules": "commonjs",
},
],
],
},
},
],
},
Object {
"test": "/(stories|story)\\\\.mdx$/",
"use": Array [
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
"babelrc": false,
"cacheDirectory": "NODE_MODULES/.cache/storybook/babel",
"configFile": false,
"overrides": Array [
Object {
"plugins": Array [
Array [
"NODE_MODULES/babel-plugin-react-docgen/lib/index.js",
Object {
"DOC_GEN_COLLECTION_NAME": "STORYBOOK_REACT_CLASSES",
},
],
],
"test": "/\\\\.(mjs|jsx?)$/",
},
],
"plugins": Array [
Array [
"NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js",
Object {
"pragma": "React.createElement",
"pragmaFrag": "React.Fragment",
},
],
],
"presets": Array [],
},
},
Object {
"loader": "NODE_MODULES/@mdx-js/loader/index.js",
"options": Object {
"compilers": Array [
[Function],
],
"remarkPlugins": Array [
[Function],
[Function],
],
},
},
],
},
Object {
"exclude": "/(stories|story)\\\\.mdx$/",
"test": "/\\\\.mdx$/",
"use": Array [
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
"babelrc": false,
"cacheDirectory": "NODE_MODULES/.cache/storybook/babel",
"configFile": false,
"overrides": Array [
Object {
"plugins": Array [
Array [
"NODE_MODULES/babel-plugin-react-docgen/lib/index.js",
Object {
"DOC_GEN_COLLECTION_NAME": "STORYBOOK_REACT_CLASSES",
},
],
],
"test": "/\\\\.(mjs|jsx?)$/",
},
],
"plugins": Array [
Array [
"NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js",
Object {
"pragma": "React.createElement",
"pragmaFrag": "React.Fragment",
},
],
],
"presets": Array [],
},
},
Object {
"loader": "NODE_MODULES/@mdx-js/loader/index.js",
"options": Object {
"remarkPlugins": Array [
[Function],
[Function],
],
},
},
],
},
Object {
"enforce": "pre",
"loader": "ROOT/lib/source-loader/dist/cjs/index.js",
"options": Object {
"injectStoryParameters": true,
"inspectLocalDependencies": true,
},
"test": "/\\\\.(stories|story)\\\\.[tj]sx?$/",
},
],
},
"plugins": Array [