diff --git a/packages/frontend/.storybook/mocks.ts b/packages/frontend/.storybook/mocks.ts index ab19604a6..db9222f0d 100644 --- a/packages/frontend/.storybook/mocks.ts +++ b/packages/frontend/.storybook/mocks.ts @@ -1,7 +1,7 @@ import { type SharedOptions, rest } from 'msw'; export const onUnhandledRequest = ((req, print) => { - if (req.url.hostname !== 'localhost' || /^\/(?:client-assets\/|fluent-emojis?\/|iframe.html$|node_modules\/|sb-|static-assets\/|vite\/)/.test(req.url.pathname)) { + if (req.url.hostname !== 'localhost' || /^\/(?:client-assets\/|fluent-emojis?\/|iframe.html$|node_modules\/|src\/|sb-|static-assets\/|vite\/)/.test(req.url.pathname)) { return } print.warning() diff --git a/packages/frontend/src/components/global/MkA.stories.impl.ts b/packages/frontend/src/components/global/MkA.stories.impl.ts index 780aa5537..3afec7f81 100644 --- a/packages/frontend/src/components/global/MkA.stories.impl.ts +++ b/packages/frontend/src/components/global/MkA.stories.impl.ts @@ -2,8 +2,8 @@ import { expect } from '@storybook/jest'; import { userEvent, within } from '@storybook/testing-library'; import { StoryObj } from '@storybook/vue3'; -import { tick } from '@/scripts/test-utils'; import MkA from './MkA.vue'; +import { tick } from '@/scripts/test-utils'; export const Default = { render(args) { return { diff --git a/packages/frontend/src/components/global/MkAd.stories.impl.ts b/packages/frontend/src/components/global/MkAd.stories.impl.ts index 436744b4f..68f299564 100644 --- a/packages/frontend/src/components/global/MkAd.stories.impl.ts +++ b/packages/frontend/src/components/global/MkAd.stories.impl.ts @@ -82,7 +82,7 @@ export const Square = { 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true', }, }, -}; +} satisfies StoryObj<typeof MkAd>; export const Horizontal = { ...common, args: { @@ -94,7 +94,7 @@ export const Horizontal = { 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true', }, }, -}; +} satisfies StoryObj<typeof MkAd>; export const HorizontalBig = { ...common, args: { @@ -106,7 +106,7 @@ export const HorizontalBig = { 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true', }, }, -}; +} satisfies StoryObj<typeof MkAd>; export const ZeroRatio = { ...Square, args: { @@ -117,4 +117,4 @@ export const ZeroRatio = { }, __hasReduce: false, }, -}; +} satisfies StoryObj<typeof MkAd>; diff --git a/packages/frontend/src/components/global/MkAvatar.stories.impl.ts b/packages/frontend/src/components/global/MkAvatar.stories.impl.ts index 2e7ba29d1..d83164ac5 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-duplicates */ import { StoryObj } from '@storybook/vue3'; import { userDetailed } from '../../../.storybook/fakes'; import MkAvatar from './MkAvatar.vue'; @@ -44,7 +43,7 @@ export const ProfilePage = { size: 120, indicator: true, }, -}; +} satisfies StoryObj<typeof MkAvatar>; export const ProfilePageCat = { ...ProfilePage, args: { @@ -54,4 +53,4 @@ export const ProfilePageCat = { isCat: true, }, }, -}; +} satisfies StoryObj<typeof MkAvatar>; diff --git a/packages/frontend/src/components/global/MkCustomEmoji.stories.impl.ts b/packages/frontend/src/components/global/MkCustomEmoji.stories.impl.ts index b31b303e7..e91fc4e2e 100644 --- a/packages/frontend/src/components/global/MkCustomEmoji.stories.impl.ts +++ b/packages/frontend/src/components/global/MkCustomEmoji.stories.impl.ts @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/explicit-function-return-type */ -/* eslint-disable import/no-duplicates */ import { StoryObj } from '@storybook/vue3'; import MkCustomEmoji from './MkCustomEmoji.vue'; export const Default = { @@ -37,10 +36,10 @@ export const Normal = { ...Default.args, normal: true, }, -}; +} satisfies StoryObj<typeof MkCustomEmoji>; export const Missing = { ...Default, args: { name: Default.args.name, }, -}; +} satisfies StoryObj<typeof MkCustomEmoji>; diff --git a/packages/frontend/src/components/global/MkEmoji.stories.impl.ts b/packages/frontend/src/components/global/MkEmoji.stories.impl.ts index 53adf646f..5baa5c2c8 100644 --- a/packages/frontend/src/components/global/MkEmoji.stories.impl.ts +++ b/packages/frontend/src/components/global/MkEmoji.stories.impl.ts @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/explicit-function-return-type */ -/* eslint-disable import/no-duplicates */ import { StoryObj } from '@storybook/vue3'; import MkEmoji from './MkEmoji.vue'; export const Default = { diff --git a/packages/frontend/src/components/global/MkError.stories.meta.ts b/packages/frontend/src/components/global/MkError.stories.meta.ts index 7c9442196..51d763ada 100644 --- a/packages/frontend/src/components/global/MkError.stories.meta.ts +++ b/packages/frontend/src/components/global/MkError.stories.meta.ts @@ -2,4 +2,4 @@ export const argTypes = { retry: { action: 'retry', }, -} +}; diff --git a/packages/frontend/src/components/global/MkLoading.stories.impl.ts b/packages/frontend/src/components/global/MkLoading.stories.impl.ts index d1e1f33f0..dd22f9245 100644 --- a/packages/frontend/src/components/global/MkLoading.stories.impl.ts +++ b/packages/frontend/src/components/global/MkLoading.stories.impl.ts @@ -34,25 +34,25 @@ export const Inline = { ...Default.args, inline: true, }, -}; +} satisfies StoryObj<typeof MkLoading>; export const Colored = { ...Default, args: { ...Default.args, colored: true, }, -}; +} satisfies StoryObj<typeof MkLoading>; export const Mini = { ...Default, args: { ...Default.args, mini: true, }, -}; +} satisfies StoryObj<typeof MkLoading>; export const Em = { ...Default, args: { ...Default.args, em: true, }, -}; +} satisfies StoryObj<typeof MkLoading>; diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts index 720aaa177..246406169 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts @@ -57,18 +57,18 @@ export const Plain = { ...Default.args, plain: true, }, -}; +} satisfies StoryObj<typeof MkMisskeyFlavoredMarkdown>; export const Nowrap = { ...Default, args: { ...Default.args, nowrap: true, }, -}; +} satisfies StoryObj<typeof MkMisskeyFlavoredMarkdown>; export const IsNotNote = { ...Default, args: { ...Default.args, isNote: false, }, -}; +} satisfies StoryObj<typeof MkMisskeyFlavoredMarkdown>; diff --git a/packages/frontend/src/components/global/MkPageHeader.stories.impl.ts b/packages/frontend/src/components/global/MkPageHeader.stories.impl.ts new file mode 100644 index 000000000..2a37ef1a8 --- /dev/null +++ b/packages/frontend/src/components/global/MkPageHeader.stories.impl.ts @@ -0,0 +1,93 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import { StoryObj } from '@storybook/vue3'; +import MkPageHeader from './MkPageHeader.vue'; +export const Empty = { + render(args) { + return { + components: { + MkPageHeader, + }, + setup() { + return { + args, + }; + }, + computed: { + props() { + return { + ...args, + }; + }, + }, + template: '<MkPageHeader v-bind="props" />', + }; + }, + args: { + tabs: [], + }, + parameters: { + layout: 'centered', + }, +} satisfies StoryObj<typeof MkPageHeader>; +export const OneTab = { + ...Empty, + args: { + ...Empty.args, + tab: 'sometabkey', + tabs: [ + { + key: 'sometabkey', + title: 'Some Tab Title', + }, + ], + }, +} satisfies StoryObj<typeof MkPageHeader>; +export const Icon = { + ...OneTab, + args: { + ...OneTab.args, + tabs: [ + { + ...OneTab.args.tabs[0], + icon: 'ti ti-home', + }, + ], + }, +} satisfies StoryObj<typeof MkPageHeader>; +export const IconOnly = { + ...Icon, + args: { + ...Icon.args, + tabs: [ + { + ...Icon.args.tabs[0], + title: undefined, + iconOnly: true, + }, + ], + }, +} satisfies StoryObj<typeof MkPageHeader>; +export const SomeTabs = { + ...Empty, + args: { + ...Empty.args, + tab: 'princess', + tabs: [ + { + key: 'princess', + title: 'Princess', + icon: 'ti ti-crown', + }, + { + key: 'fairy', + title: 'Fairy', + icon: 'ti ti-snowflake', + }, + { + key: 'angel', + title: 'Angel', + icon: 'ti ti-feather', + }, + ], + }, +} satisfies StoryObj<typeof MkPageHeader>; diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.stories.impl.ts b/packages/frontend/src/components/global/MkPageHeader.tabs.stories.impl.ts new file mode 100644 index 000000000..6d4460d59 --- /dev/null +++ b/packages/frontend/src/components/global/MkPageHeader.tabs.stories.impl.ts @@ -0,0 +1,3 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import MkPageHeader_tabs from './MkPageHeader.tabs.vue'; +void MkPageHeader_tabs; diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue index 42760da08..9e1da64e6 100644 --- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue +++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue @@ -33,14 +33,18 @@ <script lang="ts"> export type Tab = { key: string; - title: string; - icon?: string; - iconOnly?: boolean; onClick?: (ev: MouseEvent) => void; -} & { - iconOnly: true; - iccn: string; -}; +} & ( + | { + iconOnly?: false; + title: string; + icon?: string; + } + | { + iconOnly: true; + icon: string; + } +); </script> <script lang="ts" setup> diff --git a/packages/frontend/src/components/global/MkStickyContainer.stories.impl.ts b/packages/frontend/src/components/global/MkStickyContainer.stories.impl.ts new file mode 100644 index 000000000..97b8cc0c5 --- /dev/null +++ b/packages/frontend/src/components/global/MkStickyContainer.stories.impl.ts @@ -0,0 +1,3 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import MkStickyContainer from './MkStickyContainer.vue'; +void MkStickyContainer; diff --git a/packages/frontend/src/components/global/MkTime.stories.impl.ts b/packages/frontend/src/components/global/MkTime.stories.impl.ts new file mode 100644 index 000000000..fd8e874dc --- /dev/null +++ b/packages/frontend/src/components/global/MkTime.stories.impl.ts @@ -0,0 +1,312 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import { expect } from '@storybook/jest'; +import { StoryObj } from '@storybook/vue3'; +import MkTime from './MkTime.vue'; +import { i18n } from '@/i18n'; +import { dateTimeFormat } from '@/scripts/intl-const'; +const now = new Date('2023-04-01T00:00:00.000Z'); +const future = new Date(8640000000000000); +const oneHourAgo = new Date(now.getTime() - 3600000); +const oneDayAgo = new Date(now.getTime() - 86400000); +const oneWeekAgo = new Date(now.getTime() - 604800000); +const oneMonthAgo = new Date(now.getTime() - 2592000000); +const oneYearAgo = new Date(now.getTime() - 31536000000); +export const Empty = { + render(args) { + return { + components: { + MkTime, + }, + setup() { + return { + args, + }; + }, + computed: { + props() { + return { + ...args, + }; + }, + }, + template: '<MkTime v-bind="props" />', + }; + }, + async play({ canvasElement }) { + await expect(canvasElement).toHaveTextContent(i18n.ts._ago.invalid); + }, + args: { + }, + parameters: { + layout: 'centered', + }, +} satisfies StoryObj<typeof MkTime>; +export const RelativeFuture = { + ...Empty, + async play({ canvasElement }) { + await expect(canvasElement).toHaveTextContent(i18n.ts._ago.future); + }, + args: { + ...Empty.args, + time: future, + }, +} satisfies StoryObj<typeof MkTime>; +export const AbsoluteFuture = { + ...Empty, + async play({ canvasElement, args }) { + await expect(canvasElement).toHaveTextContent(dateTimeFormat.format(args.time)); + }, + args: { + ...Empty.args, + time: future, + mode: 'absolute', + }, +} satisfies StoryObj<typeof MkTime>; +export const DetailFuture = { + ...Empty, + async play(context) { + await AbsoluteFuture.play(context); + await expect(context.canvasElement).toHaveTextContent(' ('); + await RelativeFuture.play(context); + await expect(context.canvasElement).toHaveTextContent(')'); + }, + args: { + ...Empty.args, + time: future, + mode: 'detail', + }, +} satisfies StoryObj<typeof MkTime>; +export const RelativeNow = { + ...Empty, + async play({ canvasElement }) { + await expect(canvasElement).toHaveTextContent(i18n.ts._ago.justNow); + }, + args: { + ...Empty.args, + time: now, + origin: now, + mode: 'relative', + }, +} satisfies StoryObj<typeof MkTime>; +export const AbsoluteNow = { + ...Empty, + async play({ canvasElement, args }) { + await expect(canvasElement).toHaveTextContent(dateTimeFormat.format(args.time)); + }, + args: { + ...Empty.args, + time: now, + origin: now, + mode: 'absolute', + }, +} satisfies StoryObj<typeof MkTime>; +export const DetailNow = { + ...Empty, + async play(context) { + await AbsoluteNow.play(context); + await expect(context.canvasElement).toHaveTextContent(' ('); + await RelativeNow.play(context); + await expect(context.canvasElement).toHaveTextContent(')'); + }, + args: { + ...Empty.args, + time: now, + origin: now, + mode: 'detail', + }, +} satisfies StoryObj<typeof MkTime>; +export const RelativeOneHourAgo = { + ...Empty, + async play({ canvasElement }) { + await expect(canvasElement).toHaveTextContent(i18n.t('_ago.hoursAgo', { n: 1 })); + }, + args: { + ...Empty.args, + time: oneHourAgo, + origin: now, + mode: 'relative', + }, +} satisfies StoryObj<typeof MkTime>; +export const AbsoluteOneHourAgo = { + ...Empty, + async play({ canvasElement, args }) { + await expect(canvasElement).toHaveTextContent(dateTimeFormat.format(args.time)); + }, + args: { + ...Empty.args, + time: oneHourAgo, + origin: now, + mode: 'absolute', + }, +} satisfies StoryObj<typeof MkTime>; +export const DetailOneHourAgo = { + ...Empty, + async play(context) { + await AbsoluteOneHourAgo.play(context); + await expect(context.canvasElement).toHaveTextContent(' ('); + await RelativeOneHourAgo.play(context); + await expect(context.canvasElement).toHaveTextContent(')'); + }, + args: { + ...Empty.args, + time: oneHourAgo, + origin: now, + mode: 'detail', + }, +} satisfies StoryObj<typeof MkTime>; +export const RelativeOneDayAgo = { + ...Empty, + async play({ canvasElement }) { + await expect(canvasElement).toHaveTextContent(i18n.t('_ago.daysAgo', { n: 1 })); + }, + args: { + ...Empty.args, + time: oneDayAgo, + origin: now, + mode: 'relative', + }, +} satisfies StoryObj<typeof MkTime>; +export const AbsoluteOneDayAgo = { + ...Empty, + async play({ canvasElement, args }) { + await expect(canvasElement).toHaveTextContent(dateTimeFormat.format(args.time)); + }, + args: { + ...Empty.args, + time: oneDayAgo, + origin: now, + mode: 'absolute', + }, +} satisfies StoryObj<typeof MkTime>; +export const DetailOneDayAgo = { + ...Empty, + async play(context) { + await AbsoluteOneDayAgo.play(context); + await expect(context.canvasElement).toHaveTextContent(' ('); + await RelativeOneDayAgo.play(context); + await expect(context.canvasElement).toHaveTextContent(')'); + }, + args: { + ...Empty.args, + time: oneDayAgo, + origin: now, + mode: 'detail', + }, +} satisfies StoryObj<typeof MkTime>; +export const RelativeOneWeekAgo = { + ...Empty, + async play({ canvasElement }) { + await expect(canvasElement).toHaveTextContent(i18n.t('_ago.weeksAgo', { n: 1 })); + }, + args: { + ...Empty.args, + time: oneWeekAgo, + origin: now, + mode: 'relative', + }, +} satisfies StoryObj<typeof MkTime>; +export const AbsoluteOneWeekAgo = { + ...Empty, + async play({ canvasElement, args }) { + await expect(canvasElement).toHaveTextContent(dateTimeFormat.format(args.time)); + }, + args: { + ...Empty.args, + time: oneWeekAgo, + origin: now, + mode: 'absolute', + }, +} satisfies StoryObj<typeof MkTime>; +export const DetailOneWeekAgo = { + ...Empty, + async play(context) { + await AbsoluteOneWeekAgo.play(context); + await expect(context.canvasElement).toHaveTextContent(' ('); + await RelativeOneWeekAgo.play(context); + await expect(context.canvasElement).toHaveTextContent(')'); + }, + args: { + ...Empty.args, + time: oneWeekAgo, + origin: now, + mode: 'detail', + }, +} satisfies StoryObj<typeof MkTime>; +export const RelativeOneMonthAgo = { + ...Empty, + async play({ canvasElement }) { + await expect(canvasElement).toHaveTextContent(i18n.t('_ago.monthsAgo', { n: 1 })); + }, + args: { + ...Empty.args, + time: oneMonthAgo, + origin: now, + mode: 'relative', + }, +} satisfies StoryObj<typeof MkTime>; +export const AbsoluteOneMonthAgo = { + ...Empty, + async play({ canvasElement, args }) { + await expect(canvasElement).toHaveTextContent(dateTimeFormat.format(args.time)); + }, + args: { + ...Empty.args, + time: oneMonthAgo, + origin: now, + mode: 'absolute', + }, +} satisfies StoryObj<typeof MkTime>; +export const DetailOneMonthAgo = { + ...Empty, + async play(context) { + await AbsoluteOneMonthAgo.play(context); + await expect(context.canvasElement).toHaveTextContent(' ('); + await RelativeOneMonthAgo.play(context); + await expect(context.canvasElement).toHaveTextContent(')'); + }, + args: { + ...Empty.args, + time: oneMonthAgo, + origin: now, + mode: 'detail', + }, +} satisfies StoryObj<typeof MkTime>; +export const RelativeOneYearAgo = { + ...Empty, + async play({ canvasElement }) { + await expect(canvasElement).toHaveTextContent(i18n.t('_ago.yearsAgo', { n: 1 })); + }, + args: { + ...Empty.args, + time: oneYearAgo, + origin: now, + mode: 'relative', + }, +} satisfies StoryObj<typeof MkTime>; +export const AbsoluteOneYearAgo = { + ...Empty, + async play({ canvasElement, args }) { + await expect(canvasElement).toHaveTextContent(dateTimeFormat.format(args.time)); + }, + args: { + ...Empty.args, + time: oneYearAgo, + origin: now, + mode: 'absolute', + }, +} satisfies StoryObj<typeof MkTime>; +export const DetailOneYearAgo = { + ...Empty, + async play(context) { + await AbsoluteOneYearAgo.play(context); + await expect(context.canvasElement).toHaveTextContent(' ('); + await RelativeOneYearAgo.play(context); + await expect(context.canvasElement).toHaveTextContent(')'); + }, + args: { + ...Empty.args, + time: oneYearAgo, + origin: now, + mode: 'detail', + }, +} satisfies StoryObj<typeof MkTime>; diff --git a/packages/frontend/src/components/global/MkTime.vue b/packages/frontend/src/components/global/MkTime.vue index 3fa8bb9ad..99169512d 100644 --- a/packages/frontend/src/components/global/MkTime.vue +++ b/packages/frontend/src/components/global/MkTime.vue @@ -14,8 +14,10 @@ import { dateTimeFormat } from '@/scripts/intl-const'; const props = withDefaults(defineProps<{ time: Date | string | number | null; + origin?: Date | null; mode?: 'relative' | 'absolute' | 'detail'; }>(), { + origin: null, mode: 'relative', }); @@ -25,7 +27,7 @@ const _time = props.time == null ? NaN : const invalid = Number.isNaN(_time); const absolute = !invalid ? dateTimeFormat.format(_time) : i18n.ts._ago.invalid; -let now = $ref((new Date()).getTime()); +let now = $ref((props.origin ?? new Date()).getTime()); const relative = $computed<string>(() => { if (props.mode === 'absolute') return ''; // absoluteではrelativeを使わないので計算しない if (invalid) return i18n.ts._ago.invalid; @@ -46,7 +48,7 @@ const relative = $computed<string>(() => { let tickId: number; function tick() { - now = (new Date()).getTime(); + now = props.origin ?? (new Date()).getTime(); const ago = (now - _time) / 1000/*ms*/; const next = ago < 60 ? 10000 : ago < 3600 ? 60000 : 180000; diff --git a/packages/frontend/src/components/global/MkUrl.stories.impl.ts b/packages/frontend/src/components/global/MkUrl.stories.impl.ts new file mode 100644 index 000000000..06de1d3e9 --- /dev/null +++ b/packages/frontend/src/components/global/MkUrl.stories.impl.ts @@ -0,0 +1,77 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import { expect } from '@storybook/jest'; +import { userEvent, within } from '@storybook/testing-library'; +import { StoryObj } from '@storybook/vue3'; +import { rest } from 'msw'; +import { commonHandlers } from '../../../.storybook/mocks'; +import MkUrl from './MkUrl.vue'; +export const Default = { + render(args) { + return { + components: { + MkUrl, + }, + setup() { + return { + args, + }; + }, + computed: { + props() { + return { + ...args, + }; + }, + }, + template: '<MkUrl v-bind="props">Text</MkUrl>', + }; + }, + async play({ canvasElement }) { + const canvas = within(canvasElement); + const a = canvas.getByRole<HTMLAnchorElement>('link'); + await expect(a).toHaveAttribute('href', 'https://misskey-hub.net/'); + await userEvent.hover(a); + /* + await tick(); // FIXME: wait for network request + const anchors = canvas.getAllByRole<HTMLAnchorElement>('link'); + const popup = anchors.find(anchor => anchor !== a)!; // eslint-disable-line @typescript-eslint/no-non-null-assertion + await expect(popup).toBeInTheDocument(); + await expect(popup).toHaveAttribute('href', 'https://misskey-hub.net/'); + await expect(popup).toHaveTextContent('Misskey Hub'); + await expect(popup).toHaveTextContent('Misskeyはオープンソースの分散型ソーシャルネットワーキングプラットフォームです。'); + await expect(popup).toHaveTextContent('misskey-hub.net'); + const icon = within(popup).getByRole('img'); + await expect(icon).toBeInTheDocument(); + await expect(icon).toHaveAttribute('src', 'https://misskey-hub.net/favicon.ico'); + */ + await userEvent.unhover(a); + }, + args: { + url: 'https://misskey-hub.net/', + }, + parameters: { + layout: 'centered', + msw: { + handlers: [ + ...commonHandlers, + rest.get('/url', (req, res, ctx) => { + return res(ctx.json({ + title: 'Misskey Hub', + icon: 'https://misskey-hub.net/favicon.ico', + description: 'Misskeyはオープンソースの分散型ソーシャルネットワーキングプラットフォームです。', + thumbnail: null, + player: { + url: null, + width: null, + height: null, + allow: [], + }, + sitename: 'misskey-hub.net', + sensitive: false, + url: 'https://misskey-hub.net/', + })); + }), + ], + }, + }, +} satisfies StoryObj<typeof MkUrl>; diff --git a/packages/frontend/src/components/global/MkUserName.stories.impl.ts b/packages/frontend/src/components/global/MkUserName.stories.impl.ts new file mode 100644 index 000000000..37d895d04 --- /dev/null +++ b/packages/frontend/src/components/global/MkUserName.stories.impl.ts @@ -0,0 +1,57 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import { expect } from '@storybook/jest'; +import { userEvent, within } from '@storybook/testing-library'; +import { StoryObj } from '@storybook/vue3'; +import { userDetailed } from '../../../.storybook/fakes'; +import MkUserName from './MkUserName.vue'; +export const Default = { + render(args) { + return { + components: { + MkUserName, + }, + setup() { + return { + args, + }; + }, + computed: { + props() { + return { + ...args, + }; + }, + }, + template: '<MkUserName v-bind="props"/>', + }; + }, + async play({ canvasElement }) { + await expect(canvasElement).toHaveTextContent(userDetailed.name); + }, + args: { + user: userDetailed, + }, + parameters: { + layout: 'centered', + }, +} satisfies StoryObj<typeof MkUserName>; +export const Anonymous = { + ...Default, + async play({ canvasElement }) { + await expect(canvasElement).toHaveTextContent(userDetailed.username); + }, + args: { + ...Default.args, + user: { + ...userDetailed, + name: null, + }, + }, +} satisfies StoryObj<typeof MkUserName>; +export const Wrap = { + ...Default, + args: { + ...Default.args, + nowrap: false, + }, +} satisfies StoryObj<typeof MkUserName>; diff --git a/packages/frontend/src/components/global/RouterView.stories.impl.ts b/packages/frontend/src/components/global/RouterView.stories.impl.ts new file mode 100644 index 000000000..7910b8b3c --- /dev/null +++ b/packages/frontend/src/components/global/RouterView.stories.impl.ts @@ -0,0 +1,3 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import RouterView from './RouterView.vue'; +void RouterView;