diff --git a/addons/controls/README.md b/addons/controls/README.md
index d394925f99c3..169d5cbeb01f 100644
--- a/addons/controls/README.md
+++ b/addons/controls/README.md
@@ -279,17 +279,22 @@ export const VeryLongLabel = (args) => ;
VeryLongLabel.args = { label: 'this is a very long string', background: '#ff0' };
```
-This works, but it repeats code. What we want is to reuse the `Basic` story, but with a different initial state. In Storybook we do this idiomatically for Args stories:
+This works, but it repeats code. What we want is to reuse the `Basic` story, but with a different initial state. In Storybook we do this idiomatically for Args stories by refactoring the first story into a reusable story function and then `.bind`ing it to create a duplicate object on which to hang `args`:
```jsx
-export const VeryLongLabel = Basic.bind({});
+const ButtonStory = (args) => ;
+
+export const Basic = ButtonStory.bind({});
+Basic.args = { label: 'hello', background: '#ff0' };
+
+export const VeryLongLabel = ButtonStory.bind({});
VeryLongLabel.args = { label: 'this is a very long string', background: '#ff0' };
```
We can even reuse initial args from other stories:
```jsx
-export const VeryLongLabel = Basic.bind();
+export const VeryLongLabel = ButtonStory.bind({});
VeryLongLabel.args = { ...Basic.args, label: 'this is a very long string' };
```
diff --git a/addons/docs/src/mdx/__testfixtures__/story-args.mdx b/addons/docs/src/mdx/__testfixtures__/story-args.mdx
index 57a20578d9ec..e88c74c2b67f 100644
--- a/addons/docs/src/mdx/__testfixtures__/story-args.mdx
+++ b/addons/docs/src/mdx/__testfixtures__/story-args.mdx
@@ -5,6 +5,12 @@ import { Story, Meta } from '@storybook/addon-docs/blocks';
# Args
-
- Component notes
+export const ButtonStory = (args) => Component notes ;
+
+
+ {ButtonStory.bind({})}
diff --git a/addons/docs/src/mdx/__testfixtures__/story-args.output.snapshot b/addons/docs/src/mdx/__testfixtures__/story-args.output.snapshot
index ef73decd1034..e22ef3db8561 100644
--- a/addons/docs/src/mdx/__testfixtures__/story-args.output.snapshot
+++ b/addons/docs/src/mdx/__testfixtures__/story-args.output.snapshot
@@ -6,7 +6,7 @@ import { assertIsFn, AddContext } from '@storybook/addon-docs/blocks';
import { Button } from '@storybook/react/demo';
import { Story, Meta } from '@storybook/addon-docs/blocks';
-
+export const ButtonStory = (args) => Component notes ;
const makeShortcode = (name) =>
function MDXDefaultShortcode(props) {
console.warn(
@@ -17,13 +17,16 @@ const makeShortcode = (name) =>
return
;
};
-const layoutProps = {};
+const layoutProps = {
+ ButtonStory,
+};
const MDXLayout = 'wrapper';
function MDXContent({ components, ...props }) {
return (
{\`Args\`}
+
- Component notes
+ {ButtonStory.bind({})}
);
@@ -48,7 +51,7 @@ function MDXContent({ components, ...props }) {
MDXContent.isMDXComponent = true;
-export const componentNotes = () => Component notes ;
+export const componentNotes = ButtonStory.bind({});
componentNotes.storyName = 'component notes';
componentNotes.argTypes = {
a: {
@@ -62,7 +65,7 @@ componentNotes.args = {
a: 1,
b: 2,
};
-componentNotes.parameters = { storySource: { source: 'Component notes ' } };
+componentNotes.parameters = { storySource: { source: 'ButtonStory.bind({})' } };
const componentMeta = { title: 'Button', includeStories: ['componentNotes'] };
diff --git a/addons/docs/src/mdx/mdx-compiler-plugin.js b/addons/docs/src/mdx/mdx-compiler-plugin.js
index be7d6952ba29..8abe68dd3539 100644
--- a/addons/docs/src/mdx/mdx-compiler-plugin.js
+++ b/addons/docs/src/mdx/mdx-compiler-plugin.js
@@ -84,20 +84,25 @@ function genStoryExport(ast, context) {
const storyReactCode = bodyParts.length > 1 ? `<>\n${storyCode}\n>` : storyCode;
// keep track if an indentifier or function call
// avoid breaking change for 5.3
- switch (bodyParts.length === 1 && bodyParts[0].body.type) {
- // We don't know what type the identifier is, but this code
- // assumes it's a function from CSF. Let's see who complains!
- case 'Identifier':
- storyVal = `assertIsFn(${storyCode})`;
- break;
- case 'ArrowFunctionExpression':
- storyVal = `(${storyCode})`;
- break;
- default:
- storyVal = `() => (
+ const BIND_REGEX = /\.bind\(.*\)/;
+ if (bodyParts.length === 1 && BIND_REGEX.test(bodyParts[0].code)) {
+ storyVal = bodyParts[0].code;
+ } else {
+ switch (bodyParts.length === 1 && bodyParts[0].body.type) {
+ // We don't know what type the identifier is, but this code
+ // assumes it's a function from CSF. Let's see who complains!
+ case 'Identifier':
+ storyVal = `assertIsFn(${storyCode})`;
+ break;
+ case 'ArrowFunctionExpression':
+ storyVal = `(${storyCode})`;
+ break;
+ default:
+ storyVal = `() => (
${storyReactCode}
)`;
- break;
+ break;
+ }
}
}
diff --git a/examples/angular-cli/src/stories/addon-controls.stories.ts b/examples/angular-cli/src/stories/addon-controls.stories.ts
new file mode 100644
index 000000000000..86f0d915e7a4
--- /dev/null
+++ b/examples/angular-cli/src/stories/addon-controls.stories.ts
@@ -0,0 +1,18 @@
+import { ButtonComponent } from './doc-button/doc-button.component';
+
+export default {
+ title: 'Addon/Controls',
+ component: ButtonComponent,
+ parameters: { docs: { iframeHeight: 120 } },
+};
+
+const ButtonStory = (args) => ({
+ component: ButtonComponent,
+ props: args,
+});
+
+export const Basic = ButtonStory.bind({});
+Basic.args = { label: 'Args test', isDisabled: false };
+
+export const Disabled = ButtonStory.bind({});
+Disabled.args = { label: 'Disabled', isDisabled: true };
diff --git a/examples/ember-cli/.storybook/main.js b/examples/ember-cli/.storybook/main.js
index 20ab5838242f..d1f25bb46dc5 100644
--- a/examples/ember-cli/.storybook/main.js
+++ b/examples/ember-cli/.storybook/main.js
@@ -8,6 +8,7 @@ module.exports = {
'@storybook/addon-storysource',
'@storybook/addon-actions',
'@storybook/addon-docs',
+ '@storybook/addon-controls',
'@storybook/addon-links',
'@storybook/addon-knobs',
'@storybook/addon-viewport',
diff --git a/examples/ember-cli/package.json b/examples/ember-cli/package.json
index 3e715d3dec46..d7e1e5d2879e 100644
--- a/examples/ember-cli/package.json
+++ b/examples/ember-cli/package.json
@@ -19,6 +19,7 @@
"@storybook/addon-a11y": "6.0.0-beta.29",
"@storybook/addon-actions": "6.0.0-beta.29",
"@storybook/addon-backgrounds": "6.0.0-beta.29",
+ "@storybook/addon-controls": "6.0.0-beta.29",
"@storybook/addon-docs": "6.0.0-beta.29",
"@storybook/addon-knobs": "6.0.0-beta.29",
"@storybook/addon-links": "6.0.0-beta.29",
diff --git a/examples/ember-cli/stories/addon-a11y.stories.js b/examples/ember-cli/stories/addon-a11y.stories.js
index 5ef46b92224b..92cb2a614cfd 100644
--- a/examples/ember-cli/stories/addon-a11y.stories.js
+++ b/examples/ember-cli/stories/addon-a11y.stories.js
@@ -2,7 +2,6 @@ import { hbs } from 'ember-cli-htmlbars';
export default {
title: 'Addon/a11y',
-
parameters: {
options: { selectedPanel: '@storybook/a11y/panel' },
},
diff --git a/examples/ember-cli/stories/addon-actions.stories.js b/examples/ember-cli/stories/addon-actions.stories.js
index 4151467114ab..ce4b67a59eec 100644
--- a/examples/ember-cli/stories/addon-actions.stories.js
+++ b/examples/ember-cli/stories/addon-actions.stories.js
@@ -3,7 +3,6 @@ import { action } from '@storybook/addon-actions';
export default {
title: 'Addon/Actions',
-
parameters: {
options: {
selectedPanel: 'storybook/actions/panel',
diff --git a/examples/ember-cli/stories/addon-backgrounds.stories.js b/examples/ember-cli/stories/addon-backgrounds.stories.js
index 70ea6f30c4ca..39d5848ce98b 100644
--- a/examples/ember-cli/stories/addon-backgrounds.stories.js
+++ b/examples/ember-cli/stories/addon-backgrounds.stories.js
@@ -2,7 +2,6 @@ import { hbs } from 'ember-cli-htmlbars';
export default {
title: 'Addon/Backgrounds',
-
parameters: {
backgrounds: {
default: 'dark',
diff --git a/examples/ember-cli/stories/addon-controls.stories.js b/examples/ember-cli/stories/addon-controls.stories.js
new file mode 100644
index 000000000000..db2a1d8e5247
--- /dev/null
+++ b/examples/ember-cli/stories/addon-controls.stories.js
@@ -0,0 +1,19 @@
+import { hbs } from 'ember-cli-htmlbars';
+
+export default {
+ title: 'Addon/Controls',
+ argTypes: {
+ label: { type: { name: 'string' } },
+ },
+};
+
+const ButtonStory = (args) => ({
+ template: hbs`{{label}} `,
+ context: args,
+});
+
+export const Hello = ButtonStory.bind({});
+Hello.args = { label: 'Hello!' };
+
+export const Bonjour = ButtonStory.bind({});
+Bonjour.args = { label: 'Bonjour!' };
diff --git a/examples/ember-cli/stories/addon-knobs.stories.js b/examples/ember-cli/stories/addon-knobs.stories.js
index 29c9e12b595c..6aff81be1dbc 100644
--- a/examples/ember-cli/stories/addon-knobs.stories.js
+++ b/examples/ember-cli/stories/addon-knobs.stories.js
@@ -5,7 +5,6 @@ import { action } from '@storybook/addon-actions';
export default {
title: 'Addon/Knobs',
decorators: [withKnobs],
-
parameters: {
options: { selectedPanel: 'storybookjs/knobs/panel' },
},
diff --git a/examples/ember-cli/stories/index.stories.js b/examples/ember-cli/stories/index.stories.js
index f2b461b43eac..547a286c29a5 100644
--- a/examples/ember-cli/stories/index.stories.js
+++ b/examples/ember-cli/stories/index.stories.js
@@ -2,7 +2,6 @@ import { hbs } from 'ember-cli-htmlbars';
export default {
title: 'Welcome',
-
parameters: {
options: { showPanel: false },
},
diff --git a/examples/html-kitchen-sink/.storybook/main.js b/examples/html-kitchen-sink/.storybook/main.js
index 059d11ddb6cc..efa6344c6473 100644
--- a/examples/html-kitchen-sink/.storybook/main.js
+++ b/examples/html-kitchen-sink/.storybook/main.js
@@ -7,6 +7,7 @@ module.exports = {
'@storybook/addon-a11y',
'@storybook/addon-actions',
'@storybook/addon-backgrounds',
+ '@storybook/addon-controls',
'@storybook/addon-events',
'@storybook/addon-jest',
'@storybook/addon-knobs',
diff --git a/examples/html-kitchen-sink/package.json b/examples/html-kitchen-sink/package.json
index c4935c8485ca..945cbff1901a 100644
--- a/examples/html-kitchen-sink/package.json
+++ b/examples/html-kitchen-sink/package.json
@@ -16,6 +16,7 @@
"@storybook/addon-a11y": "6.0.0-beta.29",
"@storybook/addon-actions": "6.0.0-beta.29",
"@storybook/addon-backgrounds": "6.0.0-beta.29",
+ "@storybook/addon-controls": "6.0.0-beta.29",
"@storybook/addon-docs": "6.0.0-beta.29",
"@storybook/addon-events": "6.0.0-beta.29",
"@storybook/addon-jest": "6.0.0-beta.29",
diff --git a/examples/html-kitchen-sink/stories/addon-controls.stories.js b/examples/html-kitchen-sink/stories/addon-controls.stories.js
new file mode 100644
index 000000000000..78f9b1d57a6c
--- /dev/null
+++ b/examples/html-kitchen-sink/stories/addon-controls.stories.js
@@ -0,0 +1,16 @@
+export default {
+ title: 'Addons/Controls',
+ argTypes: {
+ label: { type: { name: 'string' } },
+ },
+};
+
+const ButtonStory = ({ label }) => {
+ return `${label}
`;
+};
+
+export const Hello = ButtonStory.bind({});
+Hello.args = { label: 'Hello!' };
+
+export const Bonjour = ButtonStory.bind({});
+Bonjour.args = { label: 'Bonjour!' };
diff --git a/examples/official-storybook/stories/addon-docs/props.stories.mdx b/examples/official-storybook/stories/addon-docs/props.stories.mdx
index ad44ee97ccc4..1afb27de05d2 100644
--- a/examples/official-storybook/stories/addon-docs/props.stories.mdx
+++ b/examples/official-storybook/stories/addon-docs/props.stories.mdx
@@ -11,7 +11,7 @@ import { MemoButton } from '../../components/MemoButton';
parameters={{ controls: { expanded: false } }}
/>
-export const ArgsDisplay = (args = {}) => (
+export const ArgsStory = (args = {}) => (
{Object.entries(args).map(([key, val]) => (
@@ -68,7 +68,7 @@ export const ArgsDisplay = (args = {}) => (
},
}}
>
- {(args) => }
+ {ArgsStory.bind({})}
@@ -88,7 +88,7 @@ export const ArgsDisplay = (args = {}) => (
bar: '',
}}
>
- {(args) => }
+ {ArgsStory.bind({})}
diff --git a/examples/svelte-kitchen-sink/.storybook/main.js b/examples/svelte-kitchen-sink/.storybook/main.js
index aef7a04f9872..0c0d14858f48 100644
--- a/examples/svelte-kitchen-sink/.storybook/main.js
+++ b/examples/svelte-kitchen-sink/.storybook/main.js
@@ -12,6 +12,7 @@ module.exports = {
configureJSX: true,
},
},
+ '@storybook/addon-controls',
'@storybook/addon-links',
'@storybook/addon-knobs',
'@storybook/addon-backgrounds',
diff --git a/examples/svelte-kitchen-sink/package.json b/examples/svelte-kitchen-sink/package.json
index b947393df55d..555d02374976 100644
--- a/examples/svelte-kitchen-sink/package.json
+++ b/examples/svelte-kitchen-sink/package.json
@@ -13,6 +13,7 @@
"@storybook/addon-a11y": "6.0.0-beta.29",
"@storybook/addon-actions": "6.0.0-beta.29",
"@storybook/addon-backgrounds": "6.0.0-beta.29",
+ "@storybook/addon-controls": "6.0.0-beta.29",
"@storybook/addon-docs": "6.0.0-beta.29",
"@storybook/addon-knobs": "6.0.0-beta.29",
"@storybook/addon-links": "6.0.0-beta.29",
diff --git a/examples/svelte-kitchen-sink/src/stories/addon-controls.stories.js b/examples/svelte-kitchen-sink/src/stories/addon-controls.stories.js
new file mode 100644
index 000000000000..08ce74e25268
--- /dev/null
+++ b/examples/svelte-kitchen-sink/src/stories/addon-controls.stories.js
@@ -0,0 +1,26 @@
+import ButtonView from './views/ButtonView.svelte';
+
+export default {
+ title: 'Addon/Controls',
+ argTypes: {
+ rounded: { type: { name: 'boolean' } },
+ message: { type: { name: 'string' } },
+ },
+};
+
+const ButtonStory = (args) => ({
+ Component: ButtonView,
+ props: args,
+});
+
+export const Rounded = ButtonStory.bind({});
+Rounded.args = {
+ rounded: true,
+ message: 'Rounded text',
+};
+
+export const Square = ButtonStory.bind({});
+Square.args = {
+ rounded: false,
+ message: 'Squared text',
+};
diff --git a/examples/vue-kitchen-sink/src/stories/addon-controls.stories.js b/examples/vue-kitchen-sink/src/stories/addon-controls.stories.js
new file mode 100644
index 000000000000..f7e695e05c23
--- /dev/null
+++ b/examples/vue-kitchen-sink/src/stories/addon-controls.stories.js
@@ -0,0 +1,29 @@
+import MyButton from './Button.vue';
+
+export default {
+ title: 'Addon/Controls',
+ component: MyButton,
+ argTypes: {
+ color: { control: { type: 'color' } },
+ },
+};
+
+const ButtonStory = (args) => ({
+ props: Object.keys(args),
+ components: { MyButton },
+ template: '{{label}} ',
+});
+
+export const Rounded = ButtonStory.bind({});
+Rounded.args = {
+ rounded: true,
+ color: '#f00',
+ label: 'A Button with rounded edges',
+};
+
+export const Square = ButtonStory.bind({});
+Square.args = {
+ rounded: false,
+ color: '#00f',
+ label: 'A Button with square edges',
+};
diff --git a/examples/vue-kitchen-sink/src/stories/addon-controls.stories.mdx b/examples/vue-kitchen-sink/src/stories/addon-controls.stories.mdx
new file mode 100644
index 000000000000..c67666637489
--- /dev/null
+++ b/examples/vue-kitchen-sink/src/stories/addon-controls.stories.mdx
@@ -0,0 +1,50 @@
+import { Meta, Preview, Story } from '@storybook/addon-docs/blocks';
+import MyButton from './Button.vue';
+
+
+
+export const ButtonStory = (args) => ({
+ props: Object.keys(args),
+ components: { MyButton },
+ template: '{{label}} ',
+});
+
+# Addon-controls in MDX
+
+Controls can also be defined and used in MDX stories.
+
+## Rounded
+
+
+
+ {ButtonStory.bind({})}
+
+
+
+## Square
+
+
+
+ {ButtonStory.bind({})}
+
+
diff --git a/examples/web-components-kitchen-sink/stories/addon-controls.stories.js b/examples/web-components-kitchen-sink/stories/addon-controls.stories.js
new file mode 100644
index 000000000000..181b2255156f
--- /dev/null
+++ b/examples/web-components-kitchen-sink/stories/addon-controls.stories.js
@@ -0,0 +1,33 @@
+/* eslint-disable import/extensions */
+import { html } from 'lit-html';
+import '../demo-wc-card.js';
+
+export default {
+ title: 'Addons/Controls',
+ component: 'demo-wc-card',
+};
+
+const CardStory = ({ backSide, header, rows }) =>
+ html`
+ A simple card
+ `;
+
+export const Front = CardStory.bind({});
+Front.args = { backSide: false, header: undefined, rows: [] };
+
+export const Back = CardStory.bind({});
+Back.args = { ...Front.args, backSide: true };
+
+export const FrontOwnHeader = CardStory.bind({});
+FrontOwnHeader.args = { ...Front.args, header: 'My own Header' };
+
+export const BackWithData = CardStory.bind({});
+BackWithData.args = {
+ ...Back.args,
+ rows: [
+ { header: 'health', value: '200' },
+ { header: 'mana', value: '100' },
+ ],
+};
diff --git a/lib/components/src/blocks/ArgsTable/ArgRow.stories.tsx b/lib/components/src/blocks/ArgsTable/ArgRow.stories.tsx
index c3434ba8b966..f5fae762926e 100644
--- a/lib/components/src/blocks/ArgsTable/ArgRow.stories.tsx
+++ b/lib/components/src/blocks/ArgsTable/ArgRow.stories.tsx
@@ -122,53 +122,55 @@ const withArgs = {
updateArgs: action('updateArgs'),
};
-export const String = (args) => ;
+const ArgRowStory = (args) => ;
+
+export const String = ArgRowStory.bind({});
String.args = {
row: stringType,
};
-export const LongName = (args) => ;
+export const LongName = ArgRowStory.bind({});
LongName.args = {
row: longNameType,
};
-export const LongDesc = (args) => ;
+export const LongDesc = ArgRowStory.bind({});
LongDesc.args = {
row: longDescType,
};
-export const Number = (args) => ;
+export const Number = ArgRowStory.bind({});
Number.args = {
row: numberType,
};
-export const ObjectOf = (args) => ;
+export const ObjectOf = ArgRowStory.bind({});
ObjectOf.args = {
row: objectType,
};
-export const ArrayOf = (args) => ;
+export const ArrayOf = ArgRowStory.bind({});
ArrayOf.args = {
row: arrayType,
};
-export const ComplexObject = (args) => ;
+export const ComplexObject = ArgRowStory.bind({});
ComplexObject.args = {
row: complexType,
};
-export const Func = (args) => ;
+export const Func = ArgRowStory.bind({});
Func.args = {
row: funcType,
};
-export const Markdown = (args) => ;
+export const Markdown = ArgRowStory.bind({});
Markdown.args = {
row: markdownType,
};
-export const StringCompact = (args) => ;
+export const StringCompact = ArgRowStory.bind({});
StringCompact.args = {
...String.args,
compact: true,
};
-export const Args = (args) => ;
+export const Args = ArgRowStory.bind({});
Args.args = {
...String.args,
...withArgs,
};
-export const ArgsCompact = (args) => ;
+export const ArgsCompact = ArgRowStory.bind({});
ArgsCompact.args = {
...Args.args,
compact: true,
diff --git a/lib/components/src/blocks/ArgsTable/ArgsTable.stories.tsx b/lib/components/src/blocks/ArgsTable/ArgsTable.stories.tsx
index 466994002b9b..e2752a9b9394 100644
--- a/lib/components/src/blocks/ArgsTable/ArgsTable.stories.tsx
+++ b/lib/components/src/blocks/ArgsTable/ArgsTable.stories.tsx
@@ -13,7 +13,9 @@ const eventsSection = { category: 'events ' };
const stringType = ArgRow.String.args.row;
const numberType = ArgRow.Number.args.row;
-export const Normal = (args) => ;
+const ArgsTableStory = (args) => ;
+
+export const Normal = ArgsTableStory.bind({});
Normal.args = {
rows: {
stringType,
@@ -21,7 +23,7 @@ Normal.args = {
},
};
-export const Compact = (args) => ;
+export const Compact = ArgsTableStory.bind({});
Compact.args = {
...Normal.args,
compact: true,
@@ -33,21 +35,21 @@ const sectionRows = {
c: { ...stringType, table: { ...stringType.table, ...eventsSection } },
};
-export const Sections = (args) => ;
+export const Sections = ArgsTableStory.bind({});
Sections.args = {
rows: sectionRows,
};
-export const SectionsCompact = (args) => ;
+export const SectionsCompact = ArgsTableStory.bind({});
SectionsCompact.args = {
...Sections.args,
compact: true,
};
-export const Error = (args) => ;
+export const Error = ArgsTableStory.bind({});
Error.args = {
error: ArgsTableError.NO_COMPONENT,
};
-export const Empty = (args) => ;
+export const Empty = ArgsTableStory.bind({});
Empty.args = { rows: {} };
diff --git a/lib/components/src/blocks/ArgsTable/TabbedArgsTable.stories.tsx b/lib/components/src/blocks/ArgsTable/TabbedArgsTable.stories.tsx
index 86618e3ff17f..6bbe5e71bf99 100644
--- a/lib/components/src/blocks/ArgsTable/TabbedArgsTable.stories.tsx
+++ b/lib/components/src/blocks/ArgsTable/TabbedArgsTable.stories.tsx
@@ -7,10 +7,9 @@ export default {
title: 'Docs/TabbedArgsTable',
};
-const propsSection = { category: 'props ' };
-const eventsSection = { category: 'events ' };
+const Story = (args) => ;
-export const Tabs = (args) => ;
+export const Tabs = Story.bind({});
Tabs.args = {
tabs: {
Normal: Normal.args,
@@ -19,7 +18,7 @@ Tabs.args = {
},
};
-export const Empty = Tabs.bind();
+export const Empty = Story.bind({});
Empty.args = {
tabs: {},
};