-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
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
Vue: Add Vue 3 support #13775
Vue: Add Vue 3 support #13775
Conversation
|
||
--- | ||
|
||
Storybook also comes with a lot of [addons](https://storybook.js.org/docs/vue3/configure/storybook-addons) and a great API to customize as you wish. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does vue3
section get generated on the docs website?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to be added in the frontpage repo. @jonniebigodes can help with that.
|
||
if (innerStory) { | ||
return { | ||
extends: story, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to be tested. I tried using the extends
syntax to support object and function components.
render() { | ||
return h(story, this.props || this.$props); | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I considered making this a setup
method
forceReRender(): void; | ||
raw: () => any; // todo add type | ||
load: (...args: any[]) => void; | ||
app: App; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct place to put this type?
// If an end-user calls `unmount` on the app, we need to clear our root variable | ||
unmounted() { | ||
root = null; | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to make sure this works and can be re-mounted.
import { RenderContext, StoryFnVueReturnType } from './types'; | ||
|
||
const activeStoryComponent = shallowRef<StoryFnVueReturnType | null>(null); | ||
const activeProps = shallowRef<Args>({}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is a shallow ref fine here? I don't think these props need to be wrapped in a reactive
, right?
|
||
const generator: Generator = async (packageManager, npmOptions, options) => { | ||
baseGenerator(packageManager, npmOptions, options, 'vue3', { | ||
extraPackages: ['vue-loader@^16.0.0'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now e2e testing the version string specifying on extraPackages
added in #13774
// TODO(blaine): Remove when we support Vue 3 | ||
vue: (versionRange) => versionRange === 'next' || eqMajor(versionRange, 3), | ||
// TODO(blaine): Remove when we support Vue 3 | ||
// TODO(blaine): Remove when we support Nuxt 3 | ||
nuxt: (versionRange) => eqMajor(versionRange, 3), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I specifically left nuxt here because they don't have v3 out yet and I want to make sure we work with it before allowing it in the generator.
@Aaron-Pool @backbone87 @pocka @graup your careful review much appreciated. @phated pointed out a few questions for vue experts such as yourselves! |
Very nice. I gave the example a spin and it seems to work well so far. |
Hi, when is the ETA for full Vue3 support? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const Template = (args, { argTypes }) => ({ | ||
props: Object.keys(argTypes), | ||
components: { MyButton }, | ||
template: '<my-button v-bind="$props" />', | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might not be the right time to ask this, as I understand it works the same way in vue2, but am I right in saying that the package automatically maps context.args
(the first argument) to $props
in the component?
Is there some way to override this behaviour in the story/template definition? I'm wondering if we could support something like:
const Template = (args, { argTypes }) => ({
props: [...Object.keys(argTypes), 'extraArg'],
components: { MyButton },
template: '<my-button v-bind="$props" />',
args: {
...args,
extraArg: 'value',
},
});
cc @jonniebigodes -- this came up in the development of the vue snippets, IIRC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I honestly have no idea. What happens is that the render
function receives args
and those are set in shallowRef as the props
. Those are then provided to components as props
(2nd argument to h(Component, props)
) and also use a provide()
function to be injected as props
too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tmeasday something like this in vue3 (untested, based on discussion with @phated) would be the vue way of doing things:
const Template = (args, { argTypes }) => ({
props: [...Object.keys(argsTypes), 'extraArg'],
components: { MyButton },
setup(props) {
return () => h(MyButton, { ...props, extraArg: 'foo' })
}
});
We could also potentially bastardize things to make args first class in Vue stories, but this could be confusing to Vue developers since args don't exist in the Vue world. That might look something like this:
const Template = (args) => ({
components: { MyButton },
template: '<my-button v-bind="$props" />',
args: { ...args, extraArg: 'value' },
});
We'd need to post-process this in the Vue framework code to turn it into a valid Vue component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the issue here is the "thing" returned by a vue story is a real existing thing in Vue (essentially equivalent to a component in react), so adding an extra args
property would be sort of bastardizing it. I get that. (Correct me if I have that wrong)
I think there's sort of a mismatch here between the semantics of a vue and a react story.
// In React
const Template = (args) => <Component {...args} />;
// this means:
const Template = /* a "story component" implemented as a SFC */
// In Vue
const Template = (args) = ({ ... });
// this means
const Template = (args) => /* a "story component" */
Notice the difference? In Vue we are returning a component from the function, which will later be "instanciated" (with the args) via the @storybook/vue
framework. That's like fundamentally different, which is why things are a bit hairy.
I suspect (based on not much knowledge) that really the function could be dropped from the vue template. I wonder if that would make things more consistent, and it more natural to do things like modify the args in a setup property.
@wpitallo Releasing this in alpha now, hope to get storyshots/docs this week. This will go stable in 6.2, which should be out in March. |
@phated build is failing. This is probably just a version resolution thing, but we'll need to get it sorted out before we can merge.
|
@tmeasday regarding two packages or one, it's a tradeoff between user/documentation overhead and implementation overhead. In this case, I think with the changes (code, types, etc.) and dependency injection (?) or backflips that would be required to support each variant, I figured it would be better to keep it simple and implement it as a separate package. Now that @phated 's been through the code I'd like to hear what he thinks. |
I don't understand the issue in relation to Vue. The thing returned is the component and you don't need to specify some subprop that defines it. |
I agree, I don't see how it'd be possible to support them side-by-side. |
@shilman Edit: I figured out how to make this happen locally: I had to |
@phated Good work! I am excited for this PR release into alpha. |
Issue: #10654
What I did
This PR adds initial support for Vue 3 through
@storybook/vue3
and CLI detection for Vue 3 withsb init
.It is heavily inspired by earlier prototypes by:
All of whom did the initial exploration to get Vue 3 working in storybook!
Notably missing: Vue 3 support in addon-docs and addon-storyshots, which will be added as individual features once this is in alpha. 😄
How to test
If your answer is yes to any of these, please make sure to include it in your PR.