From c4fea61f152bc8ea73d912af3467a35b34e74aaf Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 15 May 2017 17:00:16 +0100 Subject: Added inline confidential field [ci skip] --- .../javascripts/issue_show/components/app.vue | 18 ++++++++++++++++- .../components/fields/confidential_checkbox.vue | 23 ++++++++++++++++++++++ app/assets/javascripts/issue_show/index.js | 3 +++ app/assets/javascripts/issue_show/stores/index.js | 1 + app/views/projects/issues/show.html.haml | 1 + spec/javascripts/issue_show/components/app_spec.js | 1 + 6 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 app/assets/javascripts/issue_show/components/fields/confidential_checkbox.vue diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue index abddcaa2594..854c7513ee6 100644 --- a/app/assets/javascripts/issue_show/components/app.vue +++ b/app/assets/javascripts/issue_show/components/app.vue @@ -7,6 +7,7 @@ import Service from '../services/index'; import Store from '../stores'; import titleComponent from './title.vue'; import descriptionComponent from './description.vue'; +import confidentialCheckbox from './fields/confidential_checkbox.vue'; import editActions from './edit_actions.vue'; export default { @@ -41,6 +42,10 @@ export default { required: false, default: '', }, + isConfidential: { + type: Boolean, + required: true, + }, }, data() { const store = new Store({ @@ -67,12 +72,14 @@ export default { descriptionComponent, titleComponent, editActions, + confidentialCheckbox, }, methods: { openForm() { this.showForm = true; this.store.formState = { title: this.state.titleText, + confidential: this.isConfidential, }; }, closeForm() { @@ -80,7 +87,13 @@ export default { }, updateIssuable() { this.service.updateIssuable(this.store.formState) - .then(() => { + .then((res) => { + const data = res.json(); + + if (data.confidential !== this.isConfidential) { + location.reload(); + } + eventHub.$emit('close.form'); }) .catch(() => { @@ -157,6 +170,9 @@ export default { :description-text="state.descriptionText" :updated-at="state.updatedAt" :task-status="state.taskStatus" /> + diff --git a/app/assets/javascripts/issue_show/components/fields/confidential_checkbox.vue b/app/assets/javascripts/issue_show/components/fields/confidential_checkbox.vue new file mode 100644 index 00000000000..ce0471d499a --- /dev/null +++ b/app/assets/javascripts/issue_show/components/fields/confidential_checkbox.vue @@ -0,0 +1,23 @@ + + + diff --git a/app/assets/javascripts/issue_show/index.js b/app/assets/javascripts/issue_show/index.js index a2ad1a0e034..b1e8f467979 100644 --- a/app/assets/javascripts/issue_show/index.js +++ b/app/assets/javascripts/issue_show/index.js @@ -25,6 +25,7 @@ document.addEventListener('DOMContentLoaded', () => { canDestroy, endpoint, issuableRef, + isConfidential, } = issuableElement.dataset; return { @@ -35,6 +36,7 @@ document.addEventListener('DOMContentLoaded', () => { initialTitle: issuableTitleElement.innerHTML, initialDescriptionHtml: issuableDescriptionElement ? issuableDescriptionElement.innerHTML : '', initialDescriptionText: issuableDescriptionTextarea ? issuableDescriptionTextarea.textContent : '', + isConfidential: gl.utils.convertPermissionToBoolean(isConfidential), }; }, render(createElement) { @@ -47,6 +49,7 @@ document.addEventListener('DOMContentLoaded', () => { initialTitle: this.initialTitle, initialDescriptionHtml: this.initialDescriptionHtml, initialDescriptionText: this.initialDescriptionText, + isConfidential: this.isConfidential, }, }); }, diff --git a/app/assets/javascripts/issue_show/stores/index.js b/app/assets/javascripts/issue_show/stores/index.js index 0ab52c307a0..5af63369211 100644 --- a/app/assets/javascripts/issue_show/stores/index.js +++ b/app/assets/javascripts/issue_show/stores/index.js @@ -14,6 +14,7 @@ export default class Store { }; this.formState = { title: '', + confidential: false, }; } diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index d0783009a58..c21cea259a1 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -55,6 +55,7 @@ "can-update" => can?(current_user, :update_issue, @issue).to_s, "can-destroy" => can?(current_user, :destroy_issue, @issue).to_s, "issuable-ref" => @issue.to_reference, + "is-confidential" => @issue.confidential.to_s, } } %h2.title= markdown_field(@issue, :title) - if @issue.description.present? diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js index ad99249a806..a310d5cc918 100644 --- a/spec/javascripts/issue_show/components/app_spec.js +++ b/spec/javascripts/issue_show/components/app_spec.js @@ -35,6 +35,7 @@ describe('Issuable output', () => { initialDescriptionHtml: '', initialDescriptionText: '', showForm: false, + isConfidential: false, }, }).$mount(); }); -- cgit v1.2.1 From 6dd2b4b91ea42b35b01267ace809cfdf83a1a252 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 16 May 2017 10:53:46 +0100 Subject: Added spec for window reload [ci skip] --- spec/javascripts/issue_show/components/app_spec.js | 35 ++++++++++++++-------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js index ce90ac0b4ac..36cd174d341 100644 --- a/spec/javascripts/issue_show/components/app_spec.js +++ b/spec/javascripts/issue_show/components/app_spec.js @@ -76,18 +76,6 @@ describe('Issuable output', () => { }); }); - it('changes element for `form` when open', (done) => { - vm.showForm = true; - - Vue.nextTick(() => { - expect( - vm.$el.tagName, - ).toBe('FORM'); - - done(); - }); - }); - it('does not show actions if permissions are incorrect', (done) => { vm.showForm = true; vm.canUpdate = false; @@ -121,6 +109,29 @@ describe('Issuable output', () => { }); }); + it('reloads the page if the confidential status has changed', (done) => { + spyOn(window.location, 'reload'); + spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => { + resolve({ + json() { + return { + confidential: true, + }; + }, + }); + })); + + vm.updateIssuable(); + + setTimeout(() => { + expect( + window.location.reload, + ).toHaveBeenCalled(); + + done(); + }); + }); + it('closes form on error', (done) => { spyOn(window, 'Flash').and.callThrough(); spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve, reject) => { -- cgit v1.2.1 From 2a9347410263d87c2b79a5facb04826fd8d8dacf Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 16 May 2017 12:15:20 +0100 Subject: Use dash instead of underscore [ci skip] --- .../issue_show/components/fields/confidential_checkbox.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/issue_show/components/fields/confidential_checkbox.vue b/app/assets/javascripts/issue_show/components/fields/confidential_checkbox.vue index ce0471d499a..a0ff08e9111 100644 --- a/app/assets/javascripts/issue_show/components/fields/confidential_checkbox.vue +++ b/app/assets/javascripts/issue_show/components/fields/confidential_checkbox.vue @@ -11,11 +11,11 @@ diff --git a/spec/javascripts/issue_show/components/edit_actions_spec.js b/spec/javascripts/issue_show/components/edit_actions_spec.js index 8fbaf6cfb2b..f6625b748b6 100644 --- a/spec/javascripts/issue_show/components/edit_actions_spec.js +++ b/spec/javascripts/issue_show/components/edit_actions_spec.js @@ -1,18 +1,26 @@ import Vue from 'vue'; import editActions from '~/issue_show/components/edit_actions.vue'; import eventHub from '~/issue_show/event_hub'; +import Store from '~/issue_show/stores'; describe('Edit Actions components', () => { let vm; beforeEach((done) => { const Component = Vue.extend(editActions); + const store = new Store({ + titleHtml: '', + descriptionHtml: '', + issuableRef: '', + }); + store.formState.title = 'test'; spyOn(eventHub, '$emit'); vm = new Component({ propsData: { canDestroy: true, + formState: store.formState, }, }).$mount(); @@ -41,6 +49,18 @@ describe('Edit Actions components', () => { }); }); + it('disables submit button when title is blank', (done) => { + vm.formState.title = ''; + + Vue.nextTick(() => { + expect( + vm.$el.querySelector('.btn-save').getAttribute('disabled'), + ).toBe('disabled'); + + done(); + }); + }); + describe('updateIssuable', () => { it('sends update.issauble event when clicking save button', () => { vm.$el.querySelector('.btn-save').click(); -- cgit v1.2.1 From 292780272e41740102eca2630a681887d137e67e Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 17 May 2017 14:35:02 +0100 Subject: Focus the description field in the inline form when mounted [ci skip] --- .../issue_show/components/fields/description.vue | 5 +++- .../components/fields/description_spec.js | 34 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 spec/javascripts/issue_show/components/fields/description_spec.js diff --git a/app/assets/javascripts/issue_show/components/fields/description.vue b/app/assets/javascripts/issue_show/components/fields/description.vue index b4c31811a0b..35b1ea6ff2b 100644 --- a/app/assets/javascripts/issue_show/components/fields/description.vue +++ b/app/assets/javascripts/issue_show/components/fields/description.vue @@ -20,6 +20,9 @@ components: { markdownField, }, + mounted() { + this.$refs.textarea.focus(); + }, }; @@ -39,7 +42,7 @@ data-supports-slash-commands="false" aria-label="Description" v-model="formState.description" - ref="textatea" + ref="textarea" slot="textarea"> diff --git a/spec/javascripts/issue_show/components/fields/description_spec.js b/spec/javascripts/issue_show/components/fields/description_spec.js new file mode 100644 index 00000000000..6ea52feb84f --- /dev/null +++ b/spec/javascripts/issue_show/components/fields/description_spec.js @@ -0,0 +1,34 @@ +import Vue from 'vue'; +import descriptionField from '~/issue_show/components/fields/description.vue'; + +describe('Description field component', () => { + let vm; + + beforeEach((done) => { + const Component = Vue.extend(descriptionField); + + // Needs an el in the DOM to be able to text the element is focused + const el = document.createElement('div'); + + document.body.appendChild(el); + + vm = new Component({ + el, + propsData: { + formState: { + description: '', + }, + markdownDocs: '/', + markdownPreviewUrl: '/', + }, + }).$mount(); + + Vue.nextTick(done); + }); + + it('focuses field when mounted', () => { + expect( + document.activeElement, + ).toBe(vm.$refs.textarea); + }); +}); -- cgit v1.2.1 From cf0cc9726d26212e79d915339acb06edda726ec3 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 17 May 2017 16:06:17 +0100 Subject: Stops formState from updating if the form is already open [ci skip] --- .../javascripts/issue_show/components/app.vue | 14 ++++++++------ spec/javascripts/issue_show/components/app_spec.js | 21 +++++++++++++++++++-- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue index 87757b1a35d..47d9a27e99e 100644 --- a/app/assets/javascripts/issue_show/components/app.vue +++ b/app/assets/javascripts/issue_show/components/app.vue @@ -79,12 +79,14 @@ export default { }, methods: { openForm() { - this.showForm = true; - this.store.formState = { - title: this.state.titleText, - confidential: this.isConfidential, - description: this.state.descriptionText, - }; + if (!this.showForm) { + this.showForm = true; + this.store.formState = { + title: this.state.titleText, + confidential: this.isConfidential, + description: this.state.descriptionText, + }; + } }, closeForm() { this.showForm = false; diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js index 36cd174d341..646fb455d7c 100644 --- a/spec/javascripts/issue_show/components/app_spec.js +++ b/spec/javascripts/issue_show/components/app_spec.js @@ -23,7 +23,7 @@ describe('Issuable output', () => { const IssuableDescriptionComponent = Vue.extend(issuableApp); Vue.http.interceptors.push(issueShowInterceptor(issueShowData.initialRequest)); - spyOn(eventHub, '$emit'); + spyOn(eventHub, '$emit').and.callThrough(); vm = new IssuableDescriptionComponent({ propsData: { @@ -34,8 +34,9 @@ describe('Issuable output', () => { initialTitle: '', initialDescriptionHtml: '', initialDescriptionText: '', - showForm: false, isConfidential: false, + markdownPreviewUrl: '/', + markdownDocs: '/', }, }).$mount(); }); @@ -89,6 +90,22 @@ describe('Issuable output', () => { }); }); + it('does not update formState if form is already open', (done) => { + vm.openForm(); + + vm.state.titleText = 'testing 123'; + + vm.openForm(); + + Vue.nextTick(() => { + expect( + vm.store.formState.title, + ).not.toBe('testing 123'); + + done(); + }); + }); + describe('updateIssuable', () => { it('correctly updates issuable data', (done) => { spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => { -- cgit v1.2.1 From 9c9a24cee8d961fa33675b857f2ad980e20e0a4b Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 18 May 2017 09:12:44 +0100 Subject: Fixed typo --- spec/javascripts/issue_show/components/fields/description_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/javascripts/issue_show/components/fields/description_spec.js b/spec/javascripts/issue_show/components/fields/description_spec.js index 6ea52feb84f..cdb5c9ab862 100644 --- a/spec/javascripts/issue_show/components/fields/description_spec.js +++ b/spec/javascripts/issue_show/components/fields/description_spec.js @@ -7,7 +7,7 @@ describe('Description field component', () => { beforeEach((done) => { const Component = Vue.extend(descriptionField); - // Needs an el in the DOM to be able to text the element is focused + // Needs an el in the DOM to be able to test the element is focused const el = document.createElement('div'); document.body.appendChild(el); -- cgit v1.2.1