Commit 203a94e3 authored by Phil Hughes's avatar Phil Hughes

Merge branch 'issue-edit-inline' into issue-edit-inline-description-field-specs

[ci skip]
parents 6c34f5a2 f5675666
......@@ -41,6 +41,10 @@ export default {
required: false,
default: '',
},
isConfidential: {
type: Boolean,
required: true,
},
markdownPreviewUrl: {
type: String,
required: true,
......@@ -75,18 +79,27 @@ export default {
},
methods: {
openForm() {
this.showForm = true;
this.store.formState = {
title: this.state.titleText,
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;
},
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(() => {
......
......@@ -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
......
<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>
......@@ -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>
......
......@@ -2,6 +2,7 @@
import titleField from './fields/title.vue';
import descriptionField from './fields/description.vue';
import editActions from './edit_actions.vue';
import confidentialCheckbox from './fields/confidential_checkbox.vue';
export default {
props: {
......@@ -26,6 +27,7 @@
titleField,
descriptionField,
editActions,
confidentialCheckbox,
},
};
</script>
......@@ -34,11 +36,14 @@
<form>
<title-field
:form-state="formState" />
<confidential-checkbox
:form-state="formState" />
<description-field
:form-state="formState"
:markdown-preview-url="markdownPreviewUrl"
:markdown-docs="markdownDocs" />
<edit-actions
:form-state="formState"
:can-destroy="canDestroy" />
</form>
</template>
......@@ -25,6 +25,7 @@ document.addEventListener('DOMContentLoaded', () => {
canDestroy,
endpoint,
issuableRef,
isConfidential,
markdownPreviewUrl,
markdownDocs,
} = issuableElement.dataset;
......@@ -37,6 +38,7 @@ document.addEventListener('DOMContentLoaded', () => {
initialTitle: issuableTitleElement.innerHTML,
initialDescriptionHtml: issuableDescriptionElement ? issuableDescriptionElement.innerHTML : '',
initialDescriptionText: issuableDescriptionTextarea ? issuableDescriptionTextarea.textContent : '',
isConfidential: gl.utils.convertPermissionToBoolean(isConfidential),
markdownPreviewUrl,
markdownDocs,
};
......@@ -51,6 +53,7 @@ document.addEventListener('DOMContentLoaded', () => {
initialTitle: this.initialTitle,
initialDescriptionHtml: this.initialDescriptionHtml,
initialDescriptionText: this.initialDescriptionText,
isConfidential: this.isConfidential,
markdownPreviewUrl: this.markdownPreviewUrl,
markdownDocs: this.markdownDocs,
},
......
......@@ -14,6 +14,7 @@ export default class Store {
};
this.formState = {
title: '',
confidential: false,
description: '',
};
}
......
......@@ -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,
"markdown-preview-url" => preview_markdown_path(@project),
"markdown-docs" => help_page_path('user/markdown'),
} }
......
......@@ -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,7 +34,9 @@ describe('Issuable output', () => {
initialTitle: '',
initialDescriptionHtml: '',
initialDescriptionText: '',
showForm: false,
isConfidential: false,
markdownPreviewUrl: '/',
markdownDocs: '/',
},
}).$mount();
});
......@@ -88,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) => {
......@@ -108,6 +126,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) => {
......
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();
......
......@@ -8,6 +8,7 @@ describe('Description field component', () => {
beforeEach((done) => {
const Component = Vue.extend(descriptionField);
const el = document.createElement('div');
store = new Store({
titleHtml: '',
descriptionHtml: '',
......@@ -15,7 +16,10 @@ describe('Description field component', () => {
});
store.formState.description = 'test';
document.body.appendChild(el);
vm = new Component({
el,
propsData: {
markdownPreviewUrl: '/',
markdownDocs: '/',
......@@ -43,4 +47,10 @@ describe('Description field component', () => {
done();
});
});
it('focuses field when mounted', () => {
expect(
document.activeElement,
).toBe(vm.$refs.textarea);
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment