Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: MkInputをジェネリックコンポーネントにする #15345

Draft
wants to merge 17 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,12 @@ pnpm --filter frontend test
pnpm --filter misskey-js test
```

Backend tests require manual preparation of servers. See the next section for more on this.
Backend tests require manual preparation of servers. See the [Backend](#backend) section for more on this.

### Frontend
There are two types of tests for UI components:
- Tests dealing with the DOM using Testing Library: [`/packages/frontend/test/dom`](/packages/frontend/test/dom)
- Unit tests for Vue components using Vue Test Utils: [`/packages/frontend/test/vue`](/packages/frontend/test/vue)

### Backend
There are three types of test codes for the backend:
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
"@typescript-eslint/parser": "7.17.0",
"@vitest/coverage-v8": "1.6.0",
"@vue/runtime-core": "3.5.12",
"@vue/test-utils": "^2.4.6",
"acorn": "8.14.0",
"cross-env": "7.0.3",
"cypress": "13.15.2",
Expand Down
4 changes: 2 additions & 2 deletions packages/frontend/src/components/MkAntennaEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSpacer :contentMax="700">
<div>
<div class="_gaps_m">
<MkInput v-model="name">
<MkInput v-model="name" type="text">
<template #label>{{ i18n.ts.name }}</template>
</MkInput>
<MkSelect v-model="src">
Expand Down Expand Up @@ -53,6 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { watch, ref } from 'vue';
import * as Misskey from 'misskey-js';
import type { DeepPartial } from '@/scripts/merge.js';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkTextarea from '@/components/MkTextarea.vue';
Expand All @@ -62,7 +63,6 @@ import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
import { deepMerge } from '@/scripts/merge.js';
import type { DeepPartial } from '@/scripts/merge.js';

type PartialAllowedAntenna = Omit<Misskey.entities.Antenna, 'id' | 'createdAt' | 'updatedAt'> & {
id?: string;
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/components/MkAsUi.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-if="c.label" #label>{{ c.label }}</template>
<template v-if="c.caption" #caption>{{ c.caption }}</template>
</MkTextarea>
<MkInput v-else-if="c.type === 'textInput'" :small="size === 'small'" :modelValue="c.default ?? null" @update:modelValue="c.onInput">
<MkInput v-else-if="c.type === 'textInput'" :small="size === 'small'" :modelValue="c.default ?? null" type="text" @update:modelValue="c.onInput">
<template v-if="c.label" #label>{{ c.label }}</template>
<template v-if="c.caption" #caption>{{ c.caption }}</template>
</MkInput>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/

/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { action } from '@storybook/addon-actions';
import { expect, userEvent, waitFor, within } from '@storybook/test';
import { StoryObj } from '@storybook/vue3';
Expand Down Expand Up @@ -61,7 +60,7 @@ const common = {
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"/>',
template: '<MkInput v-model="q" ref="input" @vue:mounted="inputMounted" type="text"/><story v-if="textarea" :q="q" :textarea="textarea"/>',
}),
],
parameters: {
Expand Down
49 changes: 36 additions & 13 deletions packages/frontend/src/components/MkInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,17 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</template>

<script lang="ts" setup>
import { onMounted, onUnmounted, nextTick, ref, shallowRef, watch, computed, toRefs, InputHTMLAttributes } from 'vue';
<script lang="ts" setup generic="T extends InputTypeHTMLAttribute">
import { onMounted, onUnmounted, nextTick, ref, shallowRef, watch, computed, toRefs, InputTypeHTMLAttribute, InputHTMLAttributes } from 'vue';
import { debounce } from 'throttle-debounce';
import MkButton from '@/components/MkButton.vue';
import { useInterval } from '@@/js/use-interval.js';
import MkButton from '@/components/MkButton.vue';
import { i18n } from '@/i18n.js';
import { Autocomplete, SuggestionType } from '@/scripts/autocomplete.js';

const props = defineProps<{
interface MkInputProps {
modelValue: string | number | null;
type?: InputHTMLAttributes['type'];
type?: T;
required?: boolean;
readonly?: boolean;
disabled?: boolean;
Expand All @@ -74,17 +74,40 @@ const props = defineProps<{
manualSave?: boolean;
small?: boolean;
large?: boolean;
}>();
}

type MkInputValue = T extends 'number' ? number : string;

const props = defineProps<MkInputProps>();

const emit = defineEmits<{
(ev: 'change', _ev: KeyboardEvent): void;
(ev: 'keydown', _ev: KeyboardEvent): void;
(ev: 'enter', _ev: KeyboardEvent): void;
(ev: 'update:modelValue', value: string | number): void;
(ev: 'update:modelValue', value: MkInputValue): void;
}>();

const { modelValue, type, autofocus } = toRefs(props);
const v = ref(modelValue.value);
const {
modelValue,
type,
required,
readonly,
disabled,
pattern,
placeholder,
autofocus,
autocomplete,
autocapitalize,
spellcheck,
inputmode,
step,
datalist,
min,
max,
inline,
manualSave,
} = toRefs<MkInputProps>(props);
const v = ref<string | number | null>(modelValue.value);
const id = Math.random().toString(); // TODO: uuid?
const focused = ref(false);
const changed = ref(false);
Expand Down Expand Up @@ -117,10 +140,10 @@ const onKeydown = (ev: KeyboardEvent) => {

const updated = () => {
changed.value = false;
if (type.value === 'number') {
emit('update:modelValue', typeof v.value === 'number' ? v.value : parseFloat(v.value ?? '0'));
if (type?.value === 'number') {
emit('update:modelValue', (typeof v.value === 'number' ? v.value : parseFloat((v.value as string | null) ?? '0')) as MkInputValue);
} else {
emit('update:modelValue', v.value ?? '');
emit('update:modelValue', (v.value ?? '') as MkInputValue);
}
};

Expand Down Expand Up @@ -164,7 +187,7 @@ useInterval(() => {

onMounted(() => {
nextTick(() => {
if (autofocus.value) {
if (autofocus?.value) {
focus();
}
});
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/components/MkPollEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</p>
<ul>
<li v-for="(choice, i) in choices" :key="i">
<MkInput class="input" small :modelValue="choice" :placeholder="i18n.tsx._poll.choiceN({ n: i + 1 })" @update:modelValue="onInput(i, $event)">
<MkInput class="input" small :modelValue="choice" :placeholder="i18n.tsx._poll.choiceN({ n: i + 1 })" type="text" @update:modelValue="onInput(i, $event)">
</MkInput>
<button class="_button" @click="remove(i)">
<i class="ti ti-x"></i>
Expand Down
8 changes: 4 additions & 4 deletions packages/frontend/src/components/MkPreview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div :class="$style.preview">
<div>
<MkInput v-model="text">
<MkInput v-model="text" type="text">
<template #label>Text</template>
</MkInput>
<MkSwitch v-model="flag" :class="$style.preview__content1__switch_button">
Expand All @@ -18,8 +18,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkRadio v-model="radio" value="pleroma">Pleroma</MkRadio>
</div>
<div :class="$style.preview__content1__button">
<MkButton inline>This is</MkButton>
<MkButton inline primary>the button</MkButton>
<MkButton inline>This is</MkButton>
<MkButton inline primary>the button</MkButton>
</div>
</div>
<div :class="$style.preview__content2" style="pointer-events: none;">
Expand All @@ -36,13 +36,13 @@ SPDX-License-Identifier: AGPL-3.0-only

<script lang="ts" setup>
import { ref } from 'vue';
import * as config from '@@/js/config.js';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import MkRadio from '@/components/MkRadio.vue';
import * as os from '@/os.js';
import * as config from '@@/js/config.js';
import { $i } from '@/account.js';

const text = ref('');
Expand Down
6 changes: 3 additions & 3 deletions packages/frontend/src/components/MkSystemWebhookEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSpacer :marginMin="20" :marginMax="28" style="flex-grow: 1;">
<MkLoading v-if="loading !== 0"/>
<div v-else :class="$style.root" class="_gaps_m">
<MkInput v-model="title">
<MkInput v-model="title" type="text">
<template #label>{{ i18n.ts._webhookSettings.name }}</template>
</MkInput>
<MkInput v-model="url">
<MkInput v-model="url" type="text">
<template #label>URL</template>
</MkInput>
<MkInput v-model="secret">
<MkInput v-model="secret" type="text">
<template #label>{{ i18n.ts._webhookSettings.secret }}</template>
</MkInput>
<MkFolder :defaultOpen="true">
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/components/MkTokenGenerateWindow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkInfo warn>{{ information }}</MkInfo>
</div>
<div>
<MkInput v-model="name">
<MkInput v-model="name" type="text">
<template #label>{{ i18n.ts.name }}</template>
</MkInput>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div>
<MkSpacer :marginMin="20" :marginMax="28">
<div class="_gaps_m">
<MkInput v-model="title">
<MkInput v-model="title" type="text">
<template #label>{{ i18n.ts.title }}</template>
</MkInput>
<MkTextarea v-model="text">
Expand Down
8 changes: 4 additions & 4 deletions packages/frontend/src/components/MkUserSelectDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #header>{{ i18n.ts.selectUser }}</template>
<div>
<div :class="$style.form">
<MkInput v-if="computedLocalOnly" v-model="username" :autofocus="true" @update:modelValue="search">
<MkInput v-if="computedLocalOnly" v-model="username" :autofocus="true" type="text" @update:modelValue="search">
<template #label>{{ i18n.ts.username }}</template>
<template #prefix>@</template>
</MkInput>
<FormSplit v-else :minWidth="170">
<MkInput v-model="username" :autofocus="true" @update:modelValue="search">
<MkInput v-model="username" :autofocus="true" type="text" @update:modelValue="search">
<template #label>{{ i18n.ts.username }}</template>
<template #prefix>@</template>
</MkInput>
<MkInput v-model="host" :datalist="[hostname]" @update:modelValue="search">
<MkInput v-model="host" :datalist="[hostname]" type="text" @update:modelValue="search">
<template #label>{{ i18n.ts.host }}</template>
<template #prefix>@</template>
</MkInput>
Expand Down Expand Up @@ -63,6 +63,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onMounted, ref, computed, shallowRef } from 'vue';
import * as Misskey from 'misskey-js';
import { host as currentHost, hostname } from '@@/js/config.js';
import MkInput from '@/components/MkInput.vue';
import FormSplit from '@/components/form/split.vue';
import MkModalWindow from '@/components/MkModalWindow.vue';
Expand All @@ -71,7 +72,6 @@ import { defaultStore } from '@/store.js';
import { i18n } from '@/i18n.js';
import { $i } from '@/account.js';
import { instance } from '@/instance.js';
import { host as currentHost, hostname } from '@@/js/config.js';

const emit = defineEmits<{
(ev: 'ok', selected: Misskey.entities.UserDetailed): void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</FormSlot>

<MkInput v-model="name" :max="30" manualSave data-cy-user-setup-user-name>
<MkInput v-model="name" :max="30" manualSave data-cy-user-setup-user-name type="text">
<template #label>{{ i18n.ts._profile.name }}</template>
</MkInput>

Expand Down Expand Up @@ -49,7 +49,7 @@ const description = ref($i.description ?? '');
watch(name, () => {
os.apiWithDialog('i/update', {
// 空文字列をnullにしたいので??は使うな
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing

name: name.value || null,
}, undefined, {
'0b3f9f6a-2f4d-4b1f-9fb4-49d3a2fd7191': {
Expand All @@ -62,7 +62,7 @@ watch(name, () => {
watch(description, () => {
os.apiWithDialog('i/update', {
// 空文字列をnullにしたいので??は使うな
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing

description: description.value || null,
});
});
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/pages/about.emojis.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton v-if="$i && ($i.isModerator || $i.policies.canManageCustomEmojis)" primary link to="/custom-emojis-manager">{{ i18n.ts.manageCustomEmojis }}</MkButton>

<div class="query">
<MkInput v-model="q" class="" :placeholder="i18n.ts.search" autocapitalize="off">
<MkInput v-model="q" class="" :placeholder="i18n.ts.search" autocapitalize="off" type="text">
<template #prefix><i class="ti ti-search"></i></template>
</MkInput>

Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/pages/about.federation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div class="_gaps">
<div>
<MkInput v-model="host" :debounce="true" class="">
<MkInput v-model="host" :debounce="true" class="" type="text">
<template #prefix><i class="ti ti-search"></i></template>
<template #label>{{ i18n.ts.host }}</template>
</MkInput>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="loading === 0" style="display: flex; flex-direction: column; min-height: 100%;">
<MkSpacer :marginMin="20" :marginMax="28" style="flex-grow: 1;">
<div :class="$style.root" class="_gaps_m">
<MkInput v-model="title">
<MkInput v-model="title" type="text">
<template #label>{{ i18n.ts.title }}</template>
</MkInput>
<MkSelect v-model="method">
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/pages/admin/announcements.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>

<div class="_gaps">
<MkInput v-model="announcement.title">
<MkInput v-model="announcement.title" type="text">
<template #label>{{ i18n.ts.title }}</template>
</MkInput>
<MkTextarea v-model="announcement.text" mfmAutocomplete :mfmPreview="true">
Expand Down
Loading
Loading