diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 887d17961f..7f8ed0922f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -203,6 +203,90 @@ niraxは、Misskeyで使用しているオリジナルのフロントエンド
vue-routerとの最大の違いは、niraxは複数のルーターが存在することを許可している点です。
これにより、アプリ内ウィンドウでブラウザとは個別にルーティングすることなどが可能になります。
+## Storybook
+
+Misskey uses [Storybook](https://storybook.js.org/) for UI development.
+
+### Setup
+
+```bash
+cd path/to/packages/frontend
+pnpm tsc -p .storybook && (node .storybook/generate.js & node .storybook/preload-locale.js & node .storybook/preload-theme.js)
+```
+
+### Run
+
+```bash
+cd path/to/packages/frontend
+pnpm storybook dev
+```
+
+### Usage
+
+When you create a new component (in this example, `MyComponent.vue`), the story file (`MyComponent.stories.ts`) will be automatically generated by the `.storybook/generate.js` script.
+You can override the default story by creating a impl story file (`MyComponent.stories.impl.ts`).
+
+```ts
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+/* eslint-disable import/no-duplicates */
+import { StoryObj } from '@storybook/vue3';
+import MyComponent from './MyComponent.vue';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MyComponent,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...args,
+ };
+ },
+ },
+ template: '',
+ };
+ },
+ args: {
+ foo: 'bar',
+ },
+ parameters: {
+ layout: 'centered',
+ },
+} satisfies StoryObj;
+```
+
+If you want to opt-out from the automatic generation, create a `MyComponent.stories.impl.ts` file and add the following line to the file.
+
+```ts
+import MyComponent from './MyComponent.vue';
+void MyComponent;
+```
+
+You can use msw to mock API requests in the storybook. Creating a `MyComponent.stories.msw.ts` file and add the following line to the file.
+
+```ts
+import { rest } from 'msw';
+export const handlers = [
+ rest.post('/api/notes/timeline', (req, res, ctx) => {
+ return res(
+ ctx.json({
+ notes: [],
+ users: [],
+ hasNext: false,
+ })
+ );
+ }),
+];
+```
+
+Don't forget to re-run the `.storybook/generate.js` script after adding, editing, or removing the above files.
+
## Notes
### How to resolve conflictions occurred at pnpm-lock.yaml?
diff --git a/packages/frontend/.storybook/main.ts b/packages/frontend/.storybook/main.ts
index 90cc57536f..c58b6f1d70 100644
--- a/packages/frontend/.storybook/main.ts
+++ b/packages/frontend/.storybook/main.ts
@@ -1,7 +1,6 @@
import { resolve } from 'node:path';
import type { StorybookConfig } from '@storybook/vue3-vite';
import { mergeConfig } from 'vite';
-import { getConfig } from '../vite.config';
const config = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
@@ -22,18 +21,9 @@ const config = {
disableTelemetry: true,
},
async viteFinal(config, options) {
- const { plugins, build: { rollupOptions, ...build }, ...original } = getConfig();
- console.dir(config, {depth:Infinity});
- console.dir(original, {depth:Infinity});
- const x = mergeConfig(config, {
- ...original,
- build,
+ return mergeConfig(config, {
assetsInclude: [resolve(__dirname, '../node_modules/@tabler/icons-webfont/**/*.{css,eot,ttf,woff,woff2}')],
- server: {
- hmr: false,
- },
});
- return x;
},
} satisfies StorybookConfig;
export default config;
diff --git a/packages/frontend/src/components/MkCaptcha.stories.impl.ts b/packages/frontend/src/components/MkCaptcha.stories.impl.ts
new file mode 100644
index 0000000000..6ac437a277
--- /dev/null
+++ b/packages/frontend/src/components/MkCaptcha.stories.impl.ts
@@ -0,0 +1,2 @@
+import MkCaptcha from './MkCaptcha.vue';
+void MkCaptcha;
diff --git a/packages/frontend/src/components/MkCaptcha.stories.ts b/packages/frontend/src/components/MkCaptcha.stories.ts
index 78db7287ae..680c0387cc 100644
--- a/packages/frontend/src/components/MkCaptcha.stories.ts
+++ b/packages/frontend/src/components/MkCaptcha.stories.ts
@@ -1,34 +1,10 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable import/no-default-export */
-import { Meta, StoryObj } from '@storybook/vue3';
-import MkCaptcha from './MkCaptcha.vue';
+import { Meta } from '@storybook/vue3';
const meta = {
title: 'components/MkCaptcha',
component: MkCaptcha,
} satisfies Meta;
-export const Default = {
- render(args) {
- return {
- components: {
- MkCaptcha,
- },
- setup() {
- return {
- args,
- };
- },
- computed: {
- props() {
- return {
- ...args,
- };
- },
- },
- template: '',
- };
- },
- parameters: {
- layout: 'centered',
- },
-} satisfies StoryObj;
export default meta;
+import MkCaptcha from './MkCaptcha.vue';
+void MkCaptcha;
diff --git a/packages/frontend/src/components/global/MkAvatar.stories.impl.ts b/packages/frontend/src/components/global/MkAvatar.stories.impl.ts
index ca3db80b2e..6de5768af8 100644
--- a/packages/frontend/src/components/global/MkAvatar.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkAvatar.stories.impl.ts
@@ -1,5 +1,4 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
-/* eslint-disable import/no-default-export */
/* eslint-disable import/no-duplicates */
import { StoryObj } from '@storybook/vue3';
import MkAvatar from './MkAvatar.vue';
diff --git a/packages/frontend/src/components/global/MkAvatar.stories.ts b/packages/frontend/src/components/global/MkAvatar.stories.ts
index 6cd9ca0a9b..4d819982ee 100644
--- a/packages/frontend/src/components/global/MkAvatar.stories.ts
+++ b/packages/frontend/src/components/global/MkAvatar.stories.ts
@@ -7,7 +7,6 @@ const meta = {
} satisfies Meta;
export default meta;
/* eslint-disable @typescript-eslint/explicit-function-return-type */
-/* eslint-disable import/no-default-export */
/* eslint-disable import/no-duplicates */
import { StoryObj } from '@storybook/vue3';
import MkAvatar from './MkAvatar.vue';
@@ -68,13 +67,6 @@ export const ProfilePageCat = {
user: {
...ProfilePage.args.user,
isCat: true,
- // avatarUrl: 'https://millionlive-theaterdays.idolmaster-official.jp/assets/data/webp/common/footer/icon_app.png.webp',
- // avatarUrl: 'https://cdn.imastodon.net/accounts/avatars/000/144/021/original/8137afa4114ab85f.png',
- // avatarUrl: 'https://avatars.githubusercontent.com/u/4439005?v=4',
- // avatarUrl: 'https://avatars.githubusercontent.com/u/7973572?v=4',
- // avatarUrl: 'https://avatars.githubusercontent.com/u/6533808?v=4',
- // avatarUrl: 'https://avatars.githubusercontent.com/u/7106976?v=4',
- avatarUrl: 'https://avatars.githubusercontent.com/u/3396686?v=4',
},
},
};