summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Hughes <me@iamphill.com>2017-05-18 12:44:27 +0100
committerPhil Hughes <me@iamphill.com>2017-05-18 12:44:27 +0100
commitb8647134d926659b1629e9a58ce042a5ffdf4250 (patch)
tree3aabc707ecdc28c8fdea331b8ef3675cbeda2814
parent907dd68e0a18e995dc5772c9bc8117aa150edc25 (diff)
parentf5675666a11ff296e27b6dbdf0f789242fcd1b7f (diff)
downloadgitlab-ce-b8647134d926659b1629e9a58ce042a5ffdf4250.tar.gz
Merge branch 'issue-edit-inline' into issue-edit-inline-move-project
[ci skip]
-rw-r--r--app/assets/javascripts/issue_show/components/app.vue21
-rw-r--r--app/assets/javascripts/issue_show/components/edit_actions.vue13
-rw-r--r--app/assets/javascripts/issue_show/components/fields/confidential_checkbox.vue23
-rw-r--r--app/assets/javascripts/issue_show/components/fields/description.vue5
-rw-r--r--app/assets/javascripts/issue_show/components/form.vue5
-rw-r--r--app/assets/javascripts/issue_show/index.js3
-rw-r--r--app/assets/javascripts/issue_show/stores/index.js1
-rw-r--r--app/views/projects/issues/show.html.haml1
-rw-r--r--spec/javascripts/issue_show/components/app_spec.js26
-rw-r--r--spec/javascripts/issue_show/components/edit_actions_spec.js20
-rw-r--r--spec/javascripts/issue_show/components/fields/description_spec.js34
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);
+ });
+});