build(#10336): interactions
This commit is contained in:
@@ -5,9 +5,9 @@ import restart from 'vite-plugin-restart';
|
||||
const config = {
|
||||
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
|
||||
addons: [
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-essentials',
|
||||
'@storybook/addon-interactions',
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-storysource',
|
||||
'../node_modules/storybook-addon-misskey-theme',
|
||||
],
|
||||
@@ -21,6 +21,9 @@ const config = {
|
||||
core: {
|
||||
disableTelemetry: true,
|
||||
},
|
||||
features: {
|
||||
interactionsDebugger: true,
|
||||
},
|
||||
async viteFinal(config, options) {
|
||||
return mergeConfig(config, {
|
||||
plugins: [
|
||||
|
@@ -90,7 +90,9 @@ const preview = {
|
||||
popups: misskeyOS.popups,
|
||||
};
|
||||
},
|
||||
template: '<component :is="popup.component" v-for="popup in popups" :key="popup.id" v-bind="popup.props" v-on="popup.events"/><story />',
|
||||
template:
|
||||
'<component :is="popup.component" v-for="popup in popups" :key="popup.id" v-bind="popup.props" v-on="popup.events"/>' +
|
||||
'<story />',
|
||||
};
|
||||
},
|
||||
],
|
||||
|
@@ -79,11 +79,12 @@
|
||||
"@storybook/addons": "7.0.0-rc.5",
|
||||
"@storybook/blocks": "7.0.0-rc.6",
|
||||
"@storybook/core-events": "7.0.0-rc.6",
|
||||
"@storybook/jest": "0.0.10",
|
||||
"@storybook/manager-api": "7.0.0-rc.6",
|
||||
"@storybook/preview-api": "7.0.0-rc.6",
|
||||
"@storybook/react": "7.0.0-rc.6",
|
||||
"@storybook/react-vite": "7.0.0-rc.6",
|
||||
"@storybook/testing-library": "^0.0.14-next.1",
|
||||
"@storybook/testing-library": "0.0.14-next.1",
|
||||
"@storybook/theming": "7.0.0-rc.6",
|
||||
"@storybook/types": "7.0.0-rc.6",
|
||||
"@storybook/vue3": "7.0.0-rc.6",
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div role="menu">
|
||||
<div
|
||||
ref="itemsEl" v-hotkey="keymap"
|
||||
class="_popup _shadow"
|
||||
@@ -8,37 +8,37 @@
|
||||
@contextmenu.self="e => e.preventDefault()"
|
||||
>
|
||||
<template v-for="(item, i) in items2">
|
||||
<div v-if="item === null" :class="$style.divider"></div>
|
||||
<span v-else-if="item.type === 'label'" :class="[$style.label, $style.item]">
|
||||
<div v-if="item === null" role="separator" :class="$style.divider"></div>
|
||||
<span v-else-if="item.type === 'label'" role="menuitem" :class="[$style.label, $style.item]">
|
||||
<span>{{ item.text }}</span>
|
||||
</span>
|
||||
<span v-else-if="item.type === 'pending'" :tabindex="i" :class="[$style.pending, $style.item]">
|
||||
<span v-else-if="item.type === 'pending'" role="menuitem" :tabindex="i" :class="[$style.pending, $style.item]">
|
||||
<span><MkEllipsis/></span>
|
||||
</span>
|
||||
<MkA v-else-if="item.type === 'link'" :to="item.to" :tabindex="i" class="_button" :class="$style.item" @click.passive="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
|
||||
<MkA v-else-if="item.type === 'link'" role="menuitem" :to="item.to" :tabindex="i" class="_button" :class="$style.item" @click.passive="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
|
||||
<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
|
||||
<MkAvatar v-if="item.avatar" :user="item.avatar" :class="$style.avatar"/>
|
||||
<span>{{ item.text }}</span>
|
||||
<span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span>
|
||||
</MkA>
|
||||
<a v-else-if="item.type === 'a'" :href="item.href" :target="item.target" :download="item.download" :tabindex="i" class="_button" :class="$style.item" @click="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
|
||||
<a v-else-if="item.type === 'a'" role="menuitem" :href="item.href" :target="item.target" :download="item.download" :tabindex="i" class="_button" :class="$style.item" @click="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
|
||||
<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
|
||||
<span>{{ item.text }}</span>
|
||||
<span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span>
|
||||
</a>
|
||||
<button v-else-if="item.type === 'user'" :tabindex="i" class="_button" :class="[$style.item, { [$style.active]: item.active }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
|
||||
<button v-else-if="item.type === 'user'" role="menuitem" :tabindex="i" class="_button" :class="[$style.item, { [$style.active]: item.active }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
|
||||
<MkAvatar :user="item.user" :class="$style.avatar"/><MkUserName :user="item.user"/>
|
||||
<span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span>
|
||||
</button>
|
||||
<span v-else-if="item.type === 'switch'" :tabindex="i" :class="$style.item" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
|
||||
<span v-else-if="item.type === 'switch'" role="menuitemcheckbox" :tabindex="i" :class="$style.item" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
|
||||
<MkSwitch v-model="item.ref" :disabled="item.disabled" class="form-switch">{{ item.text }}</MkSwitch>
|
||||
</span>
|
||||
<button v-else-if="item.type === 'parent'" :tabindex="i" class="_button" :class="[$style.item, $style.parent, { [$style.childShowing]: childShowingItem === item }]" @mouseenter="showChildren(item, $event)">
|
||||
<button v-else-if="item.type === 'parent'" role="menuitem" :tabindex="i" class="_button" :class="[$style.item, $style.parent, { [$style.childShowing]: childShowingItem === item }]" @mouseenter="showChildren(item, $event)">
|
||||
<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
|
||||
<span>{{ item.text }}</span>
|
||||
<span :class="$style.caret"><i class="ti ti-chevron-right ti-fw"></i></span>
|
||||
</button>
|
||||
<button v-else :tabindex="i" class="_button" :class="[$style.item, { [$style.danger]: item.danger, [$style.active]: item.active }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
|
||||
<button v-else :tabindex="i" class="_button" role="menuitem" :class="[$style.item, { [$style.danger]: item.danger, [$style.active]: item.active }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
|
||||
<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
|
||||
<MkAvatar v-if="item.avatar" :user="item.avatar" :class="$style.avatar"/>
|
||||
<span>{{ item.text }}</span>
|
||||
|
46
packages/frontend/src/components/global/MkA.stories.impl.ts
Normal file
46
packages/frontend/src/components/global/MkA.stories.impl.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/* 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 MkA from './MkA.vue';
|
||||
import { tick } from '@/scripts/test-utils';
|
||||
export const Default = {
|
||||
render(args) {
|
||||
return {
|
||||
components: {
|
||||
MkA,
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
args,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
props() {
|
||||
return {
|
||||
...args,
|
||||
};
|
||||
},
|
||||
},
|
||||
template: '<MkA v-bind="props">Text</MkA>',
|
||||
};
|
||||
},
|
||||
args: {
|
||||
to: '#test',
|
||||
},
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
},
|
||||
} satisfies StoryObj<typeof MkA>;
|
||||
export const ContextMenu = {
|
||||
...Default,
|
||||
async play({ canvasElement }) {
|
||||
const canvas = within(canvasElement);
|
||||
const a = canvas.getByRole<HTMLAnchorElement>('link');
|
||||
await expect(a.href).toMatch(/^https?:\/\/.*#test$/);
|
||||
await userEvent.click(a, { button: 2 });
|
||||
await tick();
|
||||
const menu = canvas.getByRole('menu');
|
||||
await expect(menu).toBeInTheDocument();
|
||||
},
|
||||
} satisfies StoryObj<typeof MkA>;
|
@@ -1,11 +1,17 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable import/no-default-export */
|
||||
import { Meta, StoryObj } from '@storybook/vue3';
|
||||
import MkA from './MkA.vue';
|
||||
import { Meta } from '@storybook/vue3';
|
||||
const meta = {
|
||||
title: 'components/global/MkA',
|
||||
component: MkA,
|
||||
} satisfies Meta<typeof MkA>;
|
||||
export default meta;
|
||||
/* 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 MkA from './MkA.vue';
|
||||
import { tick } from '@/scripts/test-utils';
|
||||
export const Default = {
|
||||
render(args) {
|
||||
return {
|
||||
@@ -24,11 +30,22 @@ export const Default = {
|
||||
};
|
||||
},
|
||||
},
|
||||
template: '<MkA v-bind="props" />',
|
||||
template: '<MkA v-bind="props">Text</MkA>',
|
||||
};
|
||||
},
|
||||
async play({ canvasElement }) {
|
||||
const canvas = within(canvasElement);
|
||||
const a = canvas.getByRole<HTMLAnchorElement>('link');
|
||||
await expect(a.href).toMatch(/^https?:\/\/.*#test$/);
|
||||
await userEvent.click(a, { button: 2 });
|
||||
await tick();
|
||||
const menu = canvas.getByRole('menu');
|
||||
await expect(menu).toBeInTheDocument();
|
||||
},
|
||||
args: {
|
||||
to: '#test',
|
||||
},
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
},
|
||||
} satisfies StoryObj<typeof MkA>;
|
||||
export default meta;
|
||||
|
4
packages/frontend/src/scripts/test-utils.ts
Normal file
4
packages/frontend/src/scripts/test-utils.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export async function tick(): Promise<void> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
await new Promise((globalThis.requestIdleCallback ?? setTimeout) as never);
|
||||
}
|
Reference in New Issue
Block a user