Skip to content

Commit

Permalink
feat: add storybook
Browse files Browse the repository at this point in the history
Closes #226
  • Loading branch information
jojomatik committed Jul 22, 2023
1 parent 1e1a1ad commit d5ee78b
Show file tree
Hide file tree
Showing 17 changed files with 4,903 additions and 124 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!.storybook
6 changes: 5 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ module.exports = {
node: true,
"vue/setup-compiler-macros": "true",
},
extends: ["@nuxtjs/eslint-config-typescript", "plugin:prettier/recommended"],
extends: [
"@nuxtjs/eslint-config-typescript",
"plugin:prettier/recommended",
"plugin:storybook/recommended",
],
parserOptions: {
ecmaVersion: 2020,
},
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ nuxt.d.ts
.env
public/assets/fonts
public/dependencies.json
storybook-static
15 changes: 15 additions & 0 deletions .storybook/StoryWrapper.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template>
<v-app :theme="themeName">
<v-main>
<slot name="story"></slot>
</v-main>
</v-app>
</template>

<script setup lang="ts">
import { VApp, VMain } from "vuetify/components";
defineProps({
themeName: { type: String, required: true },
});
</script>
32 changes: 32 additions & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import path from "path";
import type { StorybookConfig } from "@storybook/vue3-vite";
import { ConfigEnv, loadConfigFromFile, mergeConfig } from "vite";

const config: StorybookConfig = {
stories: ["../stories/**/*.mdx", "../stories/**/*.stories.@(js|jsx|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
],
framework: {
name: "@storybook/vue3-vite",
options: {},
},
core: {
disableTelemetry: true,
},
docs: {
autodocs: true,
},
async viteFinal(baseConfig) {
const loaded = await loadConfigFromFile(
path.resolve(__dirname, "vite.config.ts") as unknown as ConfigEnv
);
if (!loaded) return baseConfig;
const userConfig = loaded.config;
return mergeConfig(baseConfig, userConfig);
},
};

export default config;
34 changes: 34 additions & 0 deletions .storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { setup } from "@storybook/vue3";
import { createVuetify } from "vuetify";
import * as components from "vuetify/components";
import options from "../vuetify-options";
import { withVuetifyTheme, DEFAULT_THEME } from "./withVuetifyTheme.decorator";

setup((app) => {
app.use(createVuetify({ ...options, components }));
});

export const globalTypes = {
theme: {
name: "Theme",
description: "Global theme for components",
defaultValue: DEFAULT_THEME,
toolbar: {
icon: "paintbrush",
// Array of plain string values or MenuItem shape (see below)
items: [
{ value: "light", title: "Light", left: "🌞" },
{ value: "dark", title: "Dark", left: "🌛" },
],
// Change title based on selected value
dynamicTitle: true,
},
},
};

export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
layout: "fullscreen",
};

export const decorators = [withVuetifyTheme];
9 changes: 9 additions & 0 deletions .storybook/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
// https://v3.nuxtjs.org/concepts/typescript
"extends": "../.nuxt/tsconfig.json",
"compilerOptions": {
"paths": {
"~": ["../"]
}
}
}
22 changes: 22 additions & 0 deletions .storybook/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { defineConfig } from "vite";
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";

// https://vitejs.dev/config/
export default defineConfig({
plugins: [
AutoImport({
imports: ["vue", "vue-router"],
dirs: ["./composables"],
vueTemplate: true,
}),
Components({
dirs: [
"./components/",
// Component folders that should be auto-imported
],
dts: true,
directoryAsNamespace: true,
}),
],
});
22 changes: 22 additions & 0 deletions .storybook/withVuetifyTheme.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { h } from "vue";
import StoryWrapper from "./StoryWrapper.vue";

export const DEFAULT_THEME = "light";

export const withVuetifyTheme = (
storyFn: () => any,
context: { globals: { theme: string }; args: {} }
) => {
const globalTheme = context.globals.theme || DEFAULT_THEME;
const story = storyFn();

return () => {
return h(
StoryWrapper,
{ themeName: globalTheme },
{
story: () => h(story, { ...context.args }),
}
);
};
};
17 changes: 16 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
"build": "nuxi build",
"start": "node .output/server/index.mjs",
"lint": "eslint --ext .ts,.js,.vue .",
"semantic-release": "semantic-release"
"semantic-release": "semantic-release",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"dependencies": {
"@fontsource/roboto": "^5.0.5",
Expand All @@ -20,17 +22,30 @@
"@nuxtjs/eslint-config-typescript": "^12.0.0",
"@nuxtjs/i18n": "^8.0.0-beta.12",
"@nuxtjs/robots": "^3.0.0",
"@storybook/addon-essentials": "7.1.0",
"@storybook/addon-interactions": "7.1.0",
"@storybook/addon-links": "7.1.0",
"@storybook/blocks": "7.1.0",
"@storybook/testing-library": "0.2.0",
"@storybook/vue3": "7.1.0",
"@storybook/vue3-vite": "7.1.0",
"@types/node": "^18.16.19",
"@unhead/vue": "^1.1.26",
"eslint": "^8.44.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-storybook": "^0.6.13",
"eslint-plugin-vue": "^9.15.1",
"prettier": "^2.8.8",
"react": "18.2.0",
"react-dom": "18.2.0",
"rollup": "^3.26.2",
"rollup-plugin-license": "^3.0.1",
"sass": "^1.63.6",
"storybook": "7.1.0",
"typescript": "^4.9.5",
"unplugin-auto-import": "^0.16.4",
"unplugin-vue-components": "^0.25.0",
"vite": "^4.3.9",
"vite-plugin-vuetify": "^1.0.2"
},
Expand Down
55 changes: 2 additions & 53 deletions plugins/vuetify.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,6 @@
import { createVuetify } from "vuetify";
import { aliases, mdi } from "vuetify/iconsets/mdi-svg";
import { defineNuxtPlugin } from "#app";
import options from "~/vuetify-options";

export default defineNuxtPlugin((app) => {
const vuetify = createVuetify({
theme: {
defaultTheme: "light",
themes: {
light: {
dark: false,
colors: {
background: "#FFFFFF",
surface: "#FFFFFF",
"surface-variant": "#424242",
"on-surface-variant": "#EEEEEE",
primary: "#004c6d",
"primary-darken-1": "#3700B3",
secondary: "#037a7a",
"secondary-darken-1": "#018786",
error: "#B00020",
info: "#2196F3",
success: "#4CAF50",
warning: "#FB8C00",
},
variables: {},
},
dark: {
dark: true,
colors: {
background: "#121212",
surface: "#212121",
"surface-variant": "#BDBDBD",
"on-surface-variant": "#424242",
primary: "#0074a5",
secondary: "#039e9e",
error: "#CF6679",
info: "#2196F3",
success: "#4CAF50",
warning: "#FB8C00",
},
variables: {},
},
},
},
icons: {
defaultSet: "mdi",
aliases,
sets: {
mdi,
},
},
ssr: true,
});

app.vueApp.use(vuetify);
app.vueApp.use(createVuetify(options));
});
5 changes: 5 additions & 0 deletions stories/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rules": {
"vue/multi-word-component-names": "off"
}
}
51 changes: 51 additions & 0 deletions stories/Button.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { Meta, StoryObj } from "@storybook/vue3";
import MyButton from "./MyButton.vue";

// More on how to set up stories at: https://storybook.js.org/docs/vue/writing-stories/introduction
const meta: Meta<typeof MyButton> = {
title: "Components/Button",
component: MyButton,
argTypes: {
onClick: {},
size: { control: "select", options: ["small", "default", "large"] },
color: { control: "select", options: ["primary", "secondary"] },
},
args: { color: "primary" }, // default value
};

export default meta;

type Story = StoryObj<typeof meta>;

/*
*👇 Render functions are a framework specific feature to allow you control on how the component renders.
* See https://storybook.js.org/docs/vue/api/csf
* to learn how to use render functions.
*/
export const Primary: Story = {
args: {
color: "primary",
text: "Button",
},
};

export const Secondary: Story = {
args: {
color: "secondary",
text: "Button",
},
};

export const Large: Story = {
args: {
text: "Button",
size: "large",
},
};

export const Small: Story = {
args: {
text: "Button",
size: "small",
},
};
1 change: 1 addition & 0 deletions stories/Introduction.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Intro
37 changes: 37 additions & 0 deletions stories/MyButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<template>
<v-btn :text="text" :size="size" :color="color" @click="onClick"> </v-btn>
</template>

<script setup lang="ts">
defineProps({
/**
* The text to display.
*/
text: { type: String, required: true },
/**
* The size of the button.
*/
size: { type: String, default: "default" },
/**
* The color of the button.
*/
color: { type: String, default: "primary" },
});
const emit = defineEmits<{
/**
* An event that is called if the button is clicked.
* @param e the event
* @param value the click event
*/
(e: "click", value: Event): void;
}>();
/**
* A function that is called if the button is clicked.
* @param e the event
*/
function onClick(e: Event): void {
emit("click", e);
}
</script>
Loading

0 comments on commit d5ee78b

Please sign in to comment.