summaryrefslogtreecommitdiff
path: root/packages/frontend/src
diff options
context:
space:
mode:
authorzyoshoka <107108195+zyoshoka@users.noreply.github.com>2024-07-30 19:48:16 +0900
committerGitHub <noreply@github.com>2024-07-30 19:48:16 +0900
commitc7354c5e306c80d25db838ff64d4f3d26a47bd7f (patch)
treead8ccd647431a0d04637ebe985e2677b81b55080 /packages/frontend/src
parentfeat: media silence (#13842) (diff)
downloadmisskey-c7354c5e306c80d25db838ff64d4f3d26a47bd7f.tar.gz
misskey-c7354c5e306c80d25db838ff64d4f3d26a47bd7f.tar.bz2
misskey-c7354c5e306c80d25db838ff64d4f3d26a47bd7f.zip
test(#10336): add `components/Mk[D-E].*` stories (#14118)
* test(storybook): add `components/Mk[D-E].*` stories * fix: mock instance name * fix: invalid `reactionAcceptance` value * style: missing trailing commas
Diffstat (limited to 'packages/frontend/src')
-rw-r--r--packages/frontend/src/components/MkDateSeparatedList.stories.impl.ts7
-rw-r--r--packages/frontend/src/components/MkDialog.stories.impl.ts159
-rw-r--r--packages/frontend/src/components/MkDivider.stories.impl.ts7
-rw-r--r--packages/frontend/src/components/MkDonation.stories.impl.ts54
-rw-r--r--packages/frontend/src/components/MkDrive.file.stories.impl.ts48
-rw-r--r--packages/frontend/src/components/MkDrive.folder.stories.impl.ts70
-rw-r--r--packages/frontend/src/components/MkDrive.navFolder.stories.impl.ts7
-rw-r--r--packages/frontend/src/components/MkDrive.stories.impl.ts82
-rw-r--r--packages/frontend/src/components/MkDriveFileThumbnail.stories.impl.ts41
-rw-r--r--packages/frontend/src/components/MkDriveFileThumbnail.vue2
-rw-r--r--packages/frontend/src/components/MkDriveSelectDialog.stories.impl.ts7
-rw-r--r--packages/frontend/src/components/MkDriveWindow.stories.impl.ts7
-rw-r--r--packages/frontend/src/components/MkEmojiPicker.section.stories.impl.ts7
-rw-r--r--packages/frontend/src/components/MkEmojiPicker.stories.impl.ts54
-rw-r--r--packages/frontend/src/components/MkEmojiPickerDialog.stories.impl.ts7
15 files changed, 558 insertions, 1 deletions
diff --git a/packages/frontend/src/components/MkDateSeparatedList.stories.impl.ts b/packages/frontend/src/components/MkDateSeparatedList.stories.impl.ts
new file mode 100644
index 0000000000..0e5635754c
--- /dev/null
+++ b/packages/frontend/src/components/MkDateSeparatedList.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkDateSeparatedList from './MkDateSeparatedList.vue';
+void MkDateSeparatedList;
diff --git a/packages/frontend/src/components/MkDialog.stories.impl.ts b/packages/frontend/src/components/MkDialog.stories.impl.ts
new file mode 100644
index 0000000000..2d8d3661f2
--- /dev/null
+++ b/packages/frontend/src/components/MkDialog.stories.impl.ts
@@ -0,0 +1,159 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { action } from '@storybook/addon-actions';
+import { expect, userEvent, waitFor, within } from '@storybook/test';
+import { StoryObj } from '@storybook/vue3';
+import { i18n } from '@/i18n.js';
+import MkDialog from './MkDialog.vue';
+const Base = {
+ render(args) {
+ return {
+ components: {
+ MkDialog,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ events() {
+ return {
+ done: action('done'),
+ closed: action('closed'),
+ };
+ },
+ },
+ template: '<MkDialog v-bind="props" v-on="events" />',
+ };
+ },
+ args: {
+ text: 'Hello, world!',
+ },
+ parameters: {
+ layout: 'centered',
+ },
+} satisfies StoryObj<typeof MkDialog>;
+export const Success = {
+ ...Base,
+ args: {
+ ...Base.args,
+ type: 'success',
+ },
+} satisfies StoryObj<typeof MkDialog>;
+export const Error = {
+ ...Base,
+ args: {
+ ...Base.args,
+ type: 'error',
+ },
+} satisfies StoryObj<typeof MkDialog>;
+export const Warning = {
+ ...Base,
+ args: {
+ ...Base.args,
+ type: 'warning',
+ },
+} satisfies StoryObj<typeof MkDialog>;
+export const Info = {
+ ...Base,
+ args: {
+ ...Base.args,
+ type: 'info',
+ },
+} satisfies StoryObj<typeof MkDialog>;
+export const Question = {
+ ...Base,
+ args: {
+ ...Base.args,
+ type: 'question',
+ },
+} satisfies StoryObj<typeof MkDialog>;
+export const Waiting = {
+ ...Base,
+ args: {
+ ...Base.args,
+ type: 'waiting',
+ },
+} satisfies StoryObj<typeof MkDialog>;
+export const DialogWithActions = {
+ ...Question,
+ args: {
+ ...Question.args,
+ text: i18n.ts.areYouSure,
+ actions: [
+ {
+ text: i18n.ts.yes,
+ primary: true,
+ callback() {
+ action('YES')();
+ },
+ },
+ {
+ text: i18n.ts.no,
+ callback() {
+ action('NO')();
+ },
+ },
+ ],
+ },
+} satisfies StoryObj<typeof MkDialog>;
+export const DialogWithDangerActions = {
+ ...Warning,
+ args: {
+ ...Warning.args,
+ text: i18n.ts.resetAreYouSure,
+ actions: [
+ {
+ text: i18n.ts.yes,
+ danger: true,
+ primary: true,
+ callback() {
+ action('YES')();
+ },
+ },
+ {
+ text: i18n.ts.no,
+ callback() {
+ action('NO')();
+ },
+ },
+ ],
+ },
+} satisfies StoryObj<typeof MkDialog>;
+export const DialogWithInput = {
+ ...Question,
+ args: {
+ ...Question.args,
+ title: 'Hello, world!',
+ text: undefined,
+ input: {
+ placeholder: i18n.ts.inputMessageHere,
+ type: 'text',
+ default: null,
+ minLength: 2,
+ maxLength: 3,
+ },
+ },
+ async play({ canvasElement }) {
+ const canvas = within(canvasElement);
+ await expect(canvasElement).toHaveTextContent(i18n.tsx._dialog.charactersBelow({ current: 0, min: 2 }));
+ const okButton = canvas.getByRole('button', { name: i18n.ts.ok });
+ await expect(okButton).toBeDisabled();
+ const input = canvas.getByRole<HTMLInputElement>('combobox');
+ await waitFor(() => userEvent.hover(input));
+ await waitFor(() => userEvent.click(input));
+ await waitFor(() => userEvent.type(input, 'M'));
+ await expect(canvasElement).toHaveTextContent(i18n.tsx._dialog.charactersBelow({ current: 1, min: 2 }));
+ await waitFor(() => userEvent.type(input, 'i'));
+ await expect(okButton).toBeEnabled();
+ },
+} satisfies StoryObj<typeof MkDialog>;
diff --git a/packages/frontend/src/components/MkDivider.stories.impl.ts b/packages/frontend/src/components/MkDivider.stories.impl.ts
new file mode 100644
index 0000000000..a593111987
--- /dev/null
+++ b/packages/frontend/src/components/MkDivider.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkDivider from './MkDivider.vue';
+void MkDivider;
diff --git a/packages/frontend/src/components/MkDonation.stories.impl.ts b/packages/frontend/src/components/MkDonation.stories.impl.ts
new file mode 100644
index 0000000000..27d6b7df6c
--- /dev/null
+++ b/packages/frontend/src/components/MkDonation.stories.impl.ts
@@ -0,0 +1,54 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { action } from '@storybook/addon-actions';
+import { StoryObj } from '@storybook/vue3';
+import { onBeforeUnmount } from 'vue';
+import MkDonation from './MkDonation.vue';
+import { instance } from '@/instance.js';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkDonation,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ events() {
+ return {
+ closed: action('closed'),
+ };
+ },
+ },
+ template: '<MkDonation v-bind="props" v-on="events" />',
+ };
+ },
+ args: {
+ // @ts-expect-error name is used for mocking instance
+ name: 'Misskey Hub',
+ },
+ decorators: [
+ (_, { args }) => ({
+ setup() {
+ // @ts-expect-error name is used for mocking instance
+ instance.name = args.name;
+ onBeforeUnmount(() => instance.name = null);
+ },
+ template: '<story/>',
+ }),
+ ],
+ parameters: {
+ layout: 'centered',
+ },
+} satisfies StoryObj<typeof MkDonation>;
diff --git a/packages/frontend/src/components/MkDrive.file.stories.impl.ts b/packages/frontend/src/components/MkDrive.file.stories.impl.ts
new file mode 100644
index 0000000000..5f6e6a0667
--- /dev/null
+++ b/packages/frontend/src/components/MkDrive.file.stories.impl.ts
@@ -0,0 +1,48 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { action } from '@storybook/addon-actions';
+import { StoryObj } from '@storybook/vue3';
+import MkDrive_file from './MkDrive.file.vue';
+import { file } from '../../.storybook/fakes.js';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkDrive_file,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ events() {
+ return {
+ chosen: action('chosen'),
+ dragstart: action('dragstart'),
+ dragend: action('dragend'),
+ };
+ },
+ },
+ template: '<MkDrive_file v-bind="props" v-on="events" />',
+ };
+ },
+ args: {
+ file: file(),
+ },
+ parameters: {
+ chromatic: {
+ // NOTE: ロードが終わるまで待つ
+ delay: 3000,
+ },
+ layout: 'centered',
+ },
+} satisfies StoryObj<typeof MkDrive_file>;
diff --git a/packages/frontend/src/components/MkDrive.folder.stories.impl.ts b/packages/frontend/src/components/MkDrive.folder.stories.impl.ts
new file mode 100644
index 0000000000..5f8ef48520
--- /dev/null
+++ b/packages/frontend/src/components/MkDrive.folder.stories.impl.ts
@@ -0,0 +1,70 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { action } from '@storybook/addon-actions';
+import { StoryObj } from '@storybook/vue3';
+import { http, HttpResponse } from 'msw';
+import * as Misskey from 'misskey-js';
+import MkDrive_folder from './MkDrive.folder.vue';
+import { folder } from '../../.storybook/fakes.js';
+import { commonHandlers } from '../../.storybook/mocks.js';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkDrive_folder,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ events() {
+ return {
+ chosen: action('chosen'),
+ move: action('move'),
+ upload: action('upload'),
+ removeFile: action('removeFile'),
+ removeFolder: action('removeFolder'),
+ dragstart: action('dragstart'),
+ dragend: action('dragend'),
+ };
+ },
+ },
+ template: '<MkDrive_folder v-bind="props" v-on="events" />',
+ };
+ },
+ args: {
+ folder: folder(),
+ },
+ parameters: {
+ layout: 'centered',
+ msw: {
+ handlers: [
+ ...commonHandlers,
+ http.post('/api/drive/folders/delete', async ({ request }) => {
+ action('POST /api/drive/folders/delete')(await request.json());
+ return HttpResponse.json(undefined, { status: 204 });
+ }),
+ http.post('/api/drive/folders/update', async ({ request }) => {
+ const req = await request.json() as Misskey.entities.DriveFoldersUpdateRequest;
+ action('POST /api/drive/folders/update')(req);
+ return HttpResponse.json({
+ ...folder(),
+ id: req.folderId,
+ name: req.name ?? folder().name,
+ parentId: req.parentId ?? folder().parentId,
+ });
+ }),
+ ],
+ },
+ },
+} satisfies StoryObj<typeof MkDrive_folder>;
diff --git a/packages/frontend/src/components/MkDrive.navFolder.stories.impl.ts b/packages/frontend/src/components/MkDrive.navFolder.stories.impl.ts
new file mode 100644
index 0000000000..9d49f24fa4
--- /dev/null
+++ b/packages/frontend/src/components/MkDrive.navFolder.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkDrive_navFolder from './MkDrive.navFolder.vue';
+void MkDrive_navFolder;
diff --git a/packages/frontend/src/components/MkDrive.stories.impl.ts b/packages/frontend/src/components/MkDrive.stories.impl.ts
new file mode 100644
index 0000000000..fe20e61415
--- /dev/null
+++ b/packages/frontend/src/components/MkDrive.stories.impl.ts
@@ -0,0 +1,82 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { action } from '@storybook/addon-actions';
+import { StoryObj } from '@storybook/vue3';
+import { http, HttpResponse } from 'msw';
+import * as Misskey from 'misskey-js';
+import MkDrive from './MkDrive.vue';
+import { file, folder } from '../../.storybook/fakes.js';
+import { commonHandlers } from '../../.storybook/mocks.js';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkDrive,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ events() {
+ return {
+ selected: action('selected'),
+ 'change-selection': action('change-selection'),
+ 'move-root': action('move-root'),
+ cd: action('cd'),
+ 'open-folder': action('open-folder'),
+ };
+ },
+ },
+ template: '<MkDrive v-bind="props" v-on="events" />',
+ };
+ },
+ parameters: {
+ chromatic: {
+ // NOTE: ロードが終わるまで待つ
+ delay: 3000,
+ },
+ layout: 'centered',
+ msw: {
+ handlers: [
+ ...commonHandlers,
+ http.post('/api/drive/files', async ({ request }) => {
+ action('POST /api/drive/files')(await request.json());
+ return HttpResponse.json([file()]);
+ }),
+ http.post('/api/drive/folders', async ({ request }) => {
+ action('POST /api/drive/folders')(await request.json());
+ return HttpResponse.json([folder(crypto.randomUUID())]);
+ }),
+ http.post('/api/drive/folders/create', async ({ request }) => {
+ const req = await request.json() as Misskey.entities.DriveFoldersCreateRequest;
+ action('POST /api/drive/folders/create')(req);
+ return HttpResponse.json(folder(crypto.randomUUID(), req.name, req.parentId));
+ }),
+ http.post('/api/drive/folders/delete', async ({ request }) => {
+ action('POST /api/drive/folders/delete')(await request.json());
+ return HttpResponse.json(undefined, { status: 204 });
+ }),
+ http.post('/api/drive/folders/update', async ({ request }) => {
+ const req = await request.json() as Misskey.entities.DriveFoldersUpdateRequest;
+ action('POST /api/drive/folders/update')(req);
+ return HttpResponse.json({
+ ...folder(),
+ id: req.folderId,
+ name: req.name ?? folder().name,
+ parentId: req.parentId ?? folder().parentId,
+ });
+ }),
+ ]
+ },
+ },
+} satisfies StoryObj<typeof MkDrive>;
diff --git a/packages/frontend/src/components/MkDriveFileThumbnail.stories.impl.ts b/packages/frontend/src/components/MkDriveFileThumbnail.stories.impl.ts
new file mode 100644
index 0000000000..3fa24d7edb
--- /dev/null
+++ b/packages/frontend/src/components/MkDriveFileThumbnail.stories.impl.ts
@@ -0,0 +1,41 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { StoryObj } from '@storybook/vue3';
+import MkDriveFileThumbnail from './MkDriveFileThumbnail.vue';
+import { file } from '../../.storybook/fakes.js';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkDriveFileThumbnail,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ template: '<MkDriveFileThumbnail v-bind="props" />',
+ };
+ },
+ args: {
+ file: file(),
+ fit: 'contain',
+ },
+ parameters: {
+ chromatic: {
+ // NOTE: ロードが終わるまで待つ
+ delay: 3000,
+ },
+ layout: 'centered',
+ },
+} satisfies StoryObj<typeof MkDriveFileThumbnail>;
diff --git a/packages/frontend/src/components/MkDriveFileThumbnail.vue b/packages/frontend/src/components/MkDriveFileThumbnail.vue
index 706c9a55c7..2c47a70970 100644
--- a/packages/frontend/src/components/MkDriveFileThumbnail.vue
+++ b/packages/frontend/src/components/MkDriveFileThumbnail.vue
@@ -26,7 +26,7 @@ import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
const props = defineProps<{
file: Misskey.entities.DriveFile;
- fit: string;
+ fit: 'cover' | 'contain';
}>();
const is = computed(() => {
diff --git a/packages/frontend/src/components/MkDriveSelectDialog.stories.impl.ts b/packages/frontend/src/components/MkDriveSelectDialog.stories.impl.ts
new file mode 100644
index 0000000000..fe8f705165
--- /dev/null
+++ b/packages/frontend/src/components/MkDriveSelectDialog.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkDriveSelectDialog from './MkDriveSelectDialog.vue';
+void MkDriveSelectDialog;
diff --git a/packages/frontend/src/components/MkDriveWindow.stories.impl.ts b/packages/frontend/src/components/MkDriveWindow.stories.impl.ts
new file mode 100644
index 0000000000..faa1f7fd5f
--- /dev/null
+++ b/packages/frontend/src/components/MkDriveWindow.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkDriveWindow from './MkDriveWindow.vue';
+void MkDriveWindow;
diff --git a/packages/frontend/src/components/MkEmojiPicker.section.stories.impl.ts b/packages/frontend/src/components/MkEmojiPicker.section.stories.impl.ts
new file mode 100644
index 0000000000..69aef577de
--- /dev/null
+++ b/packages/frontend/src/components/MkEmojiPicker.section.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkEmojiPicker_section from './MkEmojiPicker.section.vue';
+void MkEmojiPicker_section;
diff --git a/packages/frontend/src/components/MkEmojiPicker.stories.impl.ts b/packages/frontend/src/components/MkEmojiPicker.stories.impl.ts
new file mode 100644
index 0000000000..d38d8de808
--- /dev/null
+++ b/packages/frontend/src/components/MkEmojiPicker.stories.impl.ts
@@ -0,0 +1,54 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { action } from '@storybook/addon-actions';
+import { expect, userEvent, waitFor, within } from '@storybook/test';
+import { StoryObj } from '@storybook/vue3';
+import { i18n } from '@/i18n.js';
+import MkEmojiPicker from './MkEmojiPicker.vue';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkEmojiPicker,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ events() {
+ return {
+ chosen: action('chosen'),
+ };
+ },
+ },
+ template: '<MkEmojiPicker v-bind="props" v-on="events" />',
+ };
+ },
+ async play({ canvasElement }) {
+ const canvas = within(canvasElement);
+ const faceSection = canvas.getByText(/face/i);
+ await waitFor(() => userEvent.click(faceSection));
+ const grinning = canvasElement.querySelector('[data-emoji="😀"]');
+ await expect(grinning).toBeInTheDocument();
+ if (grinning == null) throw new Error(); // NOTE: not called
+ await waitFor(() => userEvent.click(grinning));
+ const recentUsedSection = canvas.getByText(new RegExp(i18n.ts.recentUsed)).parentElement;
+ await expect(recentUsedSection).toBeInTheDocument();
+ if (recentUsedSection == null) throw new Error(); // NOTE: not called
+ await expect(within(recentUsedSection).getByAltText('😀')).toBeInTheDocument();
+ await expect(within(recentUsedSection).queryByAltText('😬')).toEqual(null);
+ },
+ parameters: {
+ layout: 'centered',
+ },
+} satisfies StoryObj<typeof MkEmojiPicker>;
diff --git a/packages/frontend/src/components/MkEmojiPickerDialog.stories.impl.ts b/packages/frontend/src/components/MkEmojiPickerDialog.stories.impl.ts
new file mode 100644
index 0000000000..131087ad45
--- /dev/null
+++ b/packages/frontend/src/components/MkEmojiPickerDialog.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkEmojiPickerDialog from './MkEmojiPickerDialog.vue';
+void MkEmojiPickerDialog;