Add id to modal.vue to support data-toggle="modal"
This commit is contained in:
parent
b1e1990ee2
commit
2c47f0924f
|
@ -45,11 +45,9 @@ export default {
|
||||||
onLeaveGroup() {
|
onLeaveGroup() {
|
||||||
this.modalStatus = true;
|
this.modalStatus = true;
|
||||||
},
|
},
|
||||||
leaveGroup(leaveConfirmed) {
|
leaveGroup() {
|
||||||
this.modalStatus = false;
|
this.modalStatus = false;
|
||||||
if (leaveConfirmed) {
|
eventHub.$emit('leaveGroup', this.group, this.parentGroup);
|
||||||
eventHub.$emit('leaveGroup', this.group, this.parentGroup);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -32,10 +32,10 @@
|
||||||
methods: {
|
methods: {
|
||||||
createNewItem(type) {
|
createNewItem(type) {
|
||||||
this.modalType = type;
|
this.modalType = type;
|
||||||
this.toggleModalOpen();
|
this.openModal = true;
|
||||||
},
|
},
|
||||||
toggleModalOpen() {
|
hideModal() {
|
||||||
this.openModal = !this.openModal;
|
this.openModal = false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -95,7 +95,7 @@
|
||||||
:branch-id="branch"
|
:branch-id="branch"
|
||||||
:path="path"
|
:path="path"
|
||||||
:parent="parent"
|
:parent="parent"
|
||||||
@toggle="toggleModalOpen"
|
@hide="hideModal"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -43,10 +43,10 @@
|
||||||
type: this.type,
|
type: this.type,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.toggleModalOpen();
|
this.hideModal();
|
||||||
},
|
},
|
||||||
toggleModalOpen() {
|
hideModal() {
|
||||||
this.$emit('toggle');
|
this.$emit('hide');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -86,7 +86,7 @@
|
||||||
:title="modalTitle"
|
:title="modalTitle"
|
||||||
:primary-button-label="buttonLabel"
|
:primary-button-label="buttonLabel"
|
||||||
kind="success"
|
kind="success"
|
||||||
@toggle="toggleModalOpen"
|
@cancel="hideModal"
|
||||||
@submit="createEntryInStore"
|
@submit="createEntryInStore"
|
||||||
>
|
>
|
||||||
<form
|
<form
|
||||||
|
|
|
@ -110,7 +110,7 @@ export default {
|
||||||
kind="primary"
|
kind="primary"
|
||||||
:title="__('Branch has changed')"
|
:title="__('Branch has changed')"
|
||||||
:text="__('This branch has changed since you started editing. Would you like to create a new branch?')"
|
:text="__('This branch has changed since you started editing. Would you like to create a new branch?')"
|
||||||
@toggle="showNewBranchModal = false"
|
@cancel="showNewBranchModal = false"
|
||||||
@submit="makeCommit(true)"
|
@submit="makeCommit(true)"
|
||||||
/>
|
/>
|
||||||
<commit-files-list
|
<commit-files-list
|
||||||
|
|
|
@ -50,7 +50,7 @@ export default {
|
||||||
kind="warning"
|
kind="warning"
|
||||||
:title="__('Are you sure?')"
|
:title="__('Are you sure?')"
|
||||||
:text="__('Are you sure you want to discard your changes?')"
|
:text="__('Are you sure you want to discard your changes?')"
|
||||||
@toggle="closeDiscardPopup"
|
@cancel="closeDiscardPopup"
|
||||||
@submit="toggleEditMode(true)"
|
@submit="toggleEditMode(true)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import modal from '../../../vue_shared/components/modal.vue';
|
import modal from '~/vue_shared/components/modal.vue';
|
||||||
import { __, s__, sprintf } from '../../../locale';
|
import { __, s__, sprintf } from '~/locale';
|
||||||
import csrf from '../../../lib/utils/csrf';
|
import csrf from '~/lib/utils/csrf';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
|
@ -22,7 +22,6 @@
|
||||||
return {
|
return {
|
||||||
enteredPassword: '',
|
enteredPassword: '',
|
||||||
enteredUsername: '',
|
enteredUsername: '',
|
||||||
isOpen: false,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
@ -69,78 +68,58 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
|
||||||
|
|
||||||
return this.enteredUsername === this.username;
|
return this.enteredUsername === this.username;
|
||||||
},
|
},
|
||||||
onSubmit(status) {
|
onSubmit() {
|
||||||
if (status) {
|
this.$refs.form.submit();
|
||||||
if (!this.canSubmit()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$refs.form.submit();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.toggleOpen(false);
|
|
||||||
},
|
|
||||||
toggleOpen(isOpen) {
|
|
||||||
this.isOpen = isOpen;
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<modal
|
||||||
<modal
|
id="delete-account-modal"
|
||||||
v-if="isOpen"
|
:title="s__('Profiles|Delete your account?')"
|
||||||
:title="s__('Profiles|Delete your account?')"
|
:text="text"
|
||||||
:text="text"
|
kind="danger"
|
||||||
:kind="`danger ${!canSubmit() && 'disabled'}`"
|
:primary-button-label="s__('Profiles|Delete account')"
|
||||||
:primary-button-label="s__('Profiles|Delete account')"
|
@submit="onSubmit"
|
||||||
@toggle="toggleOpen"
|
:submit-disabled="!canSubmit()">
|
||||||
@submit="onSubmit">
|
|
||||||
|
|
||||||
<template slot="body" slot-scope="props">
|
<template slot="body" slot-scope="props">
|
||||||
<p v-html="props.text"></p>
|
<p v-html="props.text"></p>
|
||||||
|
|
||||||
<form
|
<form
|
||||||
ref="form"
|
ref="form"
|
||||||
:action="actionUrl"
|
:action="actionUrl"
|
||||||
method="post">
|
method="post">
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="hidden"
|
type="hidden"
|
||||||
name="_method"
|
name="_method"
|
||||||
value="delete" />
|
value="delete" />
|
||||||
<input
|
<input
|
||||||
type="hidden"
|
type="hidden"
|
||||||
name="authenticity_token"
|
name="authenticity_token"
|
||||||
:value="csrfToken" />
|
:value="csrfToken" />
|
||||||
|
|
||||||
<p id="input-label" v-html="inputLabel"></p>
|
<p id="input-label" v-html="inputLabel"></p>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
v-if="confirmWithPassword"
|
v-if="confirmWithPassword"
|
||||||
name="password"
|
name="password"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
type="password"
|
type="password"
|
||||||
v-model="enteredPassword"
|
v-model="enteredPassword"
|
||||||
aria-labelledby="input-label" />
|
aria-labelledby="input-label" />
|
||||||
<input
|
<input
|
||||||
v-else
|
v-else
|
||||||
name="username"
|
name="username"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
type="text"
|
type="text"
|
||||||
v-model="enteredUsername"
|
v-model="enteredUsername"
|
||||||
aria-labelledby="input-label" />
|
aria-labelledby="input-label" />
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
</modal>
|
</modal>
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-danger"
|
|
||||||
@click="toggleOpen(true)">
|
|
||||||
{{ s__('Profiles|Delete account') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
import Translate from '~/vue_shared/translate';
|
||||||
|
|
||||||
import deleteAccountModal from './components/delete_account_modal.vue';
|
import deleteAccountModal from './components/delete_account_modal.vue';
|
||||||
|
|
||||||
|
Vue.use(Translate);
|
||||||
|
|
||||||
|
const deleteAccountButton = document.getElementById('delete-account-button');
|
||||||
const deleteAccountModalEl = document.getElementById('delete-account-modal');
|
const deleteAccountModalEl = document.getElementById('delete-account-modal');
|
||||||
// eslint-disable-next-line no-new
|
// eslint-disable-next-line no-new
|
||||||
new Vue({
|
new Vue({
|
||||||
|
@ -9,6 +14,9 @@ new Vue({
|
||||||
components: {
|
components: {
|
||||||
deleteAccountModal,
|
deleteAccountModal,
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
deleteAccountButton.classList.remove('disabled');
|
||||||
|
},
|
||||||
render(createElement) {
|
render(createElement) {
|
||||||
return createElement('delete-account-modal', {
|
return createElement('delete-account-modal', {
|
||||||
props: {
|
props: {
|
||||||
|
|
|
@ -3,6 +3,10 @@ export default {
|
||||||
name: 'modal',
|
name: 'modal',
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
|
@ -62,11 +66,11 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
close() {
|
emitCancel(event) {
|
||||||
this.$emit('toggle', false);
|
this.$emit('cancel', event);
|
||||||
},
|
},
|
||||||
emitSubmit(status) {
|
emitSubmit(event) {
|
||||||
this.$emit('submit', status);
|
this.$emit('submit', event);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -75,7 +79,9 @@ export default {
|
||||||
<template>
|
<template>
|
||||||
<div class="modal-open">
|
<div class="modal-open">
|
||||||
<div
|
<div
|
||||||
class="modal show"
|
:id="id"
|
||||||
|
class="modal"
|
||||||
|
:class="id ? '' : 'show'"
|
||||||
role="dialog"
|
role="dialog"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
|
@ -93,7 +99,8 @@ export default {
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="close pull-right"
|
class="close pull-right"
|
||||||
@click="close"
|
@click="emitCancel($event)"
|
||||||
|
data-dismiss="modal"
|
||||||
aria-label="Close"
|
aria-label="Close"
|
||||||
>
|
>
|
||||||
<span aria-hidden="true">×</span>
|
<span aria-hidden="true">×</span>
|
||||||
|
@ -110,7 +117,8 @@ export default {
|
||||||
type="button"
|
type="button"
|
||||||
class="btn pull-left"
|
class="btn pull-left"
|
||||||
:class="btnCancelKindClass"
|
:class="btnCancelKindClass"
|
||||||
@click="close">
|
@click="emitCancel($event)"
|
||||||
|
data-dismiss="modal">
|
||||||
{{ closeButtonLabel }}
|
{{ closeButtonLabel }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
@ -119,13 +127,17 @@ export default {
|
||||||
class="btn pull-right js-primary-button"
|
class="btn pull-right js-primary-button"
|
||||||
:disabled="submitDisabled"
|
:disabled="submitDisabled"
|
||||||
:class="btnKindClass"
|
:class="btnKindClass"
|
||||||
@click="emitSubmit(true)">
|
@click="emitSubmit($event)"
|
||||||
|
data-dismiss="modal">
|
||||||
{{ primaryButtonLabel }}
|
{{ primaryButtonLabel }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-backdrop fade in" />
|
<div
|
||||||
|
v-if="!id"
|
||||||
|
class="modal-backdrop fade in">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -70,7 +70,7 @@ export default {
|
||||||
class="recaptcha-modal js-recaptcha-modal"
|
class="recaptcha-modal js-recaptcha-modal"
|
||||||
:hide-footer="true"
|
:hide-footer="true"
|
||||||
:title="__('Please solve the reCAPTCHA')"
|
:title="__('Please solve the reCAPTCHA')"
|
||||||
@toggle="close"
|
@cancel="close"
|
||||||
>
|
>
|
||||||
<div slot="body">
|
<div slot="body">
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -84,11 +84,13 @@
|
||||||
= s_('Profiles|Deleting an account has the following effects:')
|
= s_('Profiles|Deleting an account has the following effects:')
|
||||||
= render 'users/deletion_guidance', user: current_user
|
= render 'users/deletion_guidance', user: current_user
|
||||||
|
|
||||||
|
%button#delete-account-button.btn.btn-danger.disabled{ data: { toggle: 'modal',
|
||||||
|
target: '#delete-account-modal' } }
|
||||||
|
= s_('Profiles|Delete account')
|
||||||
|
|
||||||
#delete-account-modal{ data: { action_url: user_registration_path,
|
#delete-account-modal{ data: { action_url: user_registration_path,
|
||||||
confirm_with_password: ('true' if current_user.confirm_deletion_with_password?),
|
confirm_with_password: ('true' if current_user.confirm_deletion_with_password?),
|
||||||
username: current_user.username } }
|
username: current_user.username } }
|
||||||
%button.btn.btn-danger.disabled
|
|
||||||
= s_('Profiles|Delete account')
|
|
||||||
- else
|
- else
|
||||||
- if @user.solo_owned_groups.present?
|
- if @user.solo_owned_groups.present?
|
||||||
%p
|
%p
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Add id to modal.vue to support data-toggle="modal"
|
||||||
|
merge_request: 16189
|
||||||
|
author:
|
||||||
|
type: other
|
|
@ -47,18 +47,12 @@ describe('ItemActionsComponent', () => {
|
||||||
it('should change `modalStatus` prop to `false` and emit `leaveGroup` event with required params when called with `leaveConfirmed` as `true`', () => {
|
it('should change `modalStatus` prop to `false` and emit `leaveGroup` event with required params when called with `leaveConfirmed` as `true`', () => {
|
||||||
spyOn(eventHub, '$emit');
|
spyOn(eventHub, '$emit');
|
||||||
vm.modalStatus = true;
|
vm.modalStatus = true;
|
||||||
vm.leaveGroup(true);
|
|
||||||
|
vm.leaveGroup();
|
||||||
|
|
||||||
expect(vm.modalStatus).toBeFalsy();
|
expect(vm.modalStatus).toBeFalsy();
|
||||||
expect(eventHub.$emit).toHaveBeenCalledWith('leaveGroup', vm.group, vm.parentGroup);
|
expect(eventHub.$emit).toHaveBeenCalledWith('leaveGroup', vm.group, vm.parentGroup);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should change `modalStatus` prop to `false` and should NOT emit `leaveGroup` event when called with `leaveConfirmed` as `false`', () => {
|
|
||||||
spyOn(eventHub, '$emit');
|
|
||||||
vm.modalStatus = true;
|
|
||||||
vm.leaveGroup(false);
|
|
||||||
expect(vm.modalStatus).toBeFalsy();
|
|
||||||
expect(eventHub.$emit).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ describe('DeleteAccountModal component', () => {
|
||||||
Vue.nextTick()
|
Vue.nextTick()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(vm.enteredPassword).toBe(input.value);
|
expect(vm.enteredPassword).toBe(input.value);
|
||||||
expect(submitButton).toHaveClass('disabled');
|
expect(submitButton).toHaveAttr('disabled', 'disabled');
|
||||||
submitButton.click();
|
submitButton.click();
|
||||||
expect(form.submit).not.toHaveBeenCalled();
|
expect(form.submit).not.toHaveBeenCalled();
|
||||||
})
|
})
|
||||||
|
@ -68,7 +68,7 @@ describe('DeleteAccountModal component', () => {
|
||||||
Vue.nextTick()
|
Vue.nextTick()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(vm.enteredPassword).toBe(input.value);
|
expect(vm.enteredPassword).toBe(input.value);
|
||||||
expect(submitButton).not.toHaveClass('disabled');
|
expect(submitButton).not.toHaveAttr('disabled', 'disabled');
|
||||||
submitButton.click();
|
submitButton.click();
|
||||||
expect(form.submit).toHaveBeenCalled();
|
expect(form.submit).toHaveBeenCalled();
|
||||||
})
|
})
|
||||||
|
@ -101,7 +101,7 @@ describe('DeleteAccountModal component', () => {
|
||||||
Vue.nextTick()
|
Vue.nextTick()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(vm.enteredUsername).toBe(input.value);
|
expect(vm.enteredUsername).toBe(input.value);
|
||||||
expect(submitButton).toHaveClass('disabled');
|
expect(submitButton).toHaveAttr('disabled', 'disabled');
|
||||||
submitButton.click();
|
submitButton.click();
|
||||||
expect(form.submit).not.toHaveBeenCalled();
|
expect(form.submit).not.toHaveBeenCalled();
|
||||||
})
|
})
|
||||||
|
@ -118,7 +118,7 @@ describe('DeleteAccountModal component', () => {
|
||||||
Vue.nextTick()
|
Vue.nextTick()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(vm.enteredUsername).toBe(input.value);
|
expect(vm.enteredUsername).toBe(input.value);
|
||||||
expect(submitButton).not.toHaveClass('disabled');
|
expect(submitButton).not.toHaveAttr('disabled', 'disabled');
|
||||||
submitButton.click();
|
submitButton.click();
|
||||||
expect(form.submit).toHaveBeenCalled();
|
expect(form.submit).toHaveBeenCalled();
|
||||||
})
|
})
|
||||||
|
|
|
@ -57,15 +57,16 @@ describe('new dropdown component', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('toggleModalOpen', () => {
|
describe('hideModal', () => {
|
||||||
|
beforeAll((done) => {
|
||||||
|
vm.openModal = true;
|
||||||
|
Vue.nextTick(done);
|
||||||
|
});
|
||||||
|
|
||||||
it('closes modal after toggling', (done) => {
|
it('closes modal after toggling', (done) => {
|
||||||
vm.toggleModalOpen();
|
vm.hideModal();
|
||||||
|
|
||||||
Vue.nextTick()
|
Vue.nextTick()
|
||||||
.then(() => {
|
|
||||||
expect(vm.$el.querySelector('.modal')).not.toBeNull();
|
|
||||||
})
|
|
||||||
.then(vm.toggleModalOpen)
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(vm.$el.querySelector('.modal')).toBeNull();
|
expect(vm.$el.querySelector('.modal')).toBeNull();
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,11 +2,65 @@ import Vue from 'vue';
|
||||||
import modal from '~/vue_shared/components/modal.vue';
|
import modal from '~/vue_shared/components/modal.vue';
|
||||||
import mountComponent from '../../helpers/vue_mount_component_helper';
|
import mountComponent from '../../helpers/vue_mount_component_helper';
|
||||||
|
|
||||||
describe('Modal', () => {
|
const modalComponent = Vue.extend(modal);
|
||||||
it('does not render a primary button if no primaryButtonLabel', () => {
|
|
||||||
const modalComponent = Vue.extend(modal);
|
|
||||||
const vm = mountComponent(modalComponent);
|
|
||||||
|
|
||||||
expect(vm.$el.querySelector('.js-primary-button')).toBeNull();
|
describe('Modal', () => {
|
||||||
|
let vm;
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vm.$destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('props', () => {
|
||||||
|
describe('without primaryButtonLabel', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vm = mountComponent(modalComponent, {
|
||||||
|
primaryButtonLabel: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not render a primary button', () => {
|
||||||
|
expect(vm.$el.querySelector('.js-primary-button')).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with id', () => {
|
||||||
|
it('does not render a primary button', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vm = mountComponent(modalComponent, {
|
||||||
|
id: 'my-modal',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('assigns the id to the modal', () => {
|
||||||
|
expect(vm.$el.querySelector('#my-modal.modal')).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not show the modal immediately', () => {
|
||||||
|
expect(vm.$el.querySelector('#my-modal.modal')).not.toHaveClass('show');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not show a backdrop', () => {
|
||||||
|
expect(vm.$el.querySelector('modal-backdrop')).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works with data-toggle="modal"', (done) => {
|
||||||
|
setFixtures(`
|
||||||
|
<button id="modal-button" data-toggle="modal" data-target="#my-modal"></button>
|
||||||
|
<div id="modal-container"></div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
const modalContainer = document.getElementById('modal-container');
|
||||||
|
const modalButton = document.getElementById('modal-button');
|
||||||
|
vm = mountComponent(modalComponent, {
|
||||||
|
id: 'my-modal',
|
||||||
|
}, modalContainer);
|
||||||
|
const modalElement = vm.$el.querySelector('#my-modal');
|
||||||
|
$(modalElement).on('shown.bs.modal', () => done());
|
||||||
|
|
||||||
|
modalButton.click();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue