diff --git a/docs/api/addons-api.md b/docs/api/addons-api.md index 7db6a5636f02..26b927881e67 100644 --- a/docs/api/addons-api.md +++ b/docs/api/addons-api.md @@ -6,9 +6,15 @@ title: 'Addons API' This is the core addon API. This is how to get the addon API: -```jsx -import { addons } from '@storybook/addons'; -``` + + + + + ### addons.getChannel() @@ -21,14 +27,15 @@ It has a NodeJS [EventEmitter](https://nodejs.org/api/events.html) compatible AP This method allows you to register an addon and get the storybook API. You can do this only in the Manager App. See how we can use this: -```js -// my-addon/register.js + -import { addons } from '@storybook/addons'; + -// Register the addon with a unique name. -addons.register('my-organisation/my-addon', (api) => {}); -``` + Now you'll get an instance to our StorybookAPI. See the [api docs](#storybook-api) for Storybook API regarding using that. @@ -37,25 +44,15 @@ Now you'll get an instance to our StorybookAPI. See the [api docs](#storybook-ap This method allows you to add a panel to Storybook. (Storybook's Action Logger is a panel). You can do this only in the Manager App. See how you can use this method: -```js -// my-addon/register.js + -import { addons, types } from '@storybook/addons'; -import { AddonPanel } from '@storybook/components'; + -const MyPanel = () =>
This is a panel.
; - -// give a unique name for the panel -addons.add('my-organisation/my-addon/panel', { - title: 'My Addon', - type: types.PANEL, - render: ({ active, key }) => ( - - - - ), -}); -``` + The render function is called with `active` and `key`. @@ -67,23 +64,15 @@ As you can see, you can set any React Component as the panel. Currently, it's on The `makeDecorator` API can be used to create decorators in the style of the official addons. Use it like so: -```js -// my-addon/register.js - -import { makeDecorator } from '@storybook/addons'; + -export makeDecorator({ - name: 'withSomething', - parameterName: 'something', - wrapper: (storyFn, context, { parameters }) => { - // Do something with `parameters`, which are set via { something: ... } + - // Note you may alter the story output if you like, although generally that's - // not advised - return storyFn(context); - } -}) -``` + The options to `makeDecorator` are: @@ -107,13 +96,15 @@ Writing addons can be simplified a lot by using these Storybook hooks: ### useStorybookState -```js -export const Panel = () => { - const state = useStorybookState(); + - return
do something with storybook's state
; -}; -``` + + + Allows full access to the entire storybook state. Your component will re-render whenever the storybook state changes. @@ -122,15 +113,15 @@ If you use this, remember your component wil be re-rendered a lot, and you may n ### useStorybookApi -```js -// my-addon/register.js + -export const Panel = () => { - const state = useStorybookApi(); + - return
do something with storybook's api
; -}; -``` + Allows full access to the storybook API. @@ -138,22 +129,15 @@ Detail on the storybook api are further down. ### useChannel -```js -// my-addon/register.js + -import { STORY_CHANGED } from '@storybook/core-events'; -export const Panel = () => { - const emit = useChannel({ - STORY_CHANGED: (...args) => console.log(...args), - }); + - return ( - - ); -}; -``` + Allows for both setting subscriptions to events and getting the emitter for emitting custom event unto the channel. @@ -161,20 +145,15 @@ The messages can be listened for on both the iframe and the manager side. ### useAddonState -```js -// my-addon/register.js - -export const Panel = () => { - const [state, setState] = useAddonState('my/addon-id', 'initial state'); + - return ; -}; -export const Tool = () => { - const [state, setState] = useAddonState('my/addon-id', 'initial state'); + - return ; -}; -``` + Extremely useful for addons that need to persist some state. @@ -186,20 +165,15 @@ With this hook they can all get access to the same bit of state which is persist ### useParameter -```js -// my-addon/register.js + -export const Panel = () => { - const value = useParameter('parameter-key', 'default value'); + - return ( -
- for the currently selected story, the parameter for "parameter-key" is: - {value} -
- ); -}; -``` + This hook gets you the current story's parameter. @@ -222,51 +196,55 @@ With this method, you can select a story via an API. This method accepts two par Let's say you've got a story like this: -```js -// Heading.story.js -export default { - title: 'heading', -}; + -export const withText = () =>

Hello world

; -``` + + + This is how you can select the above story: -```js -// my-addon/register.js + + + -addons.register('my-organisation/my-addon', (api) => { - api.selectStory('heading', 'withText'); -}); -``` + ### api.selectInCurrentKind() Same as `selectStory`, but accepts a story inside current kind as the only parameter: -```js -// my-addon/register.js + -addons.register('my-organisation/my-addon', (api) => { - api.selectInCurrentKind('withText'); -}); -``` + + + ### api.setQueryParams() This method allows you to set query string parameters. You can use that as temporary storage for addons. Here's how you set query params. -```js -// my-addon/register.js + + + -addons.register('my-organisation/my-addon', (api) => { - api.setQueryParams({ - abc: 'this is abc', - bbc: 'this is bbc', - }); -}); -``` +
@@ -274,49 +252,54 @@ If you need to remove a query param, use `null` for that. For an example, let's
-```js -// my-addon/register.js + -addons.register('my-organisation/my-addon', (api) => { - api.setQueryParams({ - bbc: null, - }); -}); -``` + + + ### api.getQueryParam() This method allows you to get a query param set by above API `setQueryParams`. For example, let's say we need to get the bbc query param. Then this how we do it: -```jsx -addons.register('my-organisation/my-addon', (api) => { - api.getQueryParam('bbc'); -}); -``` + + + + + ### api.getUrlState(overrideParams) This method allows you to get application url state with some changed params. For example, if you want to get a link to a particular story: -```js -// my-addon/register.js + + + -addons.register('my-organisation/my-addon', (api) => { - const href = api.getUrlState({ - selectedKind: 'kind', - selectedStory: 'story', - }).url; -}); -``` + ### api.on(eventName, fn) This method allows you to register a handler function which will be called whenever the user navigates between stories. -```js -// my-addon/register.js + + + -addons.register('my-organisation/my-addon', (api) => { - api.on('some-event', (eventData) => console.log(eventData)); -}); -``` + diff --git a/docs/api/addons.md b/docs/api/addons.md index 491c2dae0d50..9a9f4d4bb6d6 100644 --- a/docs/api/addons.md +++ b/docs/api/addons.md @@ -35,21 +35,15 @@ Let’s write a simple addon for Storybook which: Let’s start by writing a story for our addon that exposes a custom parameter. The idea is that our addon will show this parameter in the addon panel. -```js -// Button.story.js - -import React from 'react'; -import Button from './Button'; -export default { - title: 'Button', - parameters: { - myAddon: { - data: 'this data is passed to the addon', - }, - }, -}; -export const Basic = () => ; -``` + + + + + Because we added the story at the component level, the `myAddon` parameter is associated with all stories defined in the file. @@ -57,29 +51,15 @@ Because we added the story at the component level, the `myAddon` parameter is as Now let’s add a panel to Storybook in a file called `register.js`, which is the entry point for addons to register themselves. -```js -// .storybook/register.js - -import React from 'react'; -import { addons, types } from '@storybook/addons'; -import { AddonPanel } from '@storybook/components'; - -const ADDON_ID = 'myaddon'; -const PANEL_ID = `${ADDON_ID}/panel`; - -const MyPanel = () =>
MyAddon
; -addons.register(ADDON_ID, (api) => { - addons.add(PANEL_ID, { - type: types.PANEL, - title: 'My Addon', - render: ({ active, key }) => ( - - - - ), - }); -}); -``` + + + + + This is boilerplate code for any addon that adds a panel to Storybook, and there’s really not much going on here. In this case, we’re just adding a static div that renders when the panel is selected in Storybook’s UI. @@ -87,17 +67,15 @@ This is boilerplate code for any addon that adds a panel to Storybook, and there Next, let’s replace the `MyPanel` component from above to show the parameter. -```js -// .storybook/register.js + -import { useParameter } from '@storybook/api'; -const PARAM_KEY = 'myAddon'; -const MyPanel = () => { - const value = useParameter(PARAM_KEY, null); - const item = value ? value.data : ''; - return
{item}
; -}; -``` + + + The new version is made smarter by `useParameter`, which is a [React hook](https://reactjs.org/docs/hooks-intro.html) that updates the parameter value and re-renders the panel every time the story changes. @@ -109,13 +87,15 @@ Finally, let’s hook it all up. Addons are typically published as standalone pa Update your [`.storybook/main.js`](../configure/overview.md#configure-story-rendering): -```js -// .storybook/main.js + -module.exports = { - addons: ['path/to/register.js'], -}; -``` + + + The path can be an absolute location on your file system, or a path relative to your `.storybook` directory (e.g. `./my-addon/register.js` if you defined the addon inside your `.storybook` folder). @@ -158,32 +138,27 @@ It’s possible to disable the addon panel for a particular story. To make that possible, you need to pass the `paramKey` element when you register the panel: -```js -// .storybook/register.js - -addons.register(ADDON_ID, () => { - addons.add(PANEL_ID, { - type: types.PANEL, - title: 'My Addon', - render: () =>
Addon tab content
, - paramKey: 'myAddon', // this element - }); -}); -``` + + + + + Then when adding a story, you can then pass a disabled parameter. -```js -// Button.story.js + -import React from 'react'; -export default { - title: 'Button', - parameters: { - myAddon: { disable: true }, - }, -}; -``` + + + ### Styling your addon @@ -241,25 +216,15 @@ Common uses for presets include: Here’s an example of typical preset file: -```js -//preset.js -export function config(entry = []) { - return [...entry, require.resolve('./defaultParameters')]; -} + -export function managerEntries(entries) { - return [...entries, require.resolve('./register')]; -} + -export const parameters = { - backgrounds: { - values: [ - { name: 'light', value: '#F8F8F8' }, - { name: 'dark', value: '#333333' }, - ], - }, -}; -``` + For more information on presets, see the [presets docs](./presets.md). diff --git a/docs/api/argtypes.md b/docs/api/argtypes.md index 7b6a019c4a9e..d5f2d457e412 100644 --- a/docs/api/argtypes.md +++ b/docs/api/argtypes.md @@ -36,23 +36,15 @@ To do so, Storybook uses various static analysis tools depending on your framewo The format of the generated argType will look something like: -```js -const argTypes = { - label: { - name: 'label', - type: { name: 'string', required: false }, - defaultValue: 'Hello', - description: 'demo description', - table: { - type: { summary: 'string' }, - defaultValue: { summary: 'Hello' }, - } - control: { - type: 'text' - } - } -} -``` + + + + + In this ArgTypes data structure, name, type, defaultValue, and description are standard fields in all ArgTypes (analogous to PropTypes in React). The table and control fields are addon-specific annotations. So, for example, the table annotation provides extra information to customize how label gets rendered, and the control annotation provides extra information for the control for editing the property. @@ -69,43 +61,27 @@ In this ArgTypes data structure, name, type, defaultValue, and description are s If you want more control over the props table or any other aspect of using argTypes, you can overwrite the generated argTypes for you component on a per-arg basis. For instance, with the above inferred argTypes and the following default export: -```js -export default { - title: 'Button', - component: Button, - argTypes: { - label: { - description: 'overwritten description', - table: { - type: { summary: 'something short', detail: 'something really really long' }, - }, - control: { - type: null, - }, - }, - }, -}; -``` + + + + + These values--description, table.type, and controls.type--get merged over the defaults that are extracted by Storybook. The final merged values would be: -```js -const argTypes = { - label: { - name: 'label', - type: { name: 'string', required: false }, - defaultValue: 'Hello', - description: 'overwritten description', - table: { - type: { summary: 'something short', detail: 'something really really long' }, - defaultValue: { summary: 'Hello' }, - } - control: { - type: null - } - } -} -``` + + + + + In particular, this would render a row with a modified description, a type display with a dropdown that shows the detail, and no control. @@ -113,9 +89,12 @@ In particular, this would render a row with a modified description, a type displ If you want to access the argTypes of the current component inside an addon, you can use the `useArgTypes` hook from the `@storybook/api` package: -```js -import { useArgTypes } from '@storybook/api'; + + + -// inside your panel -const { argTypes } = useArgTypes(); -``` + diff --git a/docs/api/csf.md b/docs/api/csf.md index 4aca2a1afeaf..9878501e7b40 100644 --- a/docs/api/csf.md +++ b/docs/api/csf.md @@ -20,17 +20,15 @@ The default export defines metadata about your component, including the `compone The `component` field is optional (but encouraged!), and is used by addons for automatic prop table generation and display of other component metadata. `title` should be unique, i.e. not re-used across files. -```js -// MyComponent.story.js -import MyComponent from './MyComponent'; - -export default { - title: 'Path/To/MyComponent', - component: MyComponent, - decorators: [ ... ], - parameters: { ... } -} -``` + + + + + For more examples, see [writing stories](../writing-stories/introduction.md). @@ -38,18 +36,15 @@ For more examples, see [writing stories](../writing-stories/introduction.md). With CSF, every named export in the file represents a story function by default. -```js -// MyComponent.story.js -import MyComponent from './MyComponent'; + -export default { - title: 'Path/To/MyComponent', - component: MyComponent, -}; + -export const Basic = () => ; -export const WithProp = () => ; -``` + The exported identifiers will be converted to "start case" using Lodash's [startCase](https://lodash.com/docs/#startCase) function. For example: @@ -67,15 +62,15 @@ Story functions can be annotated with a few different fields to define story-lev The `storyName` is useful if you want to use names with special characters, names that correspond to restricted keywords in Javascript, or names that collide with other variables in the file. If it's not specified, the export name will be used instead. -```jsx -// MyComponent.story.js + -export const Simple = () => ; + -Simple.storyName = 'So simple!'; -Simple.decorators = [ ... ]; -Simple.parameters = { ... }; -``` + ## Args story inputs @@ -83,40 +78,51 @@ Starting in SB 6.0, stories accept named inputs called Args. Args are dynamic da Consider Storybook’s ["Button" example](../writing-stories/introduction.md#defining-stories) of a text button that logs its click events: -```js -// Button.story.js + -import { action } from '@storybook/addon-actions'; -import { Button } from './Button'; + -export default { - title: 'Button', - component: Button -}; -export const Text = () => ; +}; +export const Tool = () => { + const [state, setState] = useAddonState('my/addon-id', 'initial state'); + + return ; +}; +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-addons-api-useapi.js.mdx b/docs/snippets/common/storybook-addons-api-useapi.js.mdx new file mode 100644 index 000000000000..da49b6418916 --- /dev/null +++ b/docs/snippets/common/storybook-addons-api-useapi.js.mdx @@ -0,0 +1,9 @@ +```js +// .storybook/my-addon/register.js + +export const Panel = () => { + const state = useStorybookApi(); + + return
do something with storybook's api
; +}; +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-addons-api-usechannel.js.mdx b/docs/snippets/common/storybook-addons-api-usechannel.js.mdx new file mode 100644 index 000000000000..b08320cdbe8b --- /dev/null +++ b/docs/snippets/common/storybook-addons-api-usechannel.js.mdx @@ -0,0 +1,16 @@ +```js +// .storybook/my-addon/register.js + +import { STORY_CHANGED } from '@storybook/core-events'; +export const Panel = () => { + const emit = useChannel({ + STORY_CHANGED: (...args) => console.log(...args), + }); + + return ( + + ); +}; +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-addons-api-useparameter.js.mdx b/docs/snippets/common/storybook-addons-api-useparameter.js.mdx new file mode 100644 index 000000000000..20aaf02af3f5 --- /dev/null +++ b/docs/snippets/common/storybook-addons-api-useparameter.js.mdx @@ -0,0 +1,14 @@ +```js +// .storybook/my-addon/register.js + +export const Panel = () => { + const value = useParameter('parameter-key', 'default value'); + + return ( +
+ for the currently selected story, the parameter for "parameter-key" is: + {value} +
+ ); +}; +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-addons-api-usestorybookstate.js.mdx b/docs/snippets/common/storybook-addons-api-usestorybookstate.js.mdx new file mode 100644 index 000000000000..88d84c9e02c1 --- /dev/null +++ b/docs/snippets/common/storybook-addons-api-usestorybookstate.js.mdx @@ -0,0 +1,9 @@ +```js +// .storybook/my-addon/register.js + +export const Panel = () => { + const state = useStorybookState(); + + return
do something with storybook's state
; +}; +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-argtypes-with-addon.js.mdx b/docs/snippets/common/storybook-argtypes-with-addon.js.mdx new file mode 100644 index 000000000000..30ba78220b20 --- /dev/null +++ b/docs/snippets/common/storybook-argtypes-with-addon.js.mdx @@ -0,0 +1,8 @@ +```js +// storybook/my-addon/register.js + +import { useArgTypes } from '@storybook/api'; + +// inside your panel +const { argTypes } = useArgTypes(); +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-babel-configuration-example.ts.mdx b/docs/snippets/common/storybook-babel-configuration-example.ts.mdx new file mode 100644 index 000000000000..8ce82c58d99f --- /dev/null +++ b/docs/snippets/common/storybook-babel-configuration-example.ts.mdx @@ -0,0 +1,13 @@ +```ts +// mithril/src/server/framework-preset-mithril.ts + +export function babelDefault(config: TransformOptions) { + return { + ...config, + plugins: [ + ...config.plugins, + require.resolve('@babel/plugin-transform-react-jsx') + ], + }; +} +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-client-globals-example-file.ts.mdx b/docs/snippets/common/storybook-client-globals-example-file.ts.mdx new file mode 100644 index 000000000000..96bd5bca622d --- /dev/null +++ b/docs/snippets/common/storybook-client-globals-example-file.ts.mdx @@ -0,0 +1,6 @@ +```ts +// vue/src/client/preview/globals.ts + +import { window } from 'global'; +window.STORYBOOK_ENV = 'vue'; +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-client-preview.ts.mdx b/docs/snippets/common/storybook-client-preview.ts.mdx new file mode 100644 index 000000000000..43b96cd58b55 --- /dev/null +++ b/docs/snippets/common/storybook-client-preview.ts.mdx @@ -0,0 +1,10 @@ +```ts +// your-framework/src/client/preview/index.ts + +import { start } from '@storybook/core/client'; +import './globals'; +import render from './render'; +const api = start(render); + +// the boilerplate code +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-customize-argtypes.js.mdx b/docs/snippets/common/storybook-customize-argtypes.js.mdx new file mode 100644 index 000000000000..fb5535b9e143 --- /dev/null +++ b/docs/snippets/common/storybook-customize-argtypes.js.mdx @@ -0,0 +1,22 @@ +```js +// Button.stories.js + +export default { + title: 'Button', + component: Button, + argTypes: { + label: { + description: 'overwritten description', + table: { + type: { + summary: 'something short', + detail: 'something really really long' + }, + }, + control: { + type: null, + }, + }, + }, +}; +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-framework-render-function.js.mdx b/docs/snippets/common/storybook-framework-render-function.js.mdx new file mode 100644 index 000000000000..3c5699a52202 --- /dev/null +++ b/docs/snippets/common/storybook-framework-render-function.js.mdx @@ -0,0 +1,9 @@ +```js +const rootElement = document.getElementById('root'); + +export default function renderMain({ storyFn }: RenderMainArgs) { + const storyObj = storyFn(); + const html = fn(storyObj); + rootElement.innerHTML = html; +} +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-generated-argtypes.js.mdx b/docs/snippets/common/storybook-generated-argtypes.js.mdx new file mode 100644 index 000000000000..de4a45b8a2a0 --- /dev/null +++ b/docs/snippets/common/storybook-generated-argtypes.js.mdx @@ -0,0 +1,17 @@ +```js +const argTypes = { + label: { + name: 'label', + type: { name: 'string', required: false }, + defaultValue: 'Hello', + description: 'demo description', + table: { + type: { summary: 'string' }, + defaultValue: { summary: 'Hello' }, + } + control: { + type: 'text' + } + } +} +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-main-advanced-config-example.js.mdx b/docs/snippets/common/storybook-main-advanced-config-example.js.mdx new file mode 100644 index 000000000000..abb0574521d1 --- /dev/null +++ b/docs/snippets/common/storybook-main-advanced-config-example.js.mdx @@ -0,0 +1,21 @@ +```js +//.storybook/main.js + +module.exports = { + managerWebpack: async (config, options) => { + // update config here + return config; + }, + managerBabel: async (config, options) => { + // update config here + return config; + }, + webpackFinal: async (config, options) => { + // change webpack config + return config; + }, + babel: async (config, options) => { + return config; + }, +}; +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-main-import-preset-config.js.mdx b/docs/snippets/common/storybook-main-import-preset-config.js.mdx new file mode 100644 index 000000000000..3b9634cc4307 --- /dev/null +++ b/docs/snippets/common/storybook-main-import-preset-config.js.mdx @@ -0,0 +1,9 @@ +```js +//.storybook/main.js + +const path = require('path'); + +module.exports = { + addons: [path.resolve('./.storybook/my-preset')], +}; +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-main-preset-config.js.mdx b/docs/snippets/common/storybook-main-preset-config.js.mdx new file mode 100644 index 000000000000..2f664ed34969 --- /dev/null +++ b/docs/snippets/common/storybook-main-preset-config.js.mdx @@ -0,0 +1,7 @@ +```js +// .storybook/main.js + +module.exports = { + addons: ['@storybook/preset-typescript'], +}; +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-main-register-addon.js.mdx b/docs/snippets/common/storybook-main-register-addon.js.mdx new file mode 100644 index 000000000000..0f587017ee85 --- /dev/null +++ b/docs/snippets/common/storybook-main-register-addon.js.mdx @@ -0,0 +1,7 @@ +```js +// .storybook/main.js + +module.exports = { + addons: ['path/to/register.js'], +}; +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-main-register-presets-managerentry.js.mdx b/docs/snippets/common/storybook-main-register-presets-managerentry.js.mdx new file mode 100644 index 000000000000..7c9d1b254dff --- /dev/null +++ b/docs/snippets/common/storybook-main-register-presets-managerentry.js.mdx @@ -0,0 +1,10 @@ +```js +// storybook/main.js + +module.exports = { + addons: [ + '@storybook/addon-storysource/register', // a managerEntry + '@storybook/addon-docs/preset', // a preset + ], +}; +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-main-register-storysource-example.js.mdx b/docs/snippets/common/storybook-main-register-storysource-example.js.mdx new file mode 100644 index 000000000000..f74a788eead5 --- /dev/null +++ b/docs/snippets/common/storybook-main-register-storysource-example.js.mdx @@ -0,0 +1,7 @@ +```js +// .storybook/main.js + +module.exports = { + addons: ['@storybook/addon-storysource'], +}; +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-main-use-manager-entries.js.mdx b/docs/snippets/common/storybook-main-use-manager-entries.js.mdx new file mode 100644 index 000000000000..5bbaa228a809 --- /dev/null +++ b/docs/snippets/common/storybook-main-use-manager-entries.js.mdx @@ -0,0 +1,7 @@ +```js +// .storybook/main.js + +module.exports = { + managerEntries: ['@storybook/addon-storysource/register'], +}; +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-main-webpack-preset-config.js.mdx b/docs/snippets/common/storybook-main-webpack-preset-config.js.mdx new file mode 100644 index 000000000000..842fbb99283a --- /dev/null +++ b/docs/snippets/common/storybook-main-webpack-preset-config.js.mdx @@ -0,0 +1,23 @@ +```js +//.storybook/main.js + +export async function webpack(baseConfig, options) { + //Modify or replace config. + //Mutating the original reference object can cause unexpected bugs, + //so in this example we replace. + const { module = {} } = baseConfig; + + return { + ...baseConfig, + module: { + ...module, + rules: [ + ...(module.rules || []), + { + /* some new loader */ + }, + ], + }, + }; +} +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-main-webpackfinal-example.js.mdx b/docs/snippets/common/storybook-main-webpackfinal-example.js.mdx new file mode 100644 index 000000000000..35ebd7e6c52b --- /dev/null +++ b/docs/snippets/common/storybook-main-webpackfinal-example.js.mdx @@ -0,0 +1,13 @@ +```js +//.storybook/main.js + +export function webpackFinal(config, { configDir }) { + if (!isReactScriptsInstalled()) { + logger.info('=> Using base config because react-scripts is not installed.'); + return config; + } + + logger.info('=> Loading create-react-app config.'); + return applyCRAWebpackConfig(config, configDir); +} +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-merged-argtypes.js.mdx b/docs/snippets/common/storybook-merged-argtypes.js.mdx new file mode 100644 index 000000000000..7eafe85038a6 --- /dev/null +++ b/docs/snippets/common/storybook-merged-argtypes.js.mdx @@ -0,0 +1,20 @@ +```js +const argTypes = { + label: { + name: 'label', + type: { name: 'string', required: false }, + defaultValue: 'Hello', + description: 'overwritten description', + table: { + type: { + summary: 'something short', + detail: 'something really really long' + }, + defaultValue: { summary: 'Hello' }, + } + control: { + type: null + } + } +} +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-preset-configuration.js.mdx b/docs/snippets/common/storybook-preset-configuration.js.mdx new file mode 100644 index 000000000000..95a0e70a0fc8 --- /dev/null +++ b/docs/snippets/common/storybook-preset-configuration.js.mdx @@ -0,0 +1,18 @@ +```js +// .storybook/typescript-preset.js + +const path = require('path'); +module.exports = { + addons: [ + { + name: '@storybook/preset-typescript', + options: { + tsLoaderOptions: { + configFile: path.resolve(__dirname, '../tsconfig.json'), + }, + include: [path.resolve(__dirname)], + }, + }, + ], +}; +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-preset-full-config-object.js.mdx b/docs/snippets/common/storybook-preset-full-config-object.js.mdx new file mode 100644 index 000000000000..743778aa7110 --- /dev/null +++ b/docs/snippets/common/storybook-preset-full-config-object.js.mdx @@ -0,0 +1,20 @@ +```js +// .storybook/my-preset.js + +module.exports = { + managerWebpack: async (config, options) => { + // update config here + return config; + }, + managerBabel: async (config, options) => { + // update config here + return config; + }, + webpackFinal: async (config, options) => { + return config; + }, + babel: async (config, options) => { + return config; + }, +}; +```` \ No newline at end of file diff --git a/docs/snippets/common/storybook-preview-decorator-params-example.js.mdx b/docs/snippets/common/storybook-preview-decorator-params-example.js.mdx new file mode 100644 index 000000000000..dd311391623d --- /dev/null +++ b/docs/snippets/common/storybook-preview-decorator-params-example.js.mdx @@ -0,0 +1,7 @@ +```js +// .storybook/preview.js +import { addDecorator, addParameters } from '@storybook/react'; + +addDecorator(...); +addParameters({ ... }); +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-server-options.ts.mdx b/docs/snippets/common/storybook-server-options.ts.mdx new file mode 100644 index 000000000000..7fff894c8b4f --- /dev/null +++ b/docs/snippets/common/storybook-server-options.ts.mdx @@ -0,0 +1,11 @@ +```ts +// vue/src/server/options.ts + +const packageJson = require('../../package.json'); + +export default { + packageJson, + framework: 'vue', + frameworkPresets: [require.resolve('./framework-preset-vue.js')], +}; +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-start-dev-server.ts.mdx b/docs/snippets/common/storybook-start-dev-server.ts.mdx new file mode 100644 index 000000000000..c5d23259897f --- /dev/null +++ b/docs/snippets/common/storybook-start-dev-server.ts.mdx @@ -0,0 +1,8 @@ +```ts +// your-framework/src/server/index.ts + +import { buildDev } from '@storybook/core/server'; +import options from './options'; + +buildDev(options); +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-storysource-manager-entries.js.mdx b/docs/snippets/common/storybook-storysource-manager-entries.js.mdx new file mode 100644 index 000000000000..5e792ec1d298 --- /dev/null +++ b/docs/snippets/common/storybook-storysource-manager-entries.js.mdx @@ -0,0 +1,7 @@ +```js +// storysource/preset.js + +export function managerEntries(entry = []) { + return [...entry, require.resolve('@storybook/addon-storysource/register')]; +} +``` \ No newline at end of file diff --git a/docs/snippets/common/storybook-test-with-storyname.js.mdx b/docs/snippets/common/storybook-test-with-storyname.js.mdx new file mode 100644 index 000000000000..d86c32b9ab4e --- /dev/null +++ b/docs/snippets/common/storybook-test-with-storyname.js.mdx @@ -0,0 +1,17 @@ +```js +// MyComponent-test.js + +it('should format CSF exports with sensible defaults', () => { + const testCases = { + name: 'Name', + someName: 'Some Name', + someNAME: 'Some NAME', + some_custom_NAME: 'Some Custom NAME', + someName1234: 'Some Name 1234', + someName1_2_3_4: 'Some Name 1 2 3 4', + }; + Object.entries(testCases).forEach(([key, val]) => { + expect(storyNameFromExport(key)).toBe(val); + }); +}); +``` \ No newline at end of file diff --git a/docs/snippets/react/button-story-click-handler-args.js.mdx b/docs/snippets/react/button-story-click-handler-args.js.mdx new file mode 100644 index 000000000000..e9ceab29ccca --- /dev/null +++ b/docs/snippets/react/button-story-click-handler-args.js.mdx @@ -0,0 +1,9 @@ +```js +// Button.stories.js + +export const Text = ({ label, onClick }) => ; +``` \ No newline at end of file diff --git a/docs/snippets/react/button-story-with-sample.js.mdx b/docs/snippets/react/button-story-with-sample.js.mdx new file mode 100644 index 000000000000..39d8ccee8f86 --- /dev/null +++ b/docs/snippets/react/button-story-with-sample.js.mdx @@ -0,0 +1,11 @@ +```js +// Button.stories.js + +import { Button } from './Button'; +export default { + title: 'Button', + component: Button, +}; + +export const Sample = () =>