diff options
author | Phil Hughes <me@iamphill.com> | 2017-05-18 12:44:27 +0100 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2017-05-18 12:44:27 +0100 |
commit | b8647134d926659b1629e9a58ce042a5ffdf4250 (patch) | |
tree | 3aabc707ecdc28c8fdea331b8ef3675cbeda2814 | |
parent | 907dd68e0a18e995dc5772c9bc8117aa150edc25 (diff) | |
parent | f5675666a11ff296e27b6dbdf0f789242fcd1b7f (diff) | |
download | gitlab-ce-b8647134d926659b1629e9a58ce042a5ffdf4250.tar.gz |
Merge branch 'issue-edit-inline' into issue-edit-inline-move-project
[ci skip]
11 files changed, 142 insertions, 10 deletions
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue index 9d5e69585f1..2a7a7d3900e 100644 --- a/app/assets/javascripts/issue_show/components/app.vue +++ b/app/assets/javascripts/issue_show/components/app.vue @@ -45,6 +45,10 @@ export default { required: false, default: '', }, + isConfidential: { + type: Boolean, + required: true, + }, markdownPreviewUrl: { type: String, required: true, @@ -83,12 +87,15 @@ export default { }, methods: { openForm() { - this.showForm = true; - this.store.formState = { - title: this.state.titleText, - description: this.state.descriptionText, - move_to_project_id: 0, - }; + if (!this.showForm) { + this.showForm = true; + this.store.formState = { + title: this.state.titleText, + confidential: this.isConfidential, + description: this.state.descriptionText, + move_to_project_id: 0, + }; + } }, closeForm() { this.showForm = false; @@ -99,6 +106,8 @@ export default { .then((data) => { if (location.pathname !== data.path) { gl.utils.visitUrl(data.path); + } if (data.confidential !== this.isConfidential) { + gl.utils.visitUrl(location.href); } eventHub.$emit('close.form'); diff --git a/app/assets/javascripts/issue_show/components/edit_actions.vue b/app/assets/javascripts/issue_show/components/edit_actions.vue index 4cefb236d32..e57b867f93b 100644 --- a/app/assets/javascripts/issue_show/components/edit_actions.vue +++ b/app/assets/javascripts/issue_show/components/edit_actions.vue @@ -7,6 +7,10 @@ type: Boolean, required: true, }, + formState: { + type: Object, + required: true, + }, }, data() { return { @@ -14,6 +18,11 @@ updateLoading: false, }; }, + computed: { + isSubmitEnabled() { + return this.formState.title.trim() !== ''; + }, + }, methods: { updateIssuable() { this.updateLoading = true; @@ -38,9 +47,9 @@ <div class="prepend-top-default append-bottom-default clearfix"> <button class="btn btn-save pull-left" - :class="{ disabled: updateLoading }" + :class="{ disabled: updateLoading || !isSubmitEnabled }" type="submit" - :disabled="updateLoading" + :disabled="updateLoading || !isSubmitEnabled" @click="updateIssuable"> Save changes <i 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..a0ff08e9111 --- /dev/null +++ b/app/assets/javascripts/issue_show/components/fields/confidential_checkbox.vue @@ -0,0 +1,23 @@ +<script> + export default { + props: { + formState: { + type: Object, + required: true, + }, + }, + }; +</script> + +<template> + <fieldset class="checkbox"> + <label for="issue-confidential"> + <input + type="checkbox" + value="1" + id="issue-confidential" + v-model="formState.confidential" /> + This issue is confidential and should only be visible to team members with at least Reporter access. + </label> + </fieldset> +</template> 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(); + }, }; </script> @@ -39,7 +42,7 @@ data-supports-slash-commands="false" aria-label="Description" v-model="formState.description" - ref="textatea" + ref="textarea" slot="textarea"> </textarea> </markdown-field> diff --git a/app/assets/javascripts/issue_show/components/form.vue b/app/assets/javascripts/issue_show/components/form.vue index 4cfc95e9888..1b50b1dcd03 100644 --- a/app/assets/javascripts/issue_show/components/form.vue +++ b/app/assets/javascripts/issue_show/components/form.vue @@ -3,6 +3,7 @@ import descriptionField from './fields/description.vue'; import editActions from './edit_actions.vue'; import projectMove from './fields/project_move.vue'; + import confidentialCheckbox from './fields/confidential_checkbox.vue'; export default { props: { @@ -36,6 +37,7 @@ descriptionField, editActions, projectMove, + confidentialCheckbox, }, }; </script> @@ -44,6 +46,8 @@ <form> <title-field :form-state="formState" /> + <confidential-checkbox + :form-state="formState" /> <description-field :form-state="formState" :markdown-preview-url="markdownPreviewUrl" @@ -53,6 +57,7 @@ :form-state="formState" :projects-autocomplete-url="projectsAutocompleteUrl" /> <edit-actions + :form-state="formState" :can-destroy="canDestroy" /> </form> </template> diff --git a/app/assets/javascripts/issue_show/index.js b/app/assets/javascripts/issue_show/index.js index 666d144f958..ce262a75e11 100644 --- a/app/assets/javascripts/issue_show/index.js +++ b/app/assets/javascripts/issue_show/index.js @@ -26,6 +26,7 @@ document.addEventListener('DOMContentLoaded', () => { canMove, endpoint, issuableRef, + isConfidential, markdownPreviewUrl, markdownDocs, projectsAutocompleteUrl, @@ -40,6 +41,7 @@ document.addEventListener('DOMContentLoaded', () => { initialTitle: issuableTitleElement.innerHTML, initialDescriptionHtml: issuableDescriptionElement ? issuableDescriptionElement.innerHTML : '', initialDescriptionText: issuableDescriptionTextarea ? issuableDescriptionTextarea.textContent : '', + isConfidential: gl.utils.convertPermissionToBoolean(isConfidential), markdownPreviewUrl, markdownDocs, projectsAutocompleteUrl, @@ -56,6 +58,7 @@ document.addEventListener('DOMContentLoaded', () => { initialTitle: this.initialTitle, initialDescriptionHtml: this.initialDescriptionHtml, initialDescriptionText: this.initialDescriptionText, + isConfidential: this.isConfidential, markdownPreviewUrl: this.markdownPreviewUrl, markdownDocs: this.markdownDocs, projectsAutocompleteUrl: this.projectsAutocompleteUrl, diff --git a/app/assets/javascripts/issue_show/stores/index.js b/app/assets/javascripts/issue_show/stores/index.js index 5717f0a8e74..1135bc0bfb5 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, description: '', move_to_project_id: 0, }; diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index a35de038f2f..8359910c679 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -56,6 +56,7 @@ "can-destroy" => can?(current_user, :destroy_issue, @issue).to_s, "can-move" => @issue.can_move?(current_user).to_s, "issuable-ref" => @issue.to_reference, + "is-confidential" => @issue.confidential.to_s, "markdown-preview-url" => preview_markdown_path(@project), "markdown-docs" => help_page_path('user/markdown'), "projects-autocomplete-url" => autocomplete_projects_path(project_id: @project.id), diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js index 700910c241a..8848ca4e078 100644 --- a/spec/javascripts/issue_show/components/app_spec.js +++ b/spec/javascripts/issue_show/components/app_spec.js @@ -14,7 +14,7 @@ const issueShowInterceptor = data => (request, next) => { })); }; -describe('Issuable output', () => { +fdescribe('Issuable output', () => { document.body.innerHTML = '<span id="task_status"></span>'; let vm; @@ -38,6 +38,7 @@ describe('Issuable output', () => { markdownPreviewUrl: '/', markdownDocs: '/', projectsAutocompleteUrl: '/', + isConfidential: false, }, }).$mount(); }); @@ -92,6 +93,29 @@ describe('Issuable output', () => { }); describe('updateIssuable', () => { + it('reloads the page if the confidential status has changed', (done) => { + spyOn(gl.utils, 'visitUrl'); + spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => { + resolve({ + json() { + return { + confidential: true, + }; + }, + }); + })); + + vm.updateIssuable(); + + setTimeout(() => { + expect( + gl.utils.visitUrl, + ).toHaveBeenCalledWith(location.href); + + done(); + }); + }); + it('correctly updates issuable data', (done) => { spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => { resolve(); 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(); 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..cdb5c9ab862 --- /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 test 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); + }); +}); |