mirror of https://github.com/mastodon/mastodon
				
				
				
			Storybook Helpers (#35158)
							parent
							
								
									0a7418e6d8
								
							
						
					
					
						commit
						c52848b444
					
				@ -0,0 +1,120 @@
 | 
			
		||||
import type { Meta, StoryObj } from '@storybook/react-vite';
 | 
			
		||||
 | 
			
		||||
import { accountFactoryState, relationshipsFactory } from '@/testing/factories';
 | 
			
		||||
 | 
			
		||||
import { Account } from './index';
 | 
			
		||||
 | 
			
		||||
const meta = {
 | 
			
		||||
  title: 'Components/Account',
 | 
			
		||||
  component: Account,
 | 
			
		||||
  argTypes: {
 | 
			
		||||
    id: {
 | 
			
		||||
      type: 'string',
 | 
			
		||||
      description: 'ID of the account to display',
 | 
			
		||||
    },
 | 
			
		||||
    size: {
 | 
			
		||||
      type: 'number',
 | 
			
		||||
      description: 'Size of the avatar in pixels',
 | 
			
		||||
    },
 | 
			
		||||
    hidden: {
 | 
			
		||||
      type: 'boolean',
 | 
			
		||||
      description: 'Whether the account is hidden or not',
 | 
			
		||||
    },
 | 
			
		||||
    minimal: {
 | 
			
		||||
      type: 'boolean',
 | 
			
		||||
      description: 'Whether to display a minimal version of the account',
 | 
			
		||||
    },
 | 
			
		||||
    defaultAction: {
 | 
			
		||||
      type: 'string',
 | 
			
		||||
      control: 'select',
 | 
			
		||||
      options: ['block', 'mute'],
 | 
			
		||||
      description: 'Default action to take on the account',
 | 
			
		||||
    },
 | 
			
		||||
    withBio: {
 | 
			
		||||
      type: 'boolean',
 | 
			
		||||
      description: 'Whether to display the account bio or not',
 | 
			
		||||
    },
 | 
			
		||||
    withMenu: {
 | 
			
		||||
      type: 'boolean',
 | 
			
		||||
      description: 'Whether to display the account menu or not',
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  args: {
 | 
			
		||||
    id: '1',
 | 
			
		||||
    size: 46,
 | 
			
		||||
    hidden: false,
 | 
			
		||||
    minimal: false,
 | 
			
		||||
    defaultAction: 'mute',
 | 
			
		||||
    withBio: false,
 | 
			
		||||
    withMenu: true,
 | 
			
		||||
  },
 | 
			
		||||
  parameters: {
 | 
			
		||||
    state: {
 | 
			
		||||
      accounts: {
 | 
			
		||||
        '1': accountFactoryState(),
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
} satisfies Meta<typeof Account>;
 | 
			
		||||
 | 
			
		||||
export default meta;
 | 
			
		||||
 | 
			
		||||
type Story = StoryObj<typeof meta>;
 | 
			
		||||
 | 
			
		||||
export const Primary: Story = {
 | 
			
		||||
  args: {
 | 
			
		||||
    id: '1',
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const Hidden: Story = {
 | 
			
		||||
  args: {
 | 
			
		||||
    hidden: true,
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const Minimal: Story = {
 | 
			
		||||
  args: {
 | 
			
		||||
    minimal: true,
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const WithBio: Story = {
 | 
			
		||||
  args: {
 | 
			
		||||
    withBio: true,
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const NoMenu: Story = {
 | 
			
		||||
  args: {
 | 
			
		||||
    withMenu: false,
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const Blocked: Story = {
 | 
			
		||||
  args: {
 | 
			
		||||
    defaultAction: 'block',
 | 
			
		||||
  },
 | 
			
		||||
  parameters: {
 | 
			
		||||
    state: {
 | 
			
		||||
      relationships: {
 | 
			
		||||
        '1': relationshipsFactory({
 | 
			
		||||
          blocking: true,
 | 
			
		||||
        }),
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const Muted: Story = {
 | 
			
		||||
  args: {},
 | 
			
		||||
  parameters: {
 | 
			
		||||
    state: {
 | 
			
		||||
      relationships: {
 | 
			
		||||
        '1': relationshipsFactory({
 | 
			
		||||
          muting: true,
 | 
			
		||||
        }),
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
@ -0,0 +1,53 @@
 | 
			
		||||
import { http, HttpResponse } from 'msw';
 | 
			
		||||
import { action } from 'storybook/actions';
 | 
			
		||||
 | 
			
		||||
import { relationshipsFactory } from './factories';
 | 
			
		||||
 | 
			
		||||
export const mockHandlers = {
 | 
			
		||||
  mute: http.post<{ id: string }>('/api/v1/accounts/:id/mute', ({ params }) => {
 | 
			
		||||
    action('muting account')(params);
 | 
			
		||||
    return HttpResponse.json(
 | 
			
		||||
      relationshipsFactory({ id: params.id, muting: true }),
 | 
			
		||||
    );
 | 
			
		||||
  }),
 | 
			
		||||
  unmute: http.post<{ id: string }>(
 | 
			
		||||
    '/api/v1/accounts/:id/unmute',
 | 
			
		||||
    ({ params }) => {
 | 
			
		||||
      action('unmuting account')(params);
 | 
			
		||||
      return HttpResponse.json(
 | 
			
		||||
        relationshipsFactory({ id: params.id, muting: false }),
 | 
			
		||||
      );
 | 
			
		||||
    },
 | 
			
		||||
  ),
 | 
			
		||||
  block: http.post<{ id: string }>(
 | 
			
		||||
    '/api/v1/accounts/:id/block',
 | 
			
		||||
    ({ params }) => {
 | 
			
		||||
      action('blocking account')(params);
 | 
			
		||||
      return HttpResponse.json(
 | 
			
		||||
        relationshipsFactory({ id: params.id, blocking: true }),
 | 
			
		||||
      );
 | 
			
		||||
    },
 | 
			
		||||
  ),
 | 
			
		||||
  unblock: http.post<{ id: string }>(
 | 
			
		||||
    '/api/v1/accounts/:id/unblock',
 | 
			
		||||
    ({ params }) => {
 | 
			
		||||
      action('unblocking account')(params);
 | 
			
		||||
      return HttpResponse.json(
 | 
			
		||||
        relationshipsFactory({
 | 
			
		||||
          id: params.id,
 | 
			
		||||
          blocking: false,
 | 
			
		||||
        }),
 | 
			
		||||
      );
 | 
			
		||||
    },
 | 
			
		||||
  ),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const unhandledRequestHandler = ({ url }: Request) => {
 | 
			
		||||
  const { pathname } = new URL(url);
 | 
			
		||||
  if (pathname.startsWith('/api/v1/')) {
 | 
			
		||||
    action(`unhandled request to ${pathname}`)(url);
 | 
			
		||||
    console.warn(
 | 
			
		||||
      `Unhandled request to ${pathname}. Please add a handler for this request in your storybook configuration.`,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
@ -0,0 +1,70 @@
 | 
			
		||||
import type { ApiRelationshipJSON } from '@/mastodon/api_types/relationships';
 | 
			
		||||
import { createAccountFromServerJSON } from '@/mastodon/models/account';
 | 
			
		||||
import type { ApiAccountJSON } from 'mastodon/api_types/accounts';
 | 
			
		||||
 | 
			
		||||
type FactoryOptions<T> = {
 | 
			
		||||
  id?: string;
 | 
			
		||||
} & Partial<T>;
 | 
			
		||||
 | 
			
		||||
type FactoryFunction<T> = (options?: FactoryOptions<T>) => T;
 | 
			
		||||
 | 
			
		||||
export const accountFactory: FactoryFunction<ApiAccountJSON> = ({
 | 
			
		||||
  id,
 | 
			
		||||
  ...data
 | 
			
		||||
} = {}) => ({
 | 
			
		||||
  id: id ?? '1',
 | 
			
		||||
  acct: 'testuser',
 | 
			
		||||
  avatar: '/avatars/original/missing.png',
 | 
			
		||||
  avatar_static: '/avatars/original/missing.png',
 | 
			
		||||
  username: 'testuser',
 | 
			
		||||
  display_name: 'Test User',
 | 
			
		||||
  bot: false,
 | 
			
		||||
  created_at: '2023-01-01T00:00:00.000Z',
 | 
			
		||||
  discoverable: true,
 | 
			
		||||
  emojis: [],
 | 
			
		||||
  fields: [],
 | 
			
		||||
  followers_count: 0,
 | 
			
		||||
  following_count: 0,
 | 
			
		||||
  group: false,
 | 
			
		||||
  header: '/header.png',
 | 
			
		||||
  header_static: '/header_static.png',
 | 
			
		||||
  indexable: true,
 | 
			
		||||
  last_status_at: '2023-01-01',
 | 
			
		||||
  locked: false,
 | 
			
		||||
  mute_expires_at: null,
 | 
			
		||||
  note: 'This is a test user account.',
 | 
			
		||||
  statuses_count: 0,
 | 
			
		||||
  suspended: false,
 | 
			
		||||
  url: '/@testuser',
 | 
			
		||||
  uri: '/users/testuser',
 | 
			
		||||
  noindex: false,
 | 
			
		||||
  roles: [],
 | 
			
		||||
  hide_collections: false,
 | 
			
		||||
  ...data,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const accountFactoryState = (
 | 
			
		||||
  options: FactoryOptions<ApiAccountJSON> = {},
 | 
			
		||||
) => createAccountFromServerJSON(accountFactory(options));
 | 
			
		||||
 | 
			
		||||
export const relationshipsFactory: FactoryFunction<ApiRelationshipJSON> = ({
 | 
			
		||||
  id,
 | 
			
		||||
  ...data
 | 
			
		||||
} = {}) => ({
 | 
			
		||||
  id: id ?? '1',
 | 
			
		||||
  following: false,
 | 
			
		||||
  followed_by: false,
 | 
			
		||||
  blocking: false,
 | 
			
		||||
  blocked_by: false,
 | 
			
		||||
  languages: null,
 | 
			
		||||
  muting_notifications: false,
 | 
			
		||||
  note: '',
 | 
			
		||||
  requested_by: false,
 | 
			
		||||
  muting: false,
 | 
			
		||||
  requested: false,
 | 
			
		||||
  domain_blocking: false,
 | 
			
		||||
  endorsed: false,
 | 
			
		||||
  notifying: false,
 | 
			
		||||
  showing_reblogs: true,
 | 
			
		||||
  ...data,
 | 
			
		||||
});
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue