Issue inline edit title field
[ci skip]
This commit is contained in:
parent
478812543c
commit
b1affe07a1
8 changed files with 133 additions and 31 deletions
|
@ -57,8 +57,14 @@ export default {
|
|||
store,
|
||||
state: store.state,
|
||||
formState: store.formState,
|
||||
showForm: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
elementType() {
|
||||
return this.showForm ? 'form' : 'div';
|
||||
},
|
||||
},
|
||||
components: {
|
||||
descriptionComponent,
|
||||
titleComponent,
|
||||
|
@ -90,6 +96,14 @@ export default {
|
|||
});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
openForm() {
|
||||
this.showForm = true;
|
||||
this.store.formState = {
|
||||
title: this.state.titleText,
|
||||
};
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.service = new Service(this.endpoint);
|
||||
this.poll = new Poll({
|
||||
|
@ -117,17 +131,21 @@ export default {
|
|||
|
||||
eventHub.$on('delete.issuable', this.deleteIssuable);
|
||||
eventHub.$on('update.issuable', this.updateIssuable);
|
||||
eventHub.$on('open.form', this.openForm);
|
||||
},
|
||||
beforeDestroy() {
|
||||
eventHub.$off('delete.issuable', this.deleteIssuable);
|
||||
eventHub.$off('update.issuable', this.updateIssuable);
|
||||
eventHub.$on('open.form', this.openForm);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div :is="elementType">
|
||||
<title-component
|
||||
:store="store"
|
||||
:show-form="showForm"
|
||||
:issuable-ref="issuableRef"
|
||||
:title-html="state.titleHtml"
|
||||
:title-text="state.titleText" />
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<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>
|
|
@ -1,8 +1,12 @@
|
|||
<script>
|
||||
import animateMixin from '../mixins/animate';
|
||||
import titleField from './fields/title.vue';
|
||||
|
||||
export default {
|
||||
mixins: [animateMixin],
|
||||
components: {
|
||||
titleField,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
preAnimation: false,
|
||||
|
@ -23,6 +27,14 @@
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
store: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
showForm: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
titleHtml() {
|
||||
|
@ -41,13 +53,19 @@
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<h2
|
||||
class="title"
|
||||
:class="{
|
||||
'issue-realtime-pre-pulse': preAnimation,
|
||||
'issue-realtime-trigger-pulse': pulseAnimation
|
||||
}"
|
||||
v-html="titleHtml"
|
||||
>
|
||||
</h2>
|
||||
<div>
|
||||
<title-field
|
||||
v-if="showForm"
|
||||
:store="store" />
|
||||
<h2
|
||||
v-else
|
||||
class="title"
|
||||
:class="{
|
||||
'issue-realtime-pre-pulse': preAnimation,
|
||||
'issue-realtime-trigger-pulse': pulseAnimation
|
||||
}"
|
||||
v-html="titleHtml"
|
||||
>
|
||||
</h2>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -35,25 +35,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
initialTitle: issuableTitleElement.innerHTML,
|
||||
initialDescriptionHtml: issuableDescriptionElement ? issuableDescriptionElement.innerHTML : '',
|
||||
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) {
|
||||
return createElement('issuable-app', {
|
||||
props: {
|
||||
|
|
|
@ -12,7 +12,9 @@ export default class Store {
|
|||
taskStatus: '',
|
||||
updatedAt: '',
|
||||
};
|
||||
this.formState = {};
|
||||
this.formState = {
|
||||
title: '',
|
||||
};
|
||||
}
|
||||
|
||||
updateState(data) {
|
||||
|
|
|
@ -75,6 +75,18 @@ describe('Issuable output', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('changes element to `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;
|
||||
|
|
30
spec/javascripts/issue_show/components/fields/title_spec.js
Normal file
30
spec/javascripts/issue_show/components/fields/title_spec.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
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');
|
||||
});
|
||||
});
|
|
@ -1,4 +1,5 @@
|
|||
import Vue from 'vue';
|
||||
import Store from '~/issue_show/stores';
|
||||
import titleComponent from '~/issue_show/components/title.vue';
|
||||
|
||||
describe('Title component', () => {
|
||||
|
@ -11,13 +12,19 @@ describe('Title component', () => {
|
|||
issuableRef: '#1',
|
||||
titleHtml: 'Testing <img />',
|
||||
titleText: 'Testing',
|
||||
showForm: false,
|
||||
store: new Store({
|
||||
titleHtml: '',
|
||||
descriptionHtml: '',
|
||||
issuableRef: '',
|
||||
}),
|
||||
},
|
||||
}).$mount();
|
||||
});
|
||||
|
||||
it('renders title HTML', () => {
|
||||
expect(
|
||||
vm.$el.innerHTML.trim(),
|
||||
vm.$el.querySelector('h2').innerHTML.trim(),
|
||||
).toBe('Testing <img>');
|
||||
});
|
||||
|
||||
|
@ -39,12 +46,12 @@ describe('Title component', () => {
|
|||
|
||||
Vue.nextTick(() => {
|
||||
expect(
|
||||
vm.$el.classList.contains('issue-realtime-pre-pulse'),
|
||||
vm.$el.querySelector('h2').classList.contains('issue-realtime-pre-pulse'),
|
||||
).toBeTruthy();
|
||||
|
||||
setTimeout(() => {
|
||||
expect(
|
||||
vm.$el.classList.contains('issue-realtime-trigger-pulse'),
|
||||
vm.$el.querySelector('h2').classList.contains('issue-realtime-trigger-pulse'),
|
||||
).toBeTruthy();
|
||||
|
||||
done();
|
||||
|
|
Loading…
Reference in a new issue