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

Addon-contexts: Improve Vue integration #6632

Merged
merged 5 commits into from
Apr 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions addons/contexts/src/preview/frameworks/vue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,21 @@ import { ID } from '../../shared/constants';
* This is the framework specific bindings for Vue.
* '@storybook/vue' expects the returning object from a decorator to be a 'VueComponent'.
*/
export const renderVue: Render<Vue.Component> = (contextNodes, propsMap, getStoryVNode) => {
export const renderVue: Render<Vue.Component> = (contextNodes, propsMap, getStoryComponent) => {
const { getRendererFrom, updateReactiveSystem } = ContextsPreviewAPI();
const reactiveProps = updateReactiveSystem(propsMap);
return Vue.extend({
name: ID,
data: () => reactiveProps,
render: createElement =>
getRendererFrom((component, props, children) =>
createElement(component, { props }, [children])
)(contextNodes, reactiveProps, () => createElement(getStoryVNode())),
getRendererFrom((Component, props, children) => {
const { key, ref, style, classNames, ...rest } = props || Object();
const contextData =
Component instanceof Object
? { key, ref, style, class: classNames, props: rest } // component as a Vue object
: { key, ref, style, class: classNames, attrs: rest }; // component as a HTML tag string
return createElement(Component, contextData, [children]);
})(contextNodes, reactiveProps, () => createElement(getStoryComponent())),
});
};

Expand Down
1 change: 1 addition & 0 deletions examples/vue-kitchen-sink/.storybook/addons.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ import '@storybook/addon-knobs/register';
import '@storybook/addon-viewport/register';
import '@storybook/addon-options/register';
import '@storybook/addon-backgrounds/register';
import '@storybook/addon-contexts/register';
1 change: 1 addition & 0 deletions examples/vue-kitchen-sink/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@storybook/addon-actions": "5.1.0-alpha.34",
"@storybook/addon-backgrounds": "5.1.0-alpha.34",
"@storybook/addon-centered": "5.1.0-alpha.34",
"@storybook/addon-contexts": "5.1.0-alpha.34",
"@storybook/addon-knobs": "5.1.0-alpha.34",
"@storybook/addon-links": "5.1.0-alpha.34",
"@storybook/addon-notes": "5.1.0-alpha.34",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Storyshots Addon|Contexts Languages 1`] = `
<div
style="color: white; background: rgb(23, 63, 95); height: 100vh; padding: 10px;"
>
<div>

Your locale is unknown, so I say NULL!

</div>
</div>
`;

exports[`Storyshots Addon|Contexts Simple CSS Theming 1`] = `
<div
style="color: white; background: rgb(23, 63, 95); height: 100vh; padding: 10px;"
>
<span>
I'm a children of the injected 'div' (where provides a theming context).
</span>
</div>
`;
144 changes: 144 additions & 0 deletions examples/vue-kitchen-sink/src/stories/addon-contexts.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { storiesOf } from '@storybook/vue';
import { withContexts } from '@storybook/addon-contexts/vue';

// Example A: Simple CSS Theming
const topLevelContexts = [
{
icon: 'sidebaralt',
title: 'CSS Themes',
components: ['div'],
params: [
{
name: 'Desert',
props: {
style: { color: 'brown', background: '#F4A261', height: '100vh', padding: '10px' },
},
},
{
name: 'Ocean',
props: {
style: { color: 'white', background: '#173F5F', height: '100vh', padding: '10px' },
},
default: true,
},
],
},
];

const storyLevelContexts = [
{
title: 'CSS Themes',
params: [
{
name: 'Forest',
props: {
style: { color: 'teal', background: '#00b894', height: '100vh', padding: '10px' },
},
},
],
},
];

const stories = storiesOf('Addon|Contexts', module).addDecorator(withContexts(topLevelContexts));

stories.add(
'Simple CSS Theming',
() => ({
template:
"<span>I'm a children of the injected 'div' (where provides a theming context).</span>",
}),
{
contexts: storyLevelContexts,
}
);

// Example B: Language (Vue provide/inject API)
const createContext = initialValue => {
const uid = `_${Date.now()}${Math.random()}`;
return {
Provider: {
name: `Context.Provider`,
props: ['value'],
provide() {
return this.value === undefined
? undefined
: {
[uid]: () => this.value,
};
},
template: `
<div>
<slot />
</div>
`,
},
Consumer: {
name: `Context.Consumer`,
inject: {
[uid]: {
default: () => () =>
initialValue instanceof Object ? { ...initialValue } : { value: initialValue },
},
},
computed: {
value() {
return this[uid]();
},
},
template: `
<div>
<slot v-bind="value" />
</div>
`,
},
};
};
Copy link
Contributor Author

@leoyli leoyli Apr 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the createContext in React is really well-designed, so I'm also thinking to make this util component a standalone package or as a helper in the addon. I have google and look into NPM registry, few examples or ideal implementation (vue2.6 changed something so they seems a bit outdated)...

Copy link
Contributor Author

@leoyli leoyli Apr 26, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have published the latest implementation for this util component: vue-create-context with some minor improvements. But I think we can just merge this PR since it is not super long and working well (or maybe just copy past the latest source code from my repo)?!


const NaiveIntlContext = createContext({
locale: 'unknown',
greeting: 'NULL',
});

stories.add(
'Languages',
() => ({
components: { 'NaiveIntlContext.Consumer': NaiveIntlContext.Consumer },
template: `
<NaiveIntlContext.Consumer v-slot="{ locale, greeting }">
Your locale is {{ locale }}, so I say {{ greeting }}!
</NaiveIntlContext.Consumer>
`,
}),
{
contexts: [
{
icon: 'globe',
title: 'Languages',
components: [NaiveIntlContext.Provider],
params: [
{
name: 'English',
props: {
value: { locale: 'en', greeting: 'Hello' },
},
},
{
name: 'French',
props: {
value: { locale: 'fr', greeting: 'Bonjour' },
},
},
{
name: 'Chinese',
props: {
value: { locale: 'cn', greeting: '你好' },
},
},
],
options: {
cancelable: true,
},
},
],
}
);