storybook-vue-addon
storybook-vue-addon copied to clipboard
[Feature] `defineArgTypes` macro to set `argTypes` for all stories
The issue
Currently, in Storybook you have the possibility to define argTypes either via automatic inference or by manually setting the properties..
Manually setting argTypes brings the benefit to allow for a specific value to be selected within the controls, or overriding/customizing descriptions.
Syntax proposal
<script setup lang="ts">
import BButton from "./b-button.vue";
defineArgs<typeof BButton>({
variant: {
options: ["primary", "secondary"],
control: "select",
},
label: {
control: "text",
description: "This is the text displayed on the button."
},
});
</script>
<template>
<Stories title="Components/Button" :component="BButton">
<Story title="Primary">
<BButton variant="primary" label="Click"/>
</Story>
<Story title="Secondary">
<BButton variant="secondary" label="Click" />
</Story>
</Stories>
</template>
Looks like a nice proposal. The args can also be specified only for a single story, right? So maybe adding a args props to Stories and Story? What do you think?
<script setup lang="ts">
import BButton from "./b-button.vue";
const args = defineArgs<typeof BButton>({
variant: {
options: ["primary", "secondary"],
control: "select",
},
label: {
control: "text",
description: "This is the text displayed on the button."
},
});
</script>
<template>
<Stories title="Components/Button" :component="BButton" :args="args">
<Story title="Primary" :args="args">
<BButton variant="primary" label="Click"/>
</Story>
<Story title="Secondary">
<BButton variant="secondary" label="Click" />
</Story>
</Stories>
</template>
Maybe call it controls instead of args/argTypes.
Looks like a nice proposal. The args can also be specified only for a single story, right? So maybe adding a
argsprops toStoriesandStory? What do you think?<script setup lang="ts"> import BButton from "./b-button.vue"; const args = defineArgs<typeof BButton>({ variant: { options: ["primary", "secondary"], control: "select", }, label: { control: "text", description: "This is the text displayed on the button." }, }); </script> <template> <Stories title="Components/Button" :component="BButton" :args="args"> <Story title="Primary" :args="args"> <BButton variant="primary" label="Click"/> </Story> <Story title="Secondary"> <BButton variant="secondary" label="Click" /> </Story> </Stories> </template>Maybe call it
controlsinstead ofargs/argTypes.
Yes, that would be a great experience.
To consider creating custom defineArgs macros, where could be in the project a good place to start looking (so that I could maybe consider how a potential implementation would look like, and maybe spot some pitfall/issues before even considering begin the work?)
However, as I revisit my original idea, there is some consideration about the API that could lead to a simpler/cleaner approach.
Currently, clients of storybook-vue-addon access the API by interacting with the props on <Stories /> and <Story /> components.
The <script /> tag in the Vue SFC, could be considered the "context" that allows them to setup and scaffold whatever environment they need within they story markup.
Therefore, since the library doesn't yet support any custom macros, and I haven't seen any discussion of anything in the pipeline that could lead to a similar implementation, we might be better off simplifying the approach and be consistent, with this type of API:
<script setup lang="ts">
import BButton from "./b-button.vue";
// this is just a static object
// Question: could we provide just the types, but not the macros,
// to help with ts autocomplete?
const argTypes /* : ArgTypes<typeof BButton>*/ = {
variant: {
options: ["primary", "secondary"],
control: "select",
},
label: {
control: "text",
description: "This is the text displayed on the button."
},
};
</script>
<template>
<Stories title="Components/Button" :component="BButton" :arg-types="argsTypes">
<Story title="Primary">
<BButton variant="primary" label="Click"/>
</Story>
<Story title="Secondary">
<BButton variant="secondary" label="Click" />
</Story>
</Stories>
</template>
This approach would remove the complexity of creating/maintaining a macro, which would only have the convenience to expose the schema with types inferred from its generic.
We can achieve the same type if we treat the schema as static object that will be parsed by our <Stories :args-types />, and maybe exporting a type interface (or using the one provided by Storybook).
What do you think?
In my head the defineArgs(Types) macro would also just be the identity function, and is only there to provide the types. A bit similar to vue's defineProps.
For the implementation, you need to get the args prop of the story at https://github.com/tobiasdiez/storybook-vue-addon/blob/02839f9b53c304ec562f34df20d9bf7bc6aa4897/src/core/parser.ts#L66-L70 and then write it back at https://github.com/tobiasdiez/storybook-vue-addon/blob/02839f9b53c304ec562f34df20d9bf7bc6aa4897/src/core/transform.ts#L105-L112. Similar to component.
@tobiasdiez so are we going to create a dedicated macro function (and a dedicated prop) for each part of the Meta configuration?
We have to support args, argTypes, parameters, etc.
I am wondering whether it makes more sense to support these features one by one by dedicated identify function, or whether we should maintain consistency with the CSF approach and provide something like defineMeta<typeof Button>.
We might have these two approaches:
Supporting each Meta config feature separately
<script setup lang="ts">
import BButton from "./b-button.vue";
defineArgs<typeof Button>({
label: 'Click',
})
defineArgTypes<typeof Button>({
variant: {
options: ["primary", "secondary"],
control: "select",
},
label: {
control: "text",
},
});
</script>
<template>
<Stories title="Components/Button" :component="BButton">
<Story title="Primary">
<BButton />
</Story>
<Story title="Secondary" :args="{label: 'Override', variant: 'secondary'}">
<BButton />
</Story>
</Stories>
</template>
or
Supporting all Meta features within the same macro/props schema
<script setup lang="ts">
import Button from "./b-button.vue";
defineMeta<typeof Button>({
args: {
label: 'Click',
variant: 'primary',
},
argTypes: {
variant: {
options: ["primary", "secondary"],
control: "select",
},
label: {
control: "text",
},
}
})
</script>
<template>
<Stories title="Components/Button" :component="BButton">
<Story title="Primary">
<BButton />
</Story>
<Story title="Secondary" :meta="{args: { label: 'Local override', variant: 'secondary'}">
<BButton />
</Story>
</Stories>
</template>
Can't say which approach would be better, before getting more familiar with the project architecture and better understanding the constraints/trade-offs of each proposal, but I think ultimately we have to pick one of the two solutions.
What do you think?
@tobiasdiez I opened a draft https://github.com/tobiasdiez/storybook-vue-addon/pull/73 it's going to be easy to discuss some proposal, API design and edge-cases with some concrete examples.