* chore(#10336): register snippets
* test(#10336): add `components/Mk[A-B].*` stories
* build: desynced lockfile
* ci(#10336): preload assets
* ci(#10336): use pull_request
* build: update lockfile
* fix: reactivity transform
* chore: track upstream changes
* refactor: avoid temporary previous tapping declarations
* revert: avoid temporary previous tapping declarations
This reverts commit e649b1b1e6
.
* test: flaky snapshots
* style: import
This commit is contained in:

committed by
GitHub

parent
2a7ba37996
commit
9bb6c536c0
@@ -0,0 +1,49 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { StoryObj } from '@storybook/vue3';
|
||||
import { rest } from 'msw';
|
||||
import { abuseUserReport } from '../../.storybook/fakes';
|
||||
import { commonHandlers } from '../../.storybook/mocks';
|
||||
import MkAbuseReport from './MkAbuseReport.vue';
|
||||
export const Default = {
|
||||
render(args) {
|
||||
return {
|
||||
components: {
|
||||
MkAbuseReport,
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
args,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
props() {
|
||||
return {
|
||||
...this.args,
|
||||
};
|
||||
},
|
||||
events() {
|
||||
return {
|
||||
resolved: action('resolved'),
|
||||
};
|
||||
},
|
||||
},
|
||||
template: '<MkAbuseReport v-bind="props" v-on="events" />',
|
||||
};
|
||||
},
|
||||
args: {
|
||||
report: abuseUserReport(),
|
||||
},
|
||||
parameters: {
|
||||
layout: 'fullscreen',
|
||||
msw: {
|
||||
handlers: [
|
||||
...commonHandlers,
|
||||
rest.post('/api/admin/resolve-abuse-user-report', async (req, res, ctx) => {
|
||||
action('POST /api/admin/resolve-abuse-user-report')(await req.json());
|
||||
return res(ctx.json({}));
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
} satisfies StoryObj<typeof MkAbuseReport>;
|
@@ -0,0 +1,49 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { StoryObj } from '@storybook/vue3';
|
||||
import { rest } from 'msw';
|
||||
import { userDetailed } from '../../.storybook/fakes';
|
||||
import { commonHandlers } from '../../.storybook/mocks';
|
||||
import MkAbuseReportWindow from './MkAbuseReportWindow.vue';
|
||||
export const Default = {
|
||||
render(args) {
|
||||
return {
|
||||
components: {
|
||||
MkAbuseReportWindow,
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
args,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
props() {
|
||||
return {
|
||||
...this.args,
|
||||
};
|
||||
},
|
||||
events() {
|
||||
return {
|
||||
'closed': action('closed'),
|
||||
};
|
||||
},
|
||||
},
|
||||
template: '<MkAbuseReportWindow v-bind="props" v-on="events" />',
|
||||
};
|
||||
},
|
||||
args: {
|
||||
user: userDetailed(),
|
||||
},
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
msw: {
|
||||
handlers: [
|
||||
...commonHandlers,
|
||||
rest.post('/api/users/report-abuse', async (req, res, ctx) => {
|
||||
action('POST /api/users/report-abuse')(await req.json());
|
||||
return res(ctx.json({}));
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
} satisfies StoryObj<typeof MkAbuseReportWindow>;
|
@@ -0,0 +1,33 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import { StoryObj } from '@storybook/vue3';
|
||||
import { userDetailed } from '../../.storybook/fakes';
|
||||
import MkAccountMoved from './MkAccountMoved.vue';
|
||||
export const Default = {
|
||||
render(args) {
|
||||
return {
|
||||
components: {
|
||||
MkAccountMoved,
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
args,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
props() {
|
||||
return {
|
||||
...this.args,
|
||||
};
|
||||
},
|
||||
},
|
||||
template: '<MkAccountMoved v-bind="props" />',
|
||||
};
|
||||
},
|
||||
args: {
|
||||
username: userDetailed().username,
|
||||
host: userDetailed().host,
|
||||
},
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
},
|
||||
} satisfies StoryObj<typeof MkAccountMoved>;
|
@@ -2,7 +2,7 @@
|
||||
<div :class="$style.root">
|
||||
<i class="ti ti-plane-departure" style="margin-right: 8px;"></i>
|
||||
{{ i18n.ts.accountMoved }}
|
||||
<MkMention :class="$style.link" :username="acct" :host="host ?? localHost"/>
|
||||
<MkMention :class="$style.link" :username="username" :host="host ?? localHost"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -12,7 +12,7 @@ import { i18n } from '@/i18n';
|
||||
import { host as localHost } from '@/config';
|
||||
|
||||
defineProps<{
|
||||
acct: string;
|
||||
username: string;
|
||||
host: string;
|
||||
}>();
|
||||
</script>
|
||||
|
@@ -0,0 +1,56 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import { StoryObj } from '@storybook/vue3';
|
||||
import { rest } from 'msw';
|
||||
import { userDetailed } from '../../.storybook/fakes';
|
||||
import { commonHandlers } from '../../.storybook/mocks';
|
||||
import MkAchievements from './MkAchievements.vue';
|
||||
import { ACHIEVEMENT_TYPES } from '@/scripts/achievements';
|
||||
export const Empty = {
|
||||
render(args) {
|
||||
return {
|
||||
components: {
|
||||
MkAchievements,
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
args,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
props() {
|
||||
return {
|
||||
...this.args,
|
||||
};
|
||||
},
|
||||
},
|
||||
template: '<MkAchievements v-bind="props" />',
|
||||
};
|
||||
},
|
||||
args: {
|
||||
user: userDetailed(),
|
||||
},
|
||||
parameters: {
|
||||
layout: 'fullscreen',
|
||||
msw: {
|
||||
handlers: [
|
||||
...commonHandlers,
|
||||
rest.post('/api/users/achievements', (req, res, ctx) => {
|
||||
return res(ctx.json([]));
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
} satisfies StoryObj<typeof MkAchievements>;
|
||||
export const All = {
|
||||
...Empty,
|
||||
parameters: {
|
||||
msw: {
|
||||
handlers: [
|
||||
...commonHandlers,
|
||||
rest.post('/api/users/achievements', (req, res, ctx) => {
|
||||
return res(ctx.json(ACHIEVEMENT_TYPES.map((name) => ({ name, unlockedAt: 0 }))));
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
} satisfies StoryObj<typeof MkAchievements>;
|
@@ -1,6 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import { StoryObj } from '@storybook/vue3';
|
||||
import MkAnalogClock from './MkAnalogClock.vue';
|
||||
import isChromatic from 'chromatic';
|
||||
export const Default = {
|
||||
render(args) {
|
||||
return {
|
||||
@@ -22,6 +23,14 @@ export const Default = {
|
||||
template: '<MkAnalogClock v-bind="props" />',
|
||||
};
|
||||
},
|
||||
args: {
|
||||
now: isChromatic() ? () => new Date('2023-01-01T10:10:30') : undefined,
|
||||
},
|
||||
decorators: [
|
||||
() => ({
|
||||
template: '<div style="container-type:inline-size;height:100%"><div style="height:100cqmin;margin:auto;width:100cqmin"><story/></div></div>',
|
||||
}),
|
||||
],
|
||||
parameters: {
|
||||
layout: 'fullscreen',
|
||||
},
|
||||
|
@@ -99,6 +99,7 @@ const props = withDefaults(defineProps<{
|
||||
graduations?: 'none' | 'dots' | 'numbers';
|
||||
fadeGraduations?: boolean;
|
||||
sAnimation?: 'none' | 'elastic' | 'easeOut';
|
||||
now?: () => Date;
|
||||
}>(), {
|
||||
numbers: false,
|
||||
thickness: 0.1,
|
||||
@@ -107,6 +108,7 @@ const props = withDefaults(defineProps<{
|
||||
graduations: 'dots',
|
||||
fadeGraduations: true,
|
||||
sAnimation: 'elastic',
|
||||
now: () => new Date(),
|
||||
});
|
||||
|
||||
const graduationsMajor = computed(() => {
|
||||
@@ -145,11 +147,17 @@ let disableSAnimate = $ref(false);
|
||||
let sOneRound = false;
|
||||
|
||||
function tick() {
|
||||
const now = new Date();
|
||||
now.setMinutes(now.getMinutes() + (new Date().getTimezoneOffset() + props.offset));
|
||||
const now = props.now();
|
||||
now.setMinutes(now.getMinutes() + now.getTimezoneOffset() + props.offset);
|
||||
const previousS = s;
|
||||
const previousM = m;
|
||||
const previousH = h;
|
||||
s = now.getSeconds();
|
||||
m = now.getMinutes();
|
||||
h = now.getHours();
|
||||
if (previousS === s && previousM === m && previousH === h) {
|
||||
return;
|
||||
}
|
||||
hAngle = Math.PI * (h % (props.twentyfour ? 24 : 12) + (m + s / 60) / 60) / (props.twentyfour ? 12 : 6);
|
||||
mAngle = Math.PI * (m + s / 60) / 30;
|
||||
if (sOneRound) { // 秒針が一周した際のアニメーションをよしなに処理する(これが無いと秒が59->0になったときに期待したアニメーションにならない)
|
||||
|
2
packages/frontend/src/components/MkAsUi.stories.impl.ts
Normal file
2
packages/frontend/src/components/MkAsUi.stories.impl.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import MkAsUi from './MkAsUi.vue';
|
||||
void MkAsUi;
|
176
packages/frontend/src/components/MkAutocomplete.stories.impl.ts
Normal file
176
packages/frontend/src/components/MkAutocomplete.stories.impl.ts
Normal file
@@ -0,0 +1,176 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { expect } from '@storybook/jest';
|
||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
||||
import { StoryObj } from '@storybook/vue3';
|
||||
import { rest } from 'msw';
|
||||
import { userDetailed } from '../../.storybook/fakes';
|
||||
import { commonHandlers } from '../../.storybook/mocks';
|
||||
import MkAutocomplete from './MkAutocomplete.vue';
|
||||
import MkInput from './MkInput.vue';
|
||||
import { tick } from '@/scripts/test-utils';
|
||||
const common = {
|
||||
render(args) {
|
||||
return {
|
||||
components: {
|
||||
MkAutocomplete,
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
args,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
props() {
|
||||
return {
|
||||
...this.args,
|
||||
};
|
||||
},
|
||||
events() {
|
||||
return {
|
||||
open: action('open'),
|
||||
closed: action('closed'),
|
||||
};
|
||||
},
|
||||
},
|
||||
template: '<MkAutocomplete v-bind="props" v-on="events" :textarea="textarea" />',
|
||||
};
|
||||
},
|
||||
args: {
|
||||
close: action('close'),
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
decorators: [
|
||||
(_, context) => ({
|
||||
components: {
|
||||
MkInput,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
q: context.args.q,
|
||||
textarea: null,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
inputMounted() {
|
||||
this.textarea = this.$refs.input.$refs.inputEl;
|
||||
},
|
||||
},
|
||||
template: '<MkInput v-model="q" ref="input" @vue:mounted="inputMounted"/><story v-if="textarea" :q="q" :textarea="textarea"/>',
|
||||
}),
|
||||
],
|
||||
parameters: {
|
||||
controls: {
|
||||
exclude: ['textarea'],
|
||||
},
|
||||
layout: 'centered',
|
||||
chromatic: {
|
||||
// FIXME: flaky
|
||||
disableSnapshot: true,
|
||||
},
|
||||
},
|
||||
} satisfies StoryObj<typeof MkAutocomplete>;
|
||||
export const User = {
|
||||
...common,
|
||||
args: {
|
||||
...common.args,
|
||||
type: 'user',
|
||||
},
|
||||
async play({ canvasElement }) {
|
||||
const canvas = within(canvasElement);
|
||||
const input = canvas.getByRole('combobox');
|
||||
await waitFor(() => userEvent.hover(input));
|
||||
await waitFor(() => userEvent.click(input));
|
||||
await waitFor(() => userEvent.type(input, 'm'));
|
||||
await waitFor(async () => {
|
||||
await userEvent.type(input, ' ', { delay: 256 });
|
||||
await tick();
|
||||
return await expect(canvas.getByRole('list')).toBeInTheDocument();
|
||||
}, { timeout: 16384 });
|
||||
},
|
||||
parameters: {
|
||||
...common.parameters,
|
||||
msw: {
|
||||
handlers: [
|
||||
...commonHandlers,
|
||||
rest.post('/api/users/search-by-username-and-host', (req, res, ctx) => {
|
||||
return res(ctx.json([
|
||||
userDetailed('44', 'mizuki', 'misskey-hub.net', 'Mizuki'),
|
||||
userDetailed('49', 'momoko', 'misskey-hub.net', 'Momoko'),
|
||||
]));
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
export const Hashtag = {
|
||||
...common,
|
||||
args: {
|
||||
...common.args,
|
||||
type: 'hashtag',
|
||||
},
|
||||
async play({ canvasElement }) {
|
||||
const canvas = within(canvasElement);
|
||||
const input = canvas.getByRole('combobox');
|
||||
await waitFor(() => userEvent.hover(input));
|
||||
await waitFor(() => userEvent.click(input));
|
||||
await waitFor(() => userEvent.type(input, '気象'));
|
||||
await waitFor(async () => {
|
||||
await userEvent.type(input, ' ', { delay: 256 });
|
||||
await tick();
|
||||
return await expect(canvas.getByRole('list')).toBeInTheDocument();
|
||||
}, { interval: 256, timeout: 16384 });
|
||||
},
|
||||
parameters: {
|
||||
...common.parameters,
|
||||
msw: {
|
||||
handlers: [
|
||||
...commonHandlers,
|
||||
rest.post('/api/hashtags/search', (req, res, ctx) => {
|
||||
return res(ctx.json([
|
||||
'気象警報注意報',
|
||||
'気象警報',
|
||||
'気象情報',
|
||||
]));
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
export const Emoji = {
|
||||
...common,
|
||||
args: {
|
||||
...common.args,
|
||||
type: 'emoji',
|
||||
},
|
||||
async play({ canvasElement }) {
|
||||
const canvas = within(canvasElement);
|
||||
const input = canvas.getByRole('combobox');
|
||||
await waitFor(() => userEvent.hover(input));
|
||||
await waitFor(() => userEvent.click(input));
|
||||
await waitFor(() => userEvent.type(input, 'smile'));
|
||||
await waitFor(async () => {
|
||||
await userEvent.type(input, ' ', { delay: 256 });
|
||||
await tick();
|
||||
return await expect(canvas.getByRole('list')).toBeInTheDocument();
|
||||
}, { interval: 256, timeout: 16384 });
|
||||
},
|
||||
} satisfies StoryObj<typeof MkAutocomplete>;
|
||||
export const MfmTag = {
|
||||
...common,
|
||||
args: {
|
||||
...common.args,
|
||||
type: 'mfmTag',
|
||||
},
|
||||
async play({ canvasElement }) {
|
||||
const canvas = within(canvasElement);
|
||||
const input = canvas.getByRole('combobox');
|
||||
await waitFor(() => userEvent.hover(input));
|
||||
await waitFor(() => userEvent.click(input));
|
||||
await waitFor(async () => {
|
||||
await tick();
|
||||
return await expect(canvas.getByRole('list')).toBeInTheDocument();
|
||||
}, { interval: 256, timeout: 16384 });
|
||||
},
|
||||
} satisfies StoryObj<typeof MkAutocomplete>;
|
46
packages/frontend/src/components/MkAvatars.stories.impl.ts
Normal file
46
packages/frontend/src/components/MkAvatars.stories.impl.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import { StoryObj } from '@storybook/vue3';
|
||||
import { rest } from 'msw';
|
||||
import { userDetailed } from '../../.storybook/fakes';
|
||||
import { commonHandlers } from '../../.storybook/mocks';
|
||||
import MkAvatars from './MkAvatars.vue';
|
||||
export const Default = {
|
||||
render(args) {
|
||||
return {
|
||||
components: {
|
||||
MkAvatars,
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
args,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
props() {
|
||||
return {
|
||||
...this.args,
|
||||
};
|
||||
},
|
||||
},
|
||||
template: '<MkAvatars v-bind="props" />',
|
||||
};
|
||||
},
|
||||
args: {
|
||||
userIds: ['17', '20', '18'],
|
||||
},
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
msw: {
|
||||
handlers: [
|
||||
...commonHandlers,
|
||||
rest.post('/api/users/show', (req, res, ctx) => {
|
||||
return res(ctx.json([
|
||||
userDetailed('17'),
|
||||
userDetailed('20'),
|
||||
userDetailed('18'),
|
||||
]));
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
} satisfies StoryObj<typeof MkAvatars>;
|
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable import/no-default-export */
|
||||
/* eslint-disable import/no-duplicates */
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { StoryObj } from '@storybook/vue3';
|
||||
import MkButton from './MkButton.vue';
|
||||
export const Default = {
|
||||
@@ -20,11 +20,60 @@ export const Default = {
|
||||
...this.args,
|
||||
};
|
||||
},
|
||||
events() {
|
||||
return {
|
||||
click: action('click'),
|
||||
};
|
||||
},
|
||||
},
|
||||
template: '<MkButton v-bind="props">Text</MkButton>',
|
||||
template: '<MkButton v-bind="props" v-on="events">Text</MkButton>',
|
||||
};
|
||||
},
|
||||
args: {
|
||||
},
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
},
|
||||
} satisfies StoryObj<typeof MkButton>;
|
||||
export const Primary = {
|
||||
...Default,
|
||||
args: {
|
||||
...Default.args,
|
||||
primary: true,
|
||||
},
|
||||
} satisfies StoryObj<typeof MkButton>;
|
||||
export const Gradate = {
|
||||
...Default,
|
||||
args: {
|
||||
...Default.args,
|
||||
gradate: true,
|
||||
},
|
||||
} satisfies StoryObj<typeof MkButton>;
|
||||
export const Rounded = {
|
||||
...Default,
|
||||
args: {
|
||||
...Default.args,
|
||||
rounded: true,
|
||||
},
|
||||
} satisfies StoryObj<typeof MkButton>;
|
||||
export const Danger = {
|
||||
...Default,
|
||||
args: {
|
||||
...Default.args,
|
||||
danger: true,
|
||||
},
|
||||
} satisfies StoryObj<typeof MkButton>;
|
||||
export const Small = {
|
||||
...Default,
|
||||
args: {
|
||||
...Default.args,
|
||||
small: true,
|
||||
},
|
||||
} satisfies StoryObj<typeof MkButton>;
|
||||
export const Large = {
|
||||
...Default,
|
||||
args: {
|
||||
...Default.args,
|
||||
large: true,
|
||||
},
|
||||
} satisfies StoryObj<typeof MkButton>;
|
||||
|
@@ -1,4 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { expect } from '@storybook/jest';
|
||||
import { waitFor } from '@storybook/testing-library';
|
||||
import { StoryObj } from '@storybook/vue3';
|
||||
@@ -20,14 +21,21 @@ export const Default = {
|
||||
...this.args,
|
||||
};
|
||||
},
|
||||
events() {
|
||||
return {
|
||||
retry: action('retry'),
|
||||
};
|
||||
},
|
||||
},
|
||||
template: '<MkError v-bind="props" />',
|
||||
template: '<MkError v-bind="props" v-on="events" />',
|
||||
};
|
||||
},
|
||||
async play({ canvasElement }) {
|
||||
await expect(canvasElement.firstElementChild).not.toBeNull();
|
||||
await waitFor(async () => expect(canvasElement.firstElementChild?.classList).not.toContain('_transition_zoom-enter-active'));
|
||||
},
|
||||
args: {
|
||||
},
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
},
|
||||
|
@@ -8,6 +8,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import isChromatic from 'chromatic/isChromatic';
|
||||
import { onUnmounted } from 'vue';
|
||||
import { i18n } from '@/i18n';
|
||||
import { dateTimeFormat } from '@/scripts/intl-const';
|
||||
@@ -17,7 +18,7 @@ const props = withDefaults(defineProps<{
|
||||
origin?: Date | null;
|
||||
mode?: 'relative' | 'absolute' | 'detail';
|
||||
}>(), {
|
||||
origin: null,
|
||||
origin: isChromatic() ? new Date('2023-04-01T00:00:00Z') : null,
|
||||
mode: 'relative',
|
||||
});
|
||||
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<!-- <div class="punished" v-if="user.isSilenced"><i class="ti ti-alert-triangle" style="margin-right: 8px;"></i> {{ i18n.ts.userSilenced }}</div> -->
|
||||
|
||||
<div class="profile _gaps">
|
||||
<MkAccountMoved v-if="user.movedToUri" :host="user.movedToUri.host" :acct="user.movedToUri.username"/>
|
||||
<MkAccountMoved v-if="user.movedToUri" :host="user.movedToUri.host" :username="user.movedToUri.username"/>
|
||||
<MkRemoteCaution v-if="user.host != null" :href="user.url ?? user.uri!" class="warn"/>
|
||||
|
||||
<div :key="user.id" class="main _panel">
|
||||
|
Reference in New Issue
Block a user