Skip to content

Commit

Permalink
Merge pull request #25443 from storybookjs/feat/portable-stories-vue3
Browse files Browse the repository at this point in the history
Vue3: Introduce portable stories API
  • Loading branch information
yannbf authored Jan 24, 2024
2 parents c1fc85f + f52e96f commit 6733062
Show file tree
Hide file tree
Showing 28 changed files with 768 additions and 114 deletions.
1 change: 1 addition & 0 deletions code/renderers/vue3/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
},
"devDependencies": {
"@digitak/esrun": "^3.2.2",
"@testing-library/vue": "^8.0.0",
"@types/prettier": "^3.0.0",
"@vitejs/plugin-vue": "^4.4.0",
"typescript": "^5.3.2",
Expand Down
30 changes: 30 additions & 0 deletions code/renderers/vue3/src/__tests__/button.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
.storybook-button {
font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 700;
border: 0;
border-radius: 3em;
cursor: pointer;
display: inline-block;
line-height: 1;
}
.storybook-button--primary {
color: white;
background-color: #1ea7fd;
}
.storybook-button--secondary {
color: #333;
background-color: transparent;
box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
}
.storybook-button--small {
font-size: 12px;
padding: 10px 16px;
}
.storybook-button--medium {
font-size: 14px;
padding: 11px 20px;
}
.storybook-button--large {
font-size: 16px;
padding: 12px 24px;
}
121 changes: 121 additions & 0 deletions code/renderers/vue3/src/__tests__/composeStories/Button.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { userEvent, within } from '@storybook/testing-library';
import type { Meta, StoryFn as CSF2Story, StoryObj } from '../..';

import Button from './Button.vue';

const meta = {
title: 'Example/Button',
component: Button,
argTypes: {
size: { control: 'select', options: ['small', 'medium', 'large'] },
backgroundColor: { control: 'color' },
onClick: { action: 'clicked' },
},
args: { primary: false },
excludeStories: /.*ImNotAStory$/,
} as Meta<typeof Button>;

export default meta;
type CSF3Story = StoryObj<typeof meta>;

// For testing purposes. Should be ignored in ComposeStories
export const ImNotAStory = 123;

const Template: CSF2Story = (args) => ({
components: { Button },
setup() {
return { args };
},
template: '<Button v-bind="args" />',
});

export const CSF2Secondary = Template.bind({});
CSF2Secondary.args = {
label: 'label coming from story args!',
primary: false,
};

const getCaptionForLocale = (locale: string) => {
switch (locale) {
case 'es':
return 'Hola!';
case 'fr':
return 'Bonjour!';
case 'kr':
return '안녕하세요!';
case 'pt':
return 'Olá!';
default:
return 'Hello!';
}
};

export const CSF2StoryWithLocale: CSF2Story = (args, { globals }) => ({
components: { Button },
setup() {
console.log({ globals });
const label = getCaptionForLocale(globals.locale);
return { args: { ...args, label } };
},
template: `<div>
<p>locale: ${globals.locale}</p>
<Button v-bind="args" />
</div>`,
});
CSF2StoryWithLocale.storyName = 'WithLocale';

export const CSF2StoryWithParamsAndDecorator = Template.bind({});
CSF2StoryWithParamsAndDecorator.args = {
label: 'foo',
};
CSF2StoryWithParamsAndDecorator.parameters = {
layout: 'centered',
};
CSF2StoryWithParamsAndDecorator.decorators = [
() => ({ template: '<div style="margin: 3em;"><story/></div>' }),
];

export const CSF3Primary: CSF3Story = {
args: {
label: 'foo',
size: 'large',
primary: true,
},
};

export const CSF3Button: CSF3Story = {
args: { label: 'foo' },
};

export const CSF3ButtonWithRender: CSF3Story = {
...CSF3Button,
render: (args) => ({
components: { Button },
setup() {
return { args };
},
template: `
<div>
<p data-testid="custom-render">I am a custom render function</p>
<Button v-bind="args" />
</div>
`,
}),
};

export const CSF3InputFieldFilled: CSF3Story = {
...CSF3Button,
render: (args) => ({
components: { Button },
setup() {
return { args };
},
template: '<input data-testid="input" />',
}),
play: async ({ canvasElement, step }) => {
const canvas = within(canvasElement);
await step('Step label', async () => {
await userEvent.type(canvas.getByTestId('input'), 'Hello world!');
});
},
};
46 changes: 46 additions & 0 deletions code/renderers/vue3/src/__tests__/composeStories/Button.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<template>
<button type="button" :class="classes" :style="style" @click="emit('myClickEvent', 0)">
{{ label }}
</button>
</template>

<script lang="ts" setup>
import { computed } from 'vue';
const props = withDefaults(
defineProps<{
/**
* The label of the button
*/
label: string;
/**
* primary or secondary button
*/
primary?: boolean;
/**
* size of the button
*/
size?: 'small' | 'medium' | 'large';
/**
* background color of the button
*/
backgroundColor?: string;
}>(),
{ primary: false }
);
const emit = defineEmits<{
(e: 'myClickEvent', id: number): void;
}>();
const classes = computed(() => ({
'storybook-button': true,
'storybook-button--primary': props.primary,
'storybook-button--secondary': !props.primary,
[`storybook-button--${props.size || 'medium'}`]: true,
}));
const style = computed(() => ({
backgroundColor: props.backgroundColor,
}));
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`Renders CSF2Secondary story 1`] = `
<body>
<div>
<button
class="storybook-button storybook-button--secondary storybook-button--medium"
type="button"
>
label coming from story args!
</button>
</div>
</body>
`;

exports[`Renders CSF2StoryWithLocale story 1`] = `
<body>
<div>
<div>
<p>
locale: undefined
</p>
<button
class="storybook-button storybook-button--secondary storybook-button--medium"
type="button"
>
Hello!
</button>
</div>
</div>
</body>
`;

exports[`Renders CSF3Button story 1`] = `
<body>
<div>
<button
class="storybook-button storybook-button--secondary storybook-button--medium"
type="button"
>
foo
</button>
</div>
</body>
`;

exports[`Renders CSF3ButtonWithRender story 1`] = `
<body>
<div>
<div>
<p
data-testid="custom-render"
>
I am a custom render function
</p>
<button
class="storybook-button storybook-button--secondary storybook-button--medium"
type="button"
>
foo
</button>
</div>
</div>
</body>
`;

exports[`Renders CSF3InputFieldFilled story 1`] = `
<body>
<div>
<input
data-testid="input"
/>
</div>
</body>
`;

exports[`Renders CSF3Primary story 1`] = `
<body>
<div>
<button
class="storybook-button storybook-button--primary storybook-button--large"
type="button"
>
foo
</button>
</div>
</body>
`;
Loading

0 comments on commit 6733062

Please sign in to comment.