diff --git a/packages/frontend/.storybook/fakes.ts b/packages/frontend/.storybook/fakes.ts
index 5fd21cdf0..b9558948a 100644
--- a/packages/frontend/.storybook/fakes.ts
+++ b/packages/frontend/.storybook/fakes.ts
@@ -17,6 +17,27 @@ export function abuseUserReport() {
};
}
+export function channel() {
+ return {
+ id: 'somechannelid',
+ createdAt: '2016-12-28T22:49:51.000Z',
+ lastNotedAt: '2023-01-01T00:00:00.000Z',
+ name: 'AKANEāCHANNEL',
+ description: 'sweetie sweet',
+ userId: 'someuserid',
+ bannerUrl: 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true',
+ pinnedNoteIds: ['somenoteid'],
+ color: '#eb613f',
+ isArchived: false,
+ usersCount: 16,
+ notesCount: 1024,
+ isFollowing: false,
+ isFavorited: false,
+ hasUnreadNote: false,
+ pinnedNotes: [note()],
+ }
+}
+
export function galleryPost(isSensitive = false) {
return {
id: 'somepostid',
@@ -60,6 +81,31 @@ export function file(isSensitive = false) {
};
}
+export function note() {
+ return {
+ id: 'somenoteid',
+ createdAt: '2016-12-28T22:49:51.000Z',
+ userId: 'someuserid',
+ user: userDetailed(),
+ text: 'make some noise',
+ cw: 'sing along',
+ visibility: 'public',
+ localOnly: false,
+ reactionAcceptance: null,
+ renoteCount: 4,
+ repliesCount: 2,
+ reactions: {
+ 'š': 16,
+ ':yo@.:': 8,
+ },
+ reactionEmojis: {},
+ fileIds: [],
+ files: [],
+ replyId: null,
+ renoteId: null,
+ }
+}
+
export function userDetailed(id = 'someuserid', username = 'miskist', host = 'misskey-hub.net', name = 'Misskey User'): entities.UserDetailed {
return {
id,
diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx
index f44242210..4a4fbb76c 100644
--- a/packages/frontend/.storybook/generate.tsx
+++ b/packages/frontend/.storybook/generate.tsx
@@ -396,7 +396,7 @@ function toStories(component: string): string {
// glob('src/{components,pages,ui,widgets}/**/*.vue')
Promise.all([
glob('src/components/global/*.vue'),
- glob('src/components/Mk{A,B}*.vue'),
+ glob('src/components/Mk{A,B,C}*.vue'),
glob('src/components/MkDigitalClock.vue'),
glob('src/components/MkGalleryPostPreview.vue'),
glob('src/components/MkSignupServerRules.vue'),
diff --git a/packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts b/packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts
new file mode 100644
index 000000000..1c856b77b
--- /dev/null
+++ b/packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts
@@ -0,0 +1,109 @@
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+import { action } from '@storybook/addon-actions';
+import { userEvent, waitFor, within } from '@storybook/testing-library';
+import { StoryObj } from '@storybook/vue3';
+import { rest } from 'msw';
+import { channel } from '../../.storybook/fakes';
+import { commonHandlers } from '../../.storybook/mocks';
+import MkChannelFollowButton from './MkChannelFollowButton.vue';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkChannelFollowButton,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ events() {
+ return {
+
+ };
+ },
+ },
+ template: '',
+ };
+ },
+ args: {
+ channel: channel(),
+ },
+ parameters: {
+ layout: 'centered',
+ msw: {
+ handlers: [
+ ...commonHandlers,
+ rest.post('/api/channels/follow', async (req, res, ctx) => {
+ action('POST /api/channels/follow')(await req.json());
+ return res(ctx.status(204));
+ }),
+ rest.post('/api/channels/unfollow', async (req, res, ctx) => {
+ action('POST /api/channels/unfollow')(await req.json());
+ return res(ctx.status(204));
+ }),
+ ],
+ },
+ },
+} satisfies StoryObj;
+export const Following = {
+ ...Default,
+ async play({ canvasElement }) {
+ const canvas = within(canvasElement);
+ const button = canvas.getByRole('button');
+ await waitFor(() => userEvent.click(button));
+ },
+ parameters: {
+ ...Default.parameters,
+ msw: {
+ handlers: [
+ ...commonHandlers,
+ rest.post('/api/channels/follow', async (req, res, ctx) => {
+ action('POST /api/channels/follow')(await req.json());
+ await new Promise(() => {});
+ }),
+ rest.post('/api/channels/unfollow', async (req, res, ctx) => {
+ action('POST /api/channels/unfollow')(await req.json());
+ await new Promise(() => {});
+ }),
+ ],
+ },
+ },
+}
+export const Followed = {
+ ...Default,
+ args: {
+ ...Default.args,
+ channel: {
+ ...channel(),
+ isFollowing: true,
+ },
+ },
+} satisfies StoryObj;
+export const Full = {
+ ...Default,
+ args: {
+ ...Default.args,
+ full: true,
+ },
+} satisfies StoryObj;
+export const FullFollowing = {
+ ...Following,
+ args: {
+ ...Following.args,
+ full: true,
+ },
+} satisfies StoryObj;
+export const FullFollowed = {
+ ...Followed,
+ args: {
+ ...Followed.args,
+ full: true,
+ },
+} satisfies StoryObj;