Commit c23c141e authored by Rajat Jain's avatar Rajat Jain Committed by Kushal Pandya

Autosave description in epics

When editing an epic, the progress was previously lost due to
lack of localstorage syncing code. This commit adds support for
localstorage sync.
parent 224916f8
...@@ -159,9 +159,23 @@ export default { ...@@ -159,9 +159,23 @@ export default {
return !!this.state.updatedAt; return !!this.state.updatedAt;
}, },
issueChanged() { issueChanged() {
const descriptionChanged = this.initialDescriptionText !== this.store.formState.description; const {
const titleChanged = this.initialTitleText !== this.store.formState.title; store: {
return descriptionChanged || titleChanged; formState: { description, title },
},
initialDescriptionText,
initialTitleText,
} = this;
if (initialDescriptionText || description) {
return initialDescriptionText !== description;
}
if (initialTitleText || title) {
return initialTitleText !== title;
}
return false;
}, },
defaultErrorMessage() { defaultErrorMessage() {
return sprintf(s__('Error updating %{issuableType}'), { issuableType: this.issuableType }); return sprintf(s__('Error updating %{issuableType}'), { issuableType: this.issuableType });
......
...@@ -145,6 +145,7 @@ export default { ...@@ -145,6 +145,7 @@ export default {
></div> ></div>
<textarea <textarea
v-if="descriptionText" v-if="descriptionText"
ref="textarea"
v-model="descriptionText" v-model="descriptionText"
:data-update-url="updateUrl" :data-update-url="updateUrl"
class="hidden js-task-list-field" class="hidden js-task-list-field"
......
...@@ -17,6 +17,7 @@ export default { ...@@ -17,6 +17,7 @@ export default {
<label class="sr-only" for="issuable-title"> Title </label> <label class="sr-only" for="issuable-title"> Title </label>
<input <input
id="issuable-title" id="issuable-title"
ref="input"
v-model="formState.title" v-model="formState.title"
class="form-control qa-title-input" class="form-control qa-title-input"
type="text" type="text"
......
<script> <script>
import $ from 'jquery';
import lockedWarning from './locked_warning.vue'; import lockedWarning from './locked_warning.vue';
import titleField from './fields/title.vue'; import titleField from './fields/title.vue';
import descriptionField from './fields/description.vue'; import descriptionField from './fields/description.vue';
import editActions from './edit_actions.vue'; import editActions from './edit_actions.vue';
import descriptionTemplate from './fields/description_template.vue'; import descriptionTemplate from './fields/description_template.vue';
import Autosave from '~/autosave';
import eventHub from '../event_hub';
export default { export default {
components: { components: {
...@@ -68,6 +71,47 @@ export default { ...@@ -68,6 +71,47 @@ export default {
return this.issuableTemplates.length; return this.issuableTemplates.length;
}, },
}, },
created() {
eventHub.$on('delete.issuable', this.resetAutosave);
eventHub.$on('update.issuable', this.resetAutosave);
eventHub.$on('close.form', this.resetAutosave);
},
mounted() {
this.initAutosave();
},
beforeDestroy() {
eventHub.$off('delete.issuable', this.resetAutosave);
eventHub.$off('update.issuable', this.resetAutosave);
eventHub.$off('close.form', this.resetAutosave);
},
methods: {
initAutosave() {
const {
description: {
$refs: { textarea },
},
title: {
$refs: { input },
},
} = this.$refs;
this.autosaveDescription = new Autosave($(textarea), [
document.location.pathname,
document.location.search,
'description',
]);
this.autosaveTitle = new Autosave($(input), [
document.location.pathname,
document.location.search,
'title',
]);
},
resetAutosave() {
this.autosaveDescription.reset();
this.autosaveTitle.reset();
},
},
}; };
</script> </script>
...@@ -89,10 +133,11 @@ export default { ...@@ -89,10 +133,11 @@ export default {
'col-12': !hasIssuableTemplates, 'col-12': !hasIssuableTemplates,
}" }"
> >
<title-field :form-state="formState" :issuable-templates="issuableTemplates" /> <title-field ref="title" :form-state="formState" :issuable-templates="issuableTemplates" />
</div> </div>
</div> </div>
<description-field <description-field
ref="description"
:form-state="formState" :form-state="formState"
:markdown-preview-path="markdownPreviewPath" :markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath" :markdown-docs-path="markdownDocsPath"
......
---
title: Autosave description in epics
merge_request: 27296
author:
type: added
...@@ -470,4 +470,51 @@ describe('Issuable output', () => { ...@@ -470,4 +470,51 @@ describe('Issuable output', () => {
.catch(done.fail); .catch(done.fail);
}); });
}); });
describe('issueChanged', () => {
beforeEach(() => {
vm.store.formState.title = '';
vm.store.formState.description = '';
vm.initialDescriptionText = '';
vm.initialTitleText = '';
});
it('returns true when title is changed', () => {
vm.store.formState.title = 'RandomText';
expect(vm.issueChanged).toBe(true);
});
it('returns false when title is empty null', () => {
vm.store.formState.title = null;
expect(vm.issueChanged).toBe(false);
});
it('returns false when `initialTitleText` is null and `formState.title` is empty string', () => {
vm.store.formState.title = '';
vm.initialTitleText = null;
expect(vm.issueChanged).toBe(false);
});
it('returns true when description is changed', () => {
vm.store.formState.description = 'RandomText';
expect(vm.issueChanged).toBe(true);
});
it('returns false when description is empty null', () => {
vm.store.formState.title = null;
expect(vm.issueChanged).toBe(false);
});
it('returns false when `initialDescriptionText` is null and `formState.description` is empty string', () => {
vm.store.formState.description = '';
vm.initialDescriptionText = null;
expect(vm.issueChanged).toBe(false);
});
});
}); });
...@@ -63,4 +63,8 @@ describe('Description field component', () => { ...@@ -63,4 +63,8 @@ describe('Description field component', () => {
expect(eventHub.$emit).toHaveBeenCalled(); expect(eventHub.$emit).toHaveBeenCalled();
}); });
it('has a ref named `textarea`', () => {
expect(vm.$refs.textarea).not.toBeNull();
});
}); });
...@@ -41,4 +41,8 @@ describe('Title field component', () => { ...@@ -41,4 +41,8 @@ describe('Title field component', () => {
expect(eventHub.$emit).toHaveBeenCalled(); expect(eventHub.$emit).toHaveBeenCalled();
}); });
it('has a ref named `input`', () => {
expect(vm.$refs.input).not.toBeNull();
});
}); });
import Vue from 'vue'; import Vue from 'vue';
import formComponent from '~/issue_show/components/form.vue'; import formComponent from '~/issue_show/components/form.vue';
import eventHub from '~/issue_show/event_hub';
describe('Inline edit form component', () => { describe('Inline edit form component', () => {
let vm; let vm;
let autosave;
let autosaveObj;
beforeEach(done => { beforeEach(done => {
autosaveObj = { reset: jasmine.createSpy() };
autosave = spyOnDependency(formComponent, 'Autosave').and.returnValue(autosaveObj);
const Component = Vue.extend(formComponent); const Component = Vue.extend(formComponent);
vm = new Component({ vm = new Component({
...@@ -53,4 +60,22 @@ describe('Inline edit form component', () => { ...@@ -53,4 +60,22 @@ describe('Inline edit form component', () => {
done(); done();
}); });
}); });
it('initialized Autosave on mount', () => {
expect(autosave).toHaveBeenCalledTimes(2);
});
it('calls reset on autosave when eventHub emits appropriate events', () => {
eventHub.$emit('close.form');
expect(autosaveObj.reset).toHaveBeenCalledTimes(2);
eventHub.$emit('delete.issuable');
expect(autosaveObj.reset).toHaveBeenCalledTimes(4);
eventHub.$emit('update.issuable');
expect(autosaveObj.reset).toHaveBeenCalledTimes(6);
});
}); });
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