Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] defineArgTypes macro to set argTypes for all stories #70

Open
floroz opened this issue Aug 15, 2023 · 5 comments
Open

[Feature] defineArgTypes macro to set argTypes for all stories #70

floroz opened this issue Aug 15, 2023 · 5 comments
Labels
status: waiting for info type: enhancement Improving upon an existing feature

Comments

@floroz
Copy link
Contributor

floroz commented Aug 15, 2023

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>
@tobiasdiez
Copy link
Owner

tobiasdiez commented Sep 4, 2023

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.

@tobiasdiez tobiasdiez added type: enhancement Improving upon an existing feature status: waiting for info labels Sep 4, 2023
@floroz
Copy link
Contributor Author

floroz commented Sep 4, 2023

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.

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?

@tobiasdiez
Copy link
Owner

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

const meta = {
title: extractTitle(root),
component: extractComponent(root),
tags: [],
}
and then write it back at
return `export default {
${title ? `title: '${title}',` : ''}
${component ? `component: ${component},` : ''}
//decorators: [ ... ],
parameters: {
${docs ? `docs: { page: MDXContent },` : ''}
}
}
. Similar to component.

@floroz
Copy link
Contributor Author

floroz commented Sep 4, 2023

@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?

@floroz
Copy link
Contributor Author

floroz commented Sep 6, 2023

@tobiasdiez I opened a draft #73 it's going to be easy to discuss some proposal, API design and edge-cases with some concrete examples.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: waiting for info type: enhancement Improving upon an existing feature
Projects
None yet
Development

No branches or pull requests

2 participants