diff options
author | Winnie Hellmann <winnie@gitlab.com> | 2017-12-21 15:26:50 +0100 |
---|---|---|
committer | Winnie Hellmann <winnie@gitlab.com> | 2018-01-02 14:56:05 +0100 |
commit | 1a1807294ae17eea3637e5797ec75e6eb773f863 (patch) | |
tree | 6a0f107065975158129b8428bc76b8ca021f0448 | |
parent | 539996f7daf9115f713c0802dea85c53c5f6838a (diff) | |
download | gitlab-ce-winh-modal-internal-state.tar.gz |
Maintain modal state internallywinh-modal-internal-state
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">×</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'); + }); + }); }); }); |