Reuse <Modal> component

This commit is contained in:
Pig Fang 2019-11-28 21:35:27 +08:00
parent 991475041b
commit aada1148fb
10 changed files with 168 additions and 307 deletions

View File

@ -1,58 +1,38 @@
<template> <template>
<div <modal
id="modal-add-player" id="modal-add-player"
class="modal fade" :title="$t('user.player.add-player')"
tabindex="-1" :ok-button-text="$t('general.submit')"
role="dialog" flex-footer
@confirm="addPlayer"
> >
<div class="modal-dialog"> <table class="table">
<div class="modal-content"> <tbody>
<div class="modal-header"> <tr>
<h4 v-t="'user.player.add-player'" class="modal-title" /> <td v-t="'general.player.player-name'" class="key" />
<button <td class="value">
type="button" <input v-model="name" class="form-control" type="text">
class="close" </td>
data-dismiss="modal" </tr>
aria-label="Close" </tbody>
> </table>
<span aria-hidden="true">&times;</span> <div class="callout callout-info">
</button> <ul class="m-0 p-0 pl-3">
</div> <li>{{ rule }}</li>
<div class="modal-body"> <li>{{ length }}</li>
<table class="table"> </ul>
<tbody>
<tr>
<td v-t="'general.player.player-name'" class="key" />
<td class="value">
<input v-model="name" class="form-control" type="text">
</td>
</tr>
</tbody>
</table>
<div class="callout callout-info">
<ul class="m-0 p-0 pl-3">
<li>{{ rule }}</li>
<li>{{ length }}</li>
</ul>
</div>
</div>
<div class="modal-footer d-flex justify-content-between">
<button class="btn btn-default" data-dismiss="modal">
{{ $t('general.close') }}
</button>
<button class="btn btn-primary" data-test="addPlayer" @click="addPlayer">
{{ $t('general.submit') }}
</button>
</div>
</div>
</div> </div>
</div> </modal>
</template> </template>
<script> <script>
import Modal from './Modal.vue'
export default { export default {
name: 'AddPlayerDialog', name: 'AddPlayerDialog',
components: {
Modal,
},
data() { data() {
return { return {
name: '', name: '',
@ -67,7 +47,6 @@ export default {
{ name: this.name }, { name: this.name },
) )
if (code === 0) { if (code === 0) {
$('#modal-add-player').modal('hide')
this.$message.success(message) this.$message.success(message)
this.$emit('add') this.$emit('add')
} else { } else {

View File

@ -1,61 +1,51 @@
<template> <template>
<div <modal
id="modal-use-as" id="modal-use-as"
class="modal fade" ref="modal"
tabindex="-1" :title="$t('user.closet.use-as.title')"
role="dialog" :ok-button-text="$t('general.submit')"
flex-footer
> >
<div class="modal-dialog"> <template v-if="players.length !== 0">
<div class="modal-content"> <div v-for="player in players" :key="player.pid" class="player-item">
<div class="modal-header"> <label class="model-label" :for="player.pid">
<h4 v-t="'user.closet.use-as.title'" class="modal-title" /> <input
<button v-model="selected"
type="button" type="radio"
class="close" name="player"
data-dismiss="modal" :value="player.pid"
aria-label="Close"
> >
<span aria-hidden="true">&times;</span> <img :src="avatarUrl(player)" width="35" height="35">
</button> <span>{{ player.name }}</span>
</div> </label>
<div class="modal-body">
<template v-if="players.length !== 0">
<div v-for="player in players" :key="player.pid" class="player-item">
<label class="model-label" :for="player.pid">
<input
v-model="selected"
type="radio"
name="player"
:value="player.pid"
>
<img :src="avatarUrl(player)" width="35" height="35">
<span>{{ player.name }}</span>
</label>
</div>
</template>
<p v-else v-t="'user.closet.use-as.empty'" />
</div>
<div class="modal-footer d-flex justify-content-between">
<a
v-if="allowAdd"
v-t="'user.closet.use-as.add'"
data-toggle="modal"
data-target="#modal-add-player"
class="btn btn-default"
href="#"
/>
<button class="btn btn-primary" data-test="submit" @click="submit">
{{ $t('general.submit') }}
</button>
</div>
</div> </div>
</div> </template>
</div> <p v-else v-t="'user.closet.use-as.empty'" />
<template #footer>
<a
v-if="allowAdd"
v-t="'user.closet.use-as.add'"
data-toggle="modal"
data-target="#modal-add-player"
class="btn btn-default"
href="#"
/>
<button class="btn btn-primary" data-test="submit" @click="submit">
{{ $t('general.submit') }}
</button>
</template>
</modal>
</template> </template>
<script> <script>
import $ from 'jquery'
import Modal from './Modal.vue'
export default { export default {
name: 'ApplyToPlayerDialog', name: 'ApplyToPlayerDialog',
components: {
Modal,
},
props: { props: {
skin: Number, skin: Number,
cape: Number, cape: Number,

View File

@ -62,60 +62,35 @@
</template> </template>
</vue-good-table> </vue-good-table>
<div <modal
id="modal-change-texture" id="modal-change-texture"
class="modal fade" :title="$t('admin.changeTexture')"
tabindex="-1" @confirm="changeTexture"
role="dialog"
> >
<div class="modal-dialog"> <div class="form-group">
<div class="modal-content"> <label v-t="'admin.textureType'" />
<div class="modal-header"> <select v-model="textureChanges.type" class="form-control">
<h4 v-t="'admin.changeTexture'" class="modal-title" /> <option v-t="'general.skin'" value="skin" />
<button <option v-t="'general.cape'" value="cape" />
type="button" </select>
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label v-t="'admin.textureType'" />
<select v-model="textureChanges.type" class="form-control">
<option v-t="'general.skin'" value="skin" />
<option v-t="'general.cape'" value="cape" />
</select>
</div>
<div class="form-group">
<label>TID</label>
<input
v-model.number="textureChanges.tid"
class="form-control"
type="text"
:placeholder="$t('admin.pidNotice')"
>
</div>
</div>
<div class="modal-footer d-flex justify-content-between">
<button class="btn btn-default" data-dismiss="modal">
{{ $t('general.close') }}
</button>
<button class="btn btn-primary" data-test="changeTexture" @click="changeTexture">
{{ $t('general.submit') }}
</button>
</div>
</div>
</div> </div>
</div> <div class="form-group">
<label>TID</label>
<input
v-model.number="textureChanges.tid"
class="form-control"
type="text"
:placeholder="$t('admin.pidNotice')"
>
</div>
</modal>
</div> </div>
</template> </template>
<script> <script>
import { VueGoodTable } from 'vue-good-table' import { VueGoodTable } from 'vue-good-table'
import 'vue-good-table/dist/vue-good-table.min.css' import 'vue-good-table/dist/vue-good-table.min.css'
import Modal from '../../components/Modal.vue'
import tableOptions from '../../components/mixins/tableOptions' import tableOptions from '../../components/mixins/tableOptions'
import serverTable from '../../components/mixins/serverTable' import serverTable from '../../components/mixins/serverTable'
import emitMounted from '../../components/mixins/emitMounted' import emitMounted from '../../components/mixins/emitMounted'
@ -123,6 +98,7 @@ import emitMounted from '../../components/mixins/emitMounted'
export default { export default {
name: 'PlayersManagement', name: 'PlayersManagement',
components: { components: {
Modal,
VueGoodTable, VueGoodTable,
}, },
mixins: [ mixins: [
@ -189,7 +165,6 @@ export default {
if (code === 0) { if (code === 0) {
player[`tid_${type}`] = tid player[`tid_${type}`] = tid
this.$message.success(message) this.$message.success(message)
$('.modal').modal('hide')
} else { } else {
this.$message.warning(message) this.$message.warning(message)
} }

View File

@ -49,60 +49,31 @@
</template> </template>
</vue-good-table> </vue-good-table>
<div <modal id="modal-create" :title="$t('user.oauth.create')" @confirm="create">
id="modal-create" <table class="table">
class="modal fade" <tbody>
tabindex="-1" <tr>
role="dialog" <td v-t="'user.oauth.name'" class="key" />
> <td class="value">
<div class="modal-dialog"> <input v-model="name" class="form-control" type="text">
<div class="modal-content"> </td>
<div class="modal-header"> </tr>
<h4 v-t="'user.oauth.create'" class="modal-title" /> <tr>
<button <td v-t="'user.oauth.redirect'" class="key" />
type="button" <td class="value">
class="close" <input v-model="callback" class="form-control" type="text">
data-dismiss="modal" </td>
aria-label="Close" </tr>
> </tbody>
<span aria-hidden="true">&times;</span> </table>
</button> </modal>
</div>
<div class="modal-body">
<table class="table">
<tbody>
<tr>
<td v-t="'user.oauth.name'" class="key" />
<td class="value">
<input v-model="name" class="form-control" type="text">
</td>
</tr>
<tr>
<td v-t="'user.oauth.redirect'" class="key" />
<td class="value">
<input v-model="callback" class="form-control" type="text">
</td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer d-flex justify-content-between">
<button class="btn btn-default" data-dismiss="modal">
{{ $t('general.close') }}
</button>
<button class="btn btn-primary" data-test="create" @click="create">
{{ $t('general.submit') }}
</button>
</div>
</div>
</div>
</div>
</div> </div>
</template> </template>
<script> <script>
import { VueGoodTable } from 'vue-good-table' import { VueGoodTable } from 'vue-good-table'
import 'vue-good-table/dist/vue-good-table.min.css' import 'vue-good-table/dist/vue-good-table.min.css'
import Modal from '../../components/Modal.vue'
import tableOptions from '../../components/mixins/tableOptions' import tableOptions from '../../components/mixins/tableOptions'
import emitMounted from '../../components/mixins/emitMounted' import emitMounted from '../../components/mixins/emitMounted'
import { walkFetch, init } from '../../scripts/net' import { walkFetch, init } from '../../scripts/net'
@ -110,6 +81,7 @@ import { walkFetch, init } from '../../scripts/net'
export default { export default {
name: 'OAuthApps', name: 'OAuthApps',
components: { components: {
Modal,
VueGoodTable, VueGoodTable,
}, },
mixins: [ mixins: [
@ -160,7 +132,6 @@ export default {
redirect: this.callback, redirect: this.callback,
}) })
if (client.id) { if (client.id) {
$('#modal-create').modal('hide')
this.clients.unshift(client) this.clients.unshift(client)
} else { } else {
this.$message.warning(client.message) this.$message.warning(client.message)

View File

@ -114,49 +114,24 @@
<add-player-dialog @add="fetchPlayers" /> <add-player-dialog @add="fetchPlayers" />
<div <modal
id="modal-clear-texture" id="modal-clear-texture"
class="modal fade" :title="$t('user.chooseClearTexture')"
tabindex="-1" @confirm="clearTexture"
role="dialog"
> >
<div class="modal-dialog"> <label class="form-group">
<div class="modal-content"> <input v-model="clear.skin" type="checkbox"> {{ $t('general.skin') }}
<div class="modal-header"> </label>
<h4 v-t="'user.chooseClearTexture'" class="modal-title" /> <br>
<button <label class="form-group">
type="button" <input v-model="clear.cape" type="checkbox"> {{ $t('general.cape') }}
class="close" </label>
data-dismiss="modal" </modal>
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<label class="form-group">
<input v-model="clear.skin" type="checkbox"> {{ $t('general.skin') }}
</label>
<br>
<label class="form-group">
<input v-model="clear.cape" type="checkbox"> {{ $t('general.cape') }}
</label>
</div>
<div class="modal-footer d-flex justify-content-between">
<button class="btn btn-default" data-dismiss="modal">
{{ $t('general.close') }}
</button>
<button class="btn btn-primary" data-test="clearTexture" @click="clearTexture">
{{ $t('general.submit') }}
</button>
</div>
</div>
</div>
</div>
</div> </div>
</template> </template>
<script> <script>
import Modal from '../../components/Modal.vue'
import AddPlayerDialog from '../../components/AddPlayerDialog.vue' import AddPlayerDialog from '../../components/AddPlayerDialog.vue'
import emitMounted from '../../components/mixins/emitMounted' import emitMounted from '../../components/mixins/emitMounted'
@ -164,6 +139,7 @@ export default {
name: 'Players', name: 'Players',
components: { components: {
AddPlayerDialog, AddPlayerDialog,
Modal,
Previewer: () => import('../../components/Previewer.vue'), Previewer: () => import('../../components/Previewer.vue'),
}, },
mixins: [ mixins: [
@ -259,7 +235,6 @@ export default {
this.clear, this.clear,
) )
if (code === 0) { if (code === 0) {
$('.modal').modal('hide')
this.$message.success(message) this.$message.success(message)
const player = this.players.find(({ pid }) => pid === this.selected) const player = this.players.find(({ pid }) => pid === this.selected)
Object.keys(this.clear) Object.keys(this.clear)

View File

@ -157,65 +157,51 @@
</div> </div>
</div> </div>
<div <form data-test="deleteAccount" @submit.prevent="deleteAccount">
id="modal-delete-account" <modal
class="modal fade" id="modal-delete-account"
tabindex="-1" type="danger"
role="dialog" :title="$t('user.profile.delete.modal-title')"
> flex-footer
<form class="modal-dialog" data-test="deleteAccount" @submit.prevent="deleteAccount"> >
<div class="modal-content bg-danger"> <!-- eslint-disable-next-line vue/no-v-html -->
<div class="modal-header"> <div v-once v-html="nl2br($t('user.profile.delete.modal-notice'))" />
<h4 v-t="'user.profile.delete.modal-title'" class="modal-title" /> <br>
<button <input
type="button" v-model="deleteConfirm"
class="close" type="password"
data-dismiss="modal" class="form-control"
aria-label="Close" :placeholder="$t('user.profile.delete.password')"
> required
<span aria-hidden="true">&times;</span> >
</button> <template #footer>
</div> <button
<div class="modal-body"> v-t="'general.close'"
<!-- eslint-disable-next-line vue/no-v-html --> type="button"
<div v-once v-html="nl2br($t('user.profile.delete.modal-notice'))" /> class="btn btn-outline-light"
<br> data-dismiss="modal"
<input />
v-model="deleteConfirm" <button
type="password" v-t="'general.submit'"
class="form-control" type="submit"
:placeholder="$t('user.profile.delete.password')" class="btn btn-outline-light"
required />
> </template>
<br> </modal>
</div> </form>
<div class="modal-footer d-flex justify-content-between">
<button
v-t="'general.close'"
type="button"
class="btn btn-outline-light"
data-dismiss="modal"
/>
<button
v-t="'general.submit'"
type="submit"
class="btn btn-outline-light"
/>
</div>
</div>
</form>
</div>
</div> </div>
</template> </template>
<script> <script>
import EmailVerification from '../../components/EmailVerification.vue' import EmailVerification from '../../components/EmailVerification.vue'
import Modal from '../../components/Modal.vue'
import emitMounted from '../../components/mixins/emitMounted' import emitMounted from '../../components/mixins/emitMounted'
export default { export default {
name: 'Profile', name: 'Profile',
components: { components: {
EmailVerification, EmailVerification,
Modal,
}, },
mixins: [ mixins: [
emitMounted, emitMounted,

View File

@ -1,6 +1,7 @@
import Vue from 'vue' import Vue from 'vue'
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import { flushPromises } from '../utils' import { flushPromises } from '../utils'
import Modal from '@/components/Modal.vue'
import AddPlayerDialog from '@/components/AddPlayerDialog.vue' import AddPlayerDialog from '@/components/AddPlayerDialog.vue'
window.blessing.extra = { window.blessing.extra = {
@ -9,16 +10,15 @@ window.blessing.extra = {
} }
test('add player', async () => { test('add player', async () => {
window.$ = jest.fn(() => ({ modal() {} }))
Vue.prototype.$http.get.mockResolvedValueOnce([]) Vue.prototype.$http.get.mockResolvedValueOnce([])
Vue.prototype.$http.post Vue.prototype.$http.post
.mockResolvedValueOnce({ code: 1, message: 'fail' }) .mockResolvedValueOnce({ code: 1, message: 'fail' })
.mockResolvedValue({ code: 0, message: 'ok' }) .mockResolvedValue({ code: 0, message: 'ok' })
const wrapper = mount(AddPlayerDialog) const wrapper = mount(AddPlayerDialog)
const button = wrapper.find('[data-test=addPlayer]') const modal = wrapper.find(Modal)
wrapper.find('input[type="text"]').setValue('the-new') wrapper.find('input[type="text"]').setValue('the-new')
button.trigger('click') modal.vm.$emit('confirm')
expect(Vue.prototype.$http.post).toBeCalledWith( expect(Vue.prototype.$http.post).toBeCalledWith(
'/user/player/add', '/user/player/add',
{ name: 'the-new' }, { name: 'the-new' },
@ -27,7 +27,7 @@ test('add player', async () => {
expect(wrapper.text()).not.toContain('the-new') expect(wrapper.text()).not.toContain('the-new')
expect(Vue.prototype.$message.warning).toBeCalledWith('fail') expect(Vue.prototype.$message.warning).toBeCalledWith('fail')
button.trigger('click') modal.vm.$emit('confirm')
await flushPromises() await flushPromises()
expect(wrapper.emitted().add).toBeDefined() expect(wrapper.emitted().add).toBeDefined()
expect(Vue.prototype.$message.success).toBeCalledWith('ok') expect(Vue.prototype.$message.success).toBeCalledWith('ok')

View File

@ -2,6 +2,7 @@ import Vue from 'vue'
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import { MessageBoxData } from 'element-ui/types/message-box' import { MessageBoxData } from 'element-ui/types/message-box'
import { flushPromises } from '../../utils' import { flushPromises } from '../../utils'
import Modal from '@/components/Modal.vue'
import Players from '@/views/admin/Players.vue' import Players from '@/views/admin/Players.vue'
test('fetch data after initializing', () => { test('fetch data after initializing', () => {
@ -16,7 +17,6 @@ test('fetch data after initializing', () => {
}) })
test('change texture', async () => { test('change texture', async () => {
window.$ = jest.fn(() => ({ modal() {} }))
Vue.prototype.$http.get.mockResolvedValue({ Vue.prototype.$http.get.mockResolvedValue({
data: [ data: [
{ pid: 1, tid_skin: 0 }, { pid: 1, tid_skin: 0 },
@ -28,12 +28,12 @@ test('change texture', async () => {
const wrapper = mount(Players) const wrapper = mount(Players)
await flushPromises() await flushPromises()
const button = wrapper.find('[data-test=changeTexture]') const modal = wrapper.find(Modal)
wrapper.findAll('.btn-default').trigger('click') wrapper.findAll('.btn-default').trigger('click')
wrapper.find('.modal-body input').setValue('5') wrapper.find('.modal-body input').setValue('5')
button.trigger('click') modal.vm.$emit('confirm')
await flushPromises() await flushPromises()
expect(Vue.prototype.$http.post).toBeCalledWith( expect(Vue.prototype.$http.post).toBeCalledWith(
'/admin/players?action=texture', '/admin/players?action=texture',
@ -41,10 +41,9 @@ test('change texture', async () => {
pid: 1, tid: 5, type: 'skin', pid: 1, tid: 5, type: 'skin',
}, },
) )
button.trigger('click') modal.vm.$emit('confirm')
await flushPromises() await flushPromises()
expect(wrapper.html()).toContain('/preview/64/5.png') expect(wrapper.html()).toContain('/preview/64/5.png')
expect($).toBeCalledWith('.modal')
}) })
test('change player name', async () => { test('change player name', async () => {

View File

@ -3,6 +3,7 @@ import { mount } from '@vue/test-utils'
import { MessageBoxData } from 'element-ui/types/message-box' import { MessageBoxData } from 'element-ui/types/message-box'
import { flushPromises } from '../../utils' import { flushPromises } from '../../utils'
import { walkFetch } from '@/scripts/net' import { walkFetch } from '@/scripts/net'
import Modal from '@/components/Modal.vue'
import OAuth from '@/views/user/OAuth.vue' import OAuth from '@/views/user/OAuth.vue'
jest.mock('@/scripts/net', () => ({ jest.mock('@/scripts/net', () => ({
@ -20,7 +21,6 @@ test('basic render', async () => {
}) })
test('create app', async () => { test('create app', async () => {
Object.assign(window, { $: () => ({ modal() {} }) })
Vue.prototype.$http.get.mockResolvedValue([]) Vue.prototype.$http.get.mockResolvedValue([])
Vue.prototype.$http.post Vue.prototype.$http.post
.mockResolvedValueOnce({ message: 'fail' }) .mockResolvedValueOnce({ message: 'fail' })
@ -28,14 +28,14 @@ test('create app', async () => {
const wrapper = mount(OAuth) const wrapper = mount(OAuth)
await flushPromises() await flushPromises()
const button = wrapper.find('[data-test=create]') const modal = wrapper.find(Modal)
const inputs = wrapper.findAll('.value') const inputs = wrapper.findAll('.value')
inputs.at(0).find('input') inputs.at(0).find('input')
.setValue('name') .setValue('name')
inputs.at(1).find('input') inputs.at(1).find('input')
.setValue('https://example.com/') .setValue('https://example.com/')
button.trigger('click') modal.vm.$emit('confirm')
await flushPromises() await flushPromises()
expect(Vue.prototype.$http.post).toBeCalledWith( expect(Vue.prototype.$http.post).toBeCalledWith(
'/oauth/clients', '/oauth/clients',
@ -43,7 +43,7 @@ test('create app', async () => {
) )
expect(Vue.prototype.$message.warning).toBeCalledWith('fail') expect(Vue.prototype.$message.warning).toBeCalledWith('fail')
button.trigger('click') modal.vm.$emit('confirm')
await flushPromises() await flushPromises()
expect(wrapper.text()).toContain('name') expect(wrapper.text()).toContain('name')
}) })

View File

@ -153,21 +153,7 @@ test('toggle preview mode', () => {
expect(wrapper.text()).toContain('user.player.texture-empty') expect(wrapper.text()).toContain('user.player.texture-empty')
}) })
test('add player', async () => {
window.$ = jest.fn(() => ({ modal() {} }))
Vue.prototype.$http.get.mockResolvedValueOnce({ data: [] })
Vue.prototype.$http.post.mockResolvedValue({ code: 0 })
const wrapper = mount(Players)
const button = wrapper.find('[data-test=addPlayer]')
wrapper.find('input[type="text"]').setValue('the-new')
button.trigger('click')
await flushPromises()
expect(Vue.prototype.$http.get).toBeCalledTimes(2)
})
test('clear texture', async () => { test('clear texture', async () => {
window.$ = jest.fn(() => ({ modal() {} }))
Vue.prototype.$http.get Vue.prototype.$http.get
.mockResolvedValueOnce({ .mockResolvedValueOnce({
data: [ data: [
@ -183,22 +169,22 @@ test('clear texture', async () => {
.mockResolvedValue({ code: 0, message: 'ok' }) .mockResolvedValue({ code: 0, message: 'ok' })
const wrapper = mount(Players) const wrapper = mount(Players)
await flushPromises() await flushPromises()
const button = wrapper.find('[data-test=clearTexture]') const modal = wrapper.find('#modal-clear-texture')
wrapper.find('.player').trigger('click') wrapper.find('.player').trigger('click')
button.trigger('click') modal.vm.$emit('confirm')
expect(Vue.prototype.$http.post).not.toBeCalled() expect(Vue.prototype.$http.post).not.toBeCalled()
wrapper.findAll('input[type="checkbox"]').at(0) wrapper.findAll('input[type="checkbox"]').at(0)
.setChecked() .setChecked()
button.trigger('click') modal.vm.$emit('confirm')
expect(Vue.prototype.$http.post).toBeCalledWith( expect(Vue.prototype.$http.post).toBeCalledWith(
'/user/player/texture/clear/1', '/user/player/texture/clear/1',
{ {
skin: true, cape: false, skin: true, cape: false,
}, },
) )
button.trigger('click') modal.vm.$emit('confirm')
await flushPromises() await flushPromises()
expect(Vue.prototype.$message.success).toBeCalledWith('ok') expect(Vue.prototype.$message.success).toBeCalledWith('ok')
}) })