summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWinnie Hellmann <winnie@gitlab.com>2017-12-21 15:26:50 +0100
committerWinnie Hellmann <winnie@gitlab.com>2018-01-02 14:56:05 +0100
commit1a1807294ae17eea3637e5797ec75e6eb773f863 (patch)
tree6a0f107065975158129b8428bc76b8ca021f0448
parent539996f7daf9115f713c0802dea85c53c5f6838a (diff)
downloadgitlab-ce-winh-modal-internal-state.tar.gz
Maintain modal state internallywinh-modal-internal-state
-rw-r--r--app/assets/javascripts/groups/components/item_actions.vue17
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/index.vue9
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/modal.vue14
-rw-r--r--app/assets/javascripts/ide/components/repo_commit_section.vue8
-rw-r--r--app/assets/javascripts/ide/components/repo_edit_button.vue3
-rw-r--r--app/assets/javascripts/profile/account/components/delete_account_modal.vue22
-rw-r--r--app/assets/javascripts/vue_shared/components/modal.vue40
-rw-r--r--app/assets/javascripts/vue_shared/components/recaptcha_modal.vue4
-rw-r--r--changelogs/unreleased/winh-modal-internal-state.yml5
-rw-r--r--spec/javascripts/groups/components/item_actions_spec.js32
-rw-r--r--spec/javascripts/profile/account/components/delete_account_modal_spec.js4
-rw-r--r--spec/javascripts/repo/components/new_dropdown/index_spec.js34
-rw-r--r--spec/javascripts/repo/components/new_dropdown/modal_spec.js16
-rw-r--r--spec/javascripts/vue_shared/components/modal_spec.js129
14 files changed, 228 insertions, 109 deletions
diff --git a/app/assets/javascripts/groups/components/item_actions.vue b/app/assets/javascripts/groups/components/item_actions.vue
index 58ba5aff7cf..2238112497d 100644
--- a/app/assets/javascripts/groups/components/item_actions.vue
+++ b/app/assets/javascripts/groups/components/item_actions.vue
@@ -25,11 +25,6 @@ export default {
required: true,
},
},
- data() {
- return {
- modalStatus: false,
- };
- },
computed: {
leaveBtnTitle() {
return COMMON_STR.LEAVE_BTN_TITLE;
@@ -43,13 +38,11 @@ export default {
},
methods: {
onLeaveGroup() {
- this.modalStatus = true;
+ this.$refs.modal.show();
},
- leaveGroup(leaveConfirmed) {
- this.modalStatus = false;
- if (leaveConfirmed) {
- eventHub.$emit('leaveGroup', this.group, this.parentGroup);
- }
+ leaveGroup() {
+ eventHub.$emit('leaveGroup', this.group, this.parentGroup);
+ this.$refs.modal.hide();
},
},
};
@@ -83,7 +76,7 @@ export default {
aria-hidden="true"/>
</a>
<modal
- v-show="modalStatus"
+ ref="modal"
:primary-button-label="__('Leave')"
kind="warning"
:title="__('Are you sure?')"
diff --git a/app/assets/javascripts/ide/components/new_dropdown/index.vue b/app/assets/javascripts/ide/components/new_dropdown/index.vue
index 6e67e99a70f..4329bf2fd02 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/index.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/index.vue
@@ -25,17 +25,13 @@
},
data() {
return {
- openModal: false,
modalType: '',
};
},
methods: {
createNewItem(type) {
this.modalType = type;
- this.toggleModalOpen();
- },
- toggleModalOpen() {
- this.openModal = !this.openModal;
+ this.$refs.modal.show();
},
},
};
@@ -90,12 +86,11 @@
</ul>
</div>
<new-modal
- v-if="openModal"
+ ref="modal"
:type="modalType"
:branch-id="branch"
:path="path"
:parent="parent"
- @toggle="toggleModalOpen"
/>
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
index a0650d37690..eb1495a88c3 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
@@ -43,10 +43,13 @@
type: this.type,
});
- this.toggleModalOpen();
+ this.$refs.modal.hide();
},
- toggleModalOpen() {
- this.$emit('toggle');
+ show() {
+ this.$refs.modal.show();
+ this.$nextTick(() => {
+ this.$refs.fieldName.focus();
+ });
},
},
computed: {
@@ -75,18 +78,15 @@
return __('File name');
},
},
- mounted() {
- this.$refs.fieldName.focus();
- },
};
</script>
<template>
<modal
+ ref="modal"
:title="modalTitle"
:primary-button-label="buttonLabel"
kind="success"
- @toggle="toggleModalOpen"
@submit="createEntryInStore"
>
<form
diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue
index 470db2c9650..b6c168e8242 100644
--- a/app/assets/javascripts/ide/components/repo_commit_section.vue
+++ b/app/assets/javascripts/ide/components/repo_commit_section.vue
@@ -16,7 +16,6 @@ export default {
},
data() {
return {
- showNewBranchModal: false,
submitCommitsLoading: false,
startNewMR: false,
commitMessage: '',
@@ -60,7 +59,7 @@ export default {
start_branch: createNewBranch ? this.currentBranchId : undefined,
};
- this.showNewBranchModal = false;
+ this.$refs.modal.hide();
this.submitCommitsLoading = true;
this.commitChanges({ payload, newMr: this.startNewMR })
@@ -83,7 +82,7 @@ export default {
this.checkCommitStatus()
.then((branchChanged) => {
if (branchChanged) {
- this.showNewBranchModal = true;
+ this.$refs.modal.show();
} else {
this.makeCommit();
}
@@ -105,12 +104,11 @@ export default {
<template>
<div class="multi-file-commit-panel-section">
<modal
- v-if="showNewBranchModal"
+ ref="modal"
:primary-button-label="__('Create new branch')"
kind="primary"
:title="__('Branch has changed')"
:text="__('This branch has changed since you started editing. Would you like to create a new branch?')"
- @toggle="showNewBranchModal = false"
@submit="makeCommit(true)"
/>
<commit-files-list
diff --git a/app/assets/javascripts/ide/components/repo_edit_button.vue b/app/assets/javascripts/ide/components/repo_edit_button.vue
index 37bd9003e96..cae0a8bb3ec 100644
--- a/app/assets/javascripts/ide/components/repo_edit_button.vue
+++ b/app/assets/javascripts/ide/components/repo_edit_button.vue
@@ -45,12 +45,13 @@ export default {
</button>
<modal
v-if="discardPopupOpen"
+ :isInitiallyVisible="true"
class="text-left"
:primary-button-label="__('Discard changes')"
kind="warning"
:title="__('Are you sure?')"
:text="__('Are you sure you want to discard your changes?')"
- @toggle="closeDiscardPopup"
+ @cancel="closeDiscardPopup"
@submit="toggleEditMode(true)"
/>
</div>
diff --git a/app/assets/javascripts/profile/account/components/delete_account_modal.vue b/app/assets/javascripts/profile/account/components/delete_account_modal.vue
index 78be6b6e884..816875c417b 100644
--- a/app/assets/javascripts/profile/account/components/delete_account_modal.vue
+++ b/app/assets/javascripts/profile/account/components/delete_account_modal.vue
@@ -22,7 +22,6 @@
return {
enteredPassword: '',
enteredUsername: '',
- isOpen: false,
};
},
components: {
@@ -69,19 +68,13 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
return this.enteredUsername === this.username;
},
- onSubmit(status) {
- if (status) {
- if (!this.canSubmit()) {
- return;
- }
-
- this.$refs.form.submit();
+ onSubmit() {
+ if (!this.canSubmit()) {
+ return;
}
- this.toggleOpen(false);
- },
- toggleOpen(isOpen) {
- this.isOpen = isOpen;
+ this.$refs.form.submit();
+ this.$refs.modal.hide();
},
},
};
@@ -90,12 +83,11 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
<template>
<div>
<modal
- v-if="isOpen"
+ ref="modal"
:title="s__('Profiles|Delete your account?')"
:text="text"
:kind="`danger ${!canSubmit() && 'disabled'}`"
:primary-button-label="s__('Profiles|Delete account')"
- @toggle="toggleOpen"
@submit="onSubmit">
<template slot="body" slot-scope="props">
@@ -139,7 +131,7 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
<button
type="button"
class="btn btn-danger"
- @click="toggleOpen(true)">
+ @click="$refs.modal.show()">
{{ s__('Profiles|Delete account') }}
</button>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/modal.vue b/app/assets/javascripts/vue_shared/components/modal.vue
index 55f466b7b41..8ee30433b2d 100644
--- a/app/assets/javascripts/vue_shared/components/modal.vue
+++ b/app/assets/javascripts/vue_shared/components/modal.vue
@@ -3,6 +3,11 @@ export default {
name: 'modal',
props: {
+ isInitiallyVisible: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
title: {
type: String,
required: false,
@@ -48,6 +53,12 @@ export default {
},
},
+ data() {
+ return {
+ isVisible: this.isInitiallyVisible,
+ };
+ },
+
computed: {
btnKindClass() {
return {
@@ -62,18 +73,29 @@ export default {
},
methods: {
- close() {
- this.$emit('toggle', false);
+ cancel() {
+ this.hide();
+ this.$emit('cancel');
+ },
+ hide() {
+ this.isVisible = false;
+ this.$emit('hide');
+ },
+ show() {
+ this.isVisible = true;
+ this.$emit('show');
},
- emitSubmit(status) {
- this.$emit('submit', status);
+ submit() {
+ this.$emit('submit');
},
},
};
</script>
<template>
-<div class="modal-open">
+<div
+ v-if="isVisible"
+ class="modal-open">
<div
class="modal show"
role="dialog"
@@ -93,7 +115,7 @@ export default {
<button
type="button"
class="close pull-right"
- @click="close"
+ @click="cancel"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
@@ -110,7 +132,7 @@ export default {
type="button"
class="btn pull-left"
:class="btnCancelKindClass"
- @click="close">
+ @click="cancel">
{{ closeButtonLabel }}
</button>
<button
@@ -119,13 +141,13 @@ export default {
class="btn pull-right js-primary-button"
:disabled="submitDisabled"
:class="btnKindClass"
- @click="emitSubmit(true)">
+ @click="submit">
{{ primaryButtonLabel }}
</button>
</div>
</div>
</div>
</div>
- <div class="modal-backdrop fade in" />
+ <div class="modal-backdrop fade in"></div>
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue b/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue
index 8053c65d498..c7ae24447e5 100644
--- a/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue
@@ -60,17 +60,19 @@ export default {
mounted() {
window.recaptchaDialogCallback = this.submit.bind(this);
+ this.$refs.modal.show();
},
};
</script>
<template>
<modal
+ ref="modal"
kind="warning"
class="recaptcha-modal js-recaptcha-modal"
:hide-footer="true"
:title="__('Please solve the reCAPTCHA')"
- @toggle="close"
+ @hide="close"
>
<div slot="body">
<p>
diff --git a/changelogs/unreleased/winh-modal-internal-state.yml b/changelogs/unreleased/winh-modal-internal-state.yml
new file mode 100644
index 00000000000..bc5795c4f8f
--- /dev/null
+++ b/changelogs/unreleased/winh-modal-internal-state.yml
@@ -0,0 +1,5 @@
+---
+title: Maintain modal state internally
+merge_request: 16085
+author:
+type: other
diff --git a/spec/javascripts/groups/components/item_actions_spec.js b/spec/javascripts/groups/components/item_actions_spec.js
index 7a5c1da4d1d..f06f1848368 100644
--- a/spec/javascripts/groups/components/item_actions_spec.js
+++ b/spec/javascripts/groups/components/item_actions_spec.js
@@ -36,28 +36,22 @@ describe('ItemActionsComponent', () => {
describe('methods', () => {
describe('onLeaveGroup', () => {
- it('should change `modalStatus` prop to `true` which shows confirmation dialog', () => {
- expect(vm.modalStatus).toBeFalsy();
+ it('should show the confirmation dialog', () => {
+ expect(vm.$refs.modal.isVisible).toBe(false);
vm.onLeaveGroup();
- expect(vm.modalStatus).toBeTruthy();
+ expect(vm.$refs.modal.isVisible).toBe(true);
});
});
describe('leaveGroup', () => {
- it('should change `modalStatus` prop to `false` and emit `leaveGroup` event with required params when called with `leaveConfirmed` as `true`', () => {
+ it('should hide modal and emit `leaveGroup` event with required params', () => {
+ vm.$refs.modal.show();
spyOn(eventHub, '$emit');
- vm.modalStatus = true;
- vm.leaveGroup(true);
- expect(vm.modalStatus).toBeFalsy();
- 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();
+ vm.leaveGroup();
+
+ expect(vm.$refs.modal.isVisible).toBe(false);
+ expect(eventHub.$emit).toHaveBeenCalledWith('leaveGroup', vm.group, vm.parentGroup);
});
});
});
@@ -98,13 +92,5 @@ describe('ItemActionsComponent', () => {
newVm.$destroy();
});
-
- it('should show modal dialog when `modalStatus` is set to `true`', () => {
- vm.modalStatus = true;
- const modalDialogEl = vm.$el.querySelector('.modal');
- expect(modalDialogEl).toBeDefined();
- expect(modalDialogEl.querySelector('.modal-title').innerText.trim()).toBe('Are you sure?');
- expect(modalDialogEl.querySelector('.btn.btn-warning').innerText.trim()).toBe('Leave');
- });
});
});
diff --git a/spec/javascripts/profile/account/components/delete_account_modal_spec.js b/spec/javascripts/profile/account/components/delete_account_modal_spec.js
index 2e94948cfb2..4c8788ba84e 100644
--- a/spec/javascripts/profile/account/components/delete_account_modal_spec.js
+++ b/spec/javascripts/profile/account/components/delete_account_modal_spec.js
@@ -35,7 +35,7 @@ describe('DeleteAccountModal component', () => {
username,
});
- vm.isOpen = true;
+ vm.$refs.modal.show();
Vue.nextTick()
.then(done)
@@ -85,7 +85,7 @@ describe('DeleteAccountModal component', () => {
username,
});
- vm.isOpen = true;
+ vm.$refs.modal.show();
Vue.nextTick()
.then(done)
diff --git a/spec/javascripts/repo/components/new_dropdown/index_spec.js b/spec/javascripts/repo/components/new_dropdown/index_spec.js
index b001c1655b4..5cfc8fc8a2f 100644
--- a/spec/javascripts/repo/components/new_dropdown/index_spec.js
+++ b/spec/javascripts/repo/components/new_dropdown/index_spec.js
@@ -34,41 +34,35 @@ describe('new dropdown component', () => {
});
describe('createNewItem', () => {
- it('sets modalType to blob when new file is clicked', () => {
+ it('sets modalType to blob when new file is clicked', (done) => {
vm.$el.querySelectorAll('a')[0].click();
- expect(vm.modalType).toBe('blob');
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.modalType).toBe('blob');
+ })
+ .then(done)
+ .catch(done.fail);
});
- it('sets modalType to tree when new directory is clicked', () => {
+ it('sets modalType to tree when new directory is clicked', (done) => {
vm.$el.querySelectorAll('a')[2].click();
- expect(vm.modalType).toBe('tree');
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.modalType).toBe('tree');
+ })
+ .then(done)
+ .catch(done.fail);
});
it('opens modal when link is clicked', (done) => {
vm.$el.querySelectorAll('a')[0].click();
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.modal')).not.toBeNull();
-
- done();
- });
- });
- });
-
- describe('toggleModalOpen', () => {
- it('closes modal after toggling', (done) => {
- vm.toggleModalOpen();
-
Vue.nextTick()
.then(() => {
expect(vm.$el.querySelector('.modal')).not.toBeNull();
})
- .then(vm.toggleModalOpen)
- .then(() => {
- expect(vm.$el.querySelector('.modal')).toBeNull();
- })
.then(done)
.catch(done.fail);
});
diff --git a/spec/javascripts/repo/components/new_dropdown/modal_spec.js b/spec/javascripts/repo/components/new_dropdown/modal_spec.js
index 233cca06ed0..dc66c7ef997 100644
--- a/spec/javascripts/repo/components/new_dropdown/modal_spec.js
+++ b/spec/javascripts/repo/components/new_dropdown/modal_spec.js
@@ -46,7 +46,7 @@ describe('new file modal component', () => {
['tree', 'blob'].forEach((type) => {
describe(type, () => {
- beforeEach(() => {
+ beforeEach((done) => {
store.state.projects.abcproject = {
web_url: '',
};
@@ -67,6 +67,8 @@ describe('new file modal component', () => {
vm.entryName = 'testing';
vm.$mount();
+ vm.show();
+ Vue.nextTick(done);
});
it(`sets modal title as ${type}`, () => {
@@ -218,7 +220,7 @@ describe('new file modal component', () => {
});
});
- it('focuses field on mount', () => {
+ it('focuses field on show', (done) => {
document.body.innerHTML += '<div class="js-test"></div>';
vm = createComponentWithStore(Component, store, {
@@ -227,9 +229,15 @@ describe('new file modal component', () => {
branchId: 'master',
path: '',
}).$mount('.js-test');
+ vm.show();
- expect(document.activeElement).toBe(vm.$refs.fieldName);
+ Vue.nextTick()
+ .then(() => {
+ expect(document.activeElement).toBe(vm.$refs.fieldName);
- vm.$el.remove();
+ vm.$el.remove();
+ })
+ .then(done)
+ .catch(done.fail);
});
});
diff --git a/spec/javascripts/vue_shared/components/modal_spec.js b/spec/javascripts/vue_shared/components/modal_spec.js
index 721f4044659..bbe01c58046 100644
--- a/spec/javascripts/vue_shared/components/modal_spec.js
+++ b/spec/javascripts/vue_shared/components/modal_spec.js
@@ -3,10 +3,133 @@ import modal from '~/vue_shared/components/modal.vue';
import mountComponent from '../../helpers/vue_mount_component_helper';
describe('Modal', () => {
- it('does not render a primary button if no primaryButtonLabel', () => {
+ let props;
+ let vm;
+
+ beforeEach((done) => {
const modalComponent = Vue.extend(modal);
- const vm = mountComponent(modalComponent);
+ vm = mountComponent(modalComponent, props);
+ Vue.nextTick(done);
+ });
+
+ describe('props', () => {
+ describe('with isInitiallyVisible = false', () => {
+ beforeAll(() => {
+ props = {
+ isInitiallyVisible: false,
+ };
+ });
+
+ it('is not visible', () => {
+ expect(vm.$el.firstChild).toBeNull();
+ });
+ });
+
+ describe('with isInitiallyVisible = true', () => {
+ beforeAll(() => {
+ props = {
+ isInitiallyVisible: true,
+ };
+ });
+
+ it('is visible', () => {
+ expect(vm.$el.querySelector('.modal')).not.toBeNull();
+ });
+ });
+
+ describe('without primaryButtonLabel', () => {
+ beforeAll(() => {
+ props = {
+ isInitiallyVisible: true,
+ primaryButtonLabel: null,
+ };
+ });
+
+ it('does not render a primary button', () => {
+ expect(vm.$el.querySelector('.js-primary-button')).toBeNull();
+ });
+ });
+ });
+
+ describe('methods', () => {
+ describe('cancel', () => {
+ beforeAll(() => {
+ props = {
+ isInitiallyVisible: true,
+ };
+ });
+
+ it('hides the modal', () => {
+ spyOn(vm, '$emit');
+ spyOn(vm, 'hide');
+
+ vm.cancel();
+
+ expect(vm.hide).toHaveBeenCalled();
+ expect(vm.$emit).toHaveBeenCalledWith('cancel');
+ });
+ });
+
+ describe('hide', () => {
+ beforeAll(() => {
+ props = {
+ isInitiallyVisible: true,
+ };
+ });
+
+ it('makes the modal invisible', (done) => {
+ spyOn(vm, '$emit');
+
+ vm.hide();
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.$el.firstChild).toBeNull();
+ expect(vm.$emit).toHaveBeenCalledWith('hide');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('show', () => {
+ beforeAll(() => {
+ props = {
+ isInitiallyVisible: false,
+ };
+ });
+
+ it('makes the modal visible', (done) => {
+ spyOn(vm, '$emit');
+
+ vm.show();
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.$el.querySelector('.modal')).not.toBeNull();
+ expect(vm.$emit).toHaveBeenCalledWith('show');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('submit', () => {
+ beforeAll(() => {
+ props = {
+ isInitiallyVisible: true,
+ };
+ });
+
+ it('emits submit event', () => {
+ spyOn(vm, '$emit');
+ spyOn(vm, 'hide');
+
+ vm.submit();
- expect(vm.$el.querySelector('.js-primary-button')).toBeNull();
+ expect(vm.hide).not.toHaveBeenCalled(); // submit event handler should call hide()
+ expect(vm.$emit).toHaveBeenCalledWith('submit');
+ });
+ });
});
});