Commit e61ce83b authored by Phil Hughes's avatar Phil Hughes

Issue inline edit title field

[ci skip]
parent 47881254
...@@ -41,10 +41,6 @@ export default { ...@@ -41,10 +41,6 @@ export default {
required: false, required: false,
default: '', default: '',
}, },
showForm: {
type: Boolean,
required: true,
},
}, },
data() { data() {
const store = new Store({ const store = new Store({
...@@ -57,14 +53,26 @@ export default { ...@@ -57,14 +53,26 @@ export default {
store, store,
state: store.state, state: store.state,
formState: store.formState, formState: store.formState,
showForm: false,
}; };
}, },
computed: {
elementType() {
return this.showForm ? 'form' : 'div';
},
},
components: { components: {
descriptionComponent, descriptionComponent,
titleComponent, titleComponent,
editActions, editActions,
}, },
methods: { methods: {
openForm() {
this.showForm = true;
this.store.formState = {
title: this.state.titleText,
};
},
updateIssuable() { updateIssuable() {
this.service.updateIssuable(this.formState) this.service.updateIssuable(this.formState)
.then(() => { .then(() => {
...@@ -117,17 +125,21 @@ export default { ...@@ -117,17 +125,21 @@ export default {
eventHub.$on('delete.issuable', this.deleteIssuable); eventHub.$on('delete.issuable', this.deleteIssuable);
eventHub.$on('update.issuable', this.updateIssuable); eventHub.$on('update.issuable', this.updateIssuable);
eventHub.$on('open.form', this.openForm);
}, },
beforeDestroy() { beforeDestroy() {
eventHub.$off('delete.issuable', this.deleteIssuable); eventHub.$off('delete.issuable', this.deleteIssuable);
eventHub.$off('update.issuable', this.updateIssuable); eventHub.$off('update.issuable', this.updateIssuable);
eventHub.$off('open.form', this.openForm);
}, },
}; };
</script> </script>
<template> <template>
<div> <div :is="elementType">
<title-component <title-component
:store="store"
:show-form="showForm"
:issuable-ref="issuableRef" :issuable-ref="issuableRef"
:title-html="state.titleHtml" :title-html="state.titleHtml"
:title-text="state.titleText" /> :title-text="state.titleText" />
......
<script>
export default {
props: {
store: {
type: Object,
required: true,
},
},
data() {
return {
state: this.store.formState,
};
},
};
</script>
<template>
<fieldset>
<label
class="sr-only"
for="issue-title">
Title
</label>
<input
id="issue-title"
class="form-control"
type="text"
placeholder="Issue title"
aria-label="Issue title"
v-model="state.title" />
</fieldset>
</template>
<script> <script>
import animateMixin from '../mixins/animate'; import animateMixin from '../mixins/animate';
import titleField from './fields/title.vue';
export default { export default {
mixins: [animateMixin], mixins: [animateMixin],
components: {
titleField,
},
data() { data() {
return { return {
preAnimation: false, preAnimation: false,
...@@ -23,6 +27,14 @@ ...@@ -23,6 +27,14 @@
type: String, type: String,
required: true, required: true,
}, },
store: {
type: Object,
required: true,
},
showForm: {
type: Boolean,
required: true,
},
}, },
watch: { watch: {
titleHtml() { titleHtml() {
...@@ -41,7 +53,12 @@ ...@@ -41,7 +53,12 @@
</script> </script>
<template> <template>
<div>
<title-field
v-if="showForm"
:store="store" />
<h2 <h2
v-else
class="title" class="title"
:class="{ :class="{
'issue-realtime-pre-pulse': preAnimation, 'issue-realtime-pre-pulse': preAnimation,
...@@ -50,4 +67,5 @@ ...@@ -50,4 +67,5 @@
v-html="titleHtml" v-html="titleHtml"
> >
</h2> </h2>
</div>
</template> </template>
...@@ -35,25 +35,8 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -35,25 +35,8 @@ document.addEventListener('DOMContentLoaded', () => {
initialTitle: issuableTitleElement.innerHTML, initialTitle: issuableTitleElement.innerHTML,
initialDescriptionHtml: issuableDescriptionElement ? issuableDescriptionElement.innerHTML : '', initialDescriptionHtml: issuableDescriptionElement ? issuableDescriptionElement.innerHTML : '',
initialDescriptionText: issuableDescriptionTextarea ? issuableDescriptionTextarea.textContent : '', initialDescriptionText: issuableDescriptionTextarea ? issuableDescriptionTextarea.textContent : '',
showForm: false,
}; };
}, },
methods: {
openForm() {
this.showForm = true;
},
closeForm() {
this.showForm = false;
},
},
created() {
eventHub.$on('open.form', this.openForm);
eventHub.$on('close.form', this.closeForm);
},
beforeDestroy() {
eventHub.$off('open.form', this.openForm);
eventHub.$off('close.form', this.closeForm);
},
render(createElement) { render(createElement) {
return createElement('issuable-app', { return createElement('issuable-app', {
props: { props: {
...@@ -64,7 +47,6 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -64,7 +47,6 @@ document.addEventListener('DOMContentLoaded', () => {
initialTitle: this.initialTitle, initialTitle: this.initialTitle,
initialDescriptionHtml: this.initialDescriptionHtml, initialDescriptionHtml: this.initialDescriptionHtml,
initialDescriptionText: this.initialDescriptionText, initialDescriptionText: this.initialDescriptionText,
showForm: this.showForm,
}, },
}); });
}, },
......
...@@ -12,7 +12,9 @@ export default class Store { ...@@ -12,7 +12,9 @@ export default class Store {
taskStatus: '', taskStatus: '',
updatedAt: '', updatedAt: '',
}; };
this.formState = {}; this.formState = {
title: '',
};
} }
updateState(data) { updateState(data) {
......
...@@ -75,6 +75,18 @@ describe('Issuable output', () => { ...@@ -75,6 +75,18 @@ 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) => { it('does not show actions if permissions are incorrect', (done) => {
vm.showForm = true; vm.showForm = true;
vm.canUpdate = false; vm.canUpdate = false;
......
import Vue from 'vue';
import Store from '~/issue_show/stores';
import titleField from '~/issue_show/components/fields/title.vue';
describe('Title field component', () => {
let vm;
let store;
beforeEach(() => {
const Component = Vue.extend(titleField);
store = new Store({
titleHtml: '',
descriptionHtml: '',
issuableRef: '',
});
store.formState.title = 'test';
vm = new Component({
propsData: {
store,
},
}).$mount();
});
it('renders form control with formState title', () => {
expect(
vm.$el.querySelector('.form-control').value,
).toBe('test');
});
});
import Vue from 'vue'; import Vue from 'vue';
import Store from '~/issue_show/stores';
import titleComponent from '~/issue_show/components/title.vue'; import titleComponent from '~/issue_show/components/title.vue';
describe('Title component', () => { describe('Title component', () => {
...@@ -11,13 +12,19 @@ describe('Title component', () => { ...@@ -11,13 +12,19 @@ describe('Title component', () => {
issuableRef: '#1', issuableRef: '#1',
titleHtml: 'Testing <img />', titleHtml: 'Testing <img />',
titleText: 'Testing', titleText: 'Testing',
showForm: false,
store: new Store({
titleHtml: '',
descriptionHtml: '',
issuableRef: '',
}),
}, },
}).$mount(); }).$mount();
}); });
it('renders title HTML', () => { it('renders title HTML', () => {
expect( expect(
vm.$el.innerHTML.trim(), vm.$el.querySelector('h2').innerHTML.trim(),
).toBe('Testing <img>'); ).toBe('Testing <img>');
}); });
...@@ -39,12 +46,12 @@ describe('Title component', () => { ...@@ -39,12 +46,12 @@ describe('Title component', () => {
Vue.nextTick(() => { Vue.nextTick(() => {
expect( expect(
vm.$el.classList.contains('issue-realtime-pre-pulse'), vm.$el.querySelector('h2').classList.contains('issue-realtime-pre-pulse'),
).toBeTruthy(); ).toBeTruthy();
setTimeout(() => { setTimeout(() => {
expect( expect(
vm.$el.classList.contains('issue-realtime-trigger-pulse'), vm.$el.querySelector('h2').classList.contains('issue-realtime-trigger-pulse'),
).toBeTruthy(); ).toBeTruthy();
done(); done();
......
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