summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRajat Jain <rjain@gitlab.com>2019-04-11 15:29:32 +0530
committerRajat Jain <rjain@gitlab.com>2019-04-16 16:35:25 +0530
commitdb38ff9a88a0d4cbdad9d8cc867810a539d9ba93 (patch)
tree4d34e80dc0efb35f36c04fea7fcb78a4cff8344e
parent026c92d5fa82fac87386d5691c3d5b1e02f2eb5e (diff)
downloadgitlab-ce-ce-4681-autosave.tar.gz
Autosave description in epicsce-4681-autosave
When editing an epic, the progress was previously lost due to lack of localstorage syncing code. This commit adds support for localstorage sync.
-rw-r--r--app/assets/javascripts/issue_show/components/app.vue20
-rw-r--r--app/assets/javascripts/issue_show/components/description.vue1
-rw-r--r--app/assets/javascripts/issue_show/components/fields/title.vue1
-rw-r--r--app/assets/javascripts/issue_show/components/form.vue47
-rw-r--r--app/helpers/labels_helper.rb2
-rw-r--r--changelogs/unreleased/ce-4681-autosave.yml5
-rw-r--r--spec/javascripts/issue_show/components/app_spec.js47
-rw-r--r--spec/javascripts/issue_show/components/fields/description_spec.js4
-rw-r--r--spec/javascripts/issue_show/components/fields/title_spec.js4
-rw-r--r--spec/javascripts/issue_show/components/form_spec.js25
10 files changed, 151 insertions, 5 deletions
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue
index bd757a76ee7..ab0b4231255 100644
--- a/app/assets/javascripts/issue_show/components/app.vue
+++ b/app/assets/javascripts/issue_show/components/app.vue
@@ -159,9 +159,23 @@ export default {
return !!this.state.updatedAt;
},
issueChanged() {
- const descriptionChanged = this.initialDescriptionText !== this.store.formState.description;
- const titleChanged = this.initialTitleText !== this.store.formState.title;
- return descriptionChanged || titleChanged;
+ const {
+ store: {
+ formState: { description, title },
+ },
+ initialDescriptionText,
+ initialTitleText,
+ } = this;
+
+ if (initialDescriptionText || description) {
+ return initialDescriptionText !== description;
+ }
+
+ if (initialTitleText || title) {
+ return initialTitleText !== title;
+ }
+
+ return false;
},
defaultErrorMessage() {
return sprintf(s__('Error updating %{issuableType}'), { issuableType: this.issuableType });
diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue
index 732184dc782..385e9543973 100644
--- a/app/assets/javascripts/issue_show/components/description.vue
+++ b/app/assets/javascripts/issue_show/components/description.vue
@@ -145,6 +145,7 @@ export default {
></div>
<textarea
v-if="descriptionText"
+ ref="textarea"
v-model="descriptionText"
:data-update-url="updateUrl"
class="hidden js-task-list-field"
diff --git a/app/assets/javascripts/issue_show/components/fields/title.vue b/app/assets/javascripts/issue_show/components/fields/title.vue
index c3d7ba4907f..a3371cb9614 100644
--- a/app/assets/javascripts/issue_show/components/fields/title.vue
+++ b/app/assets/javascripts/issue_show/components/fields/title.vue
@@ -17,6 +17,7 @@ export default {
<label class="sr-only" for="issuable-title"> Title </label>
<input
id="issuable-title"
+ ref="input"
v-model="formState.title"
class="form-control qa-title-input"
type="text"
diff --git a/app/assets/javascripts/issue_show/components/form.vue b/app/assets/javascripts/issue_show/components/form.vue
index eade31f1d14..528ccb77efc 100644
--- a/app/assets/javascripts/issue_show/components/form.vue
+++ b/app/assets/javascripts/issue_show/components/form.vue
@@ -1,9 +1,12 @@
<script>
+import $ from 'jquery';
import lockedWarning from './locked_warning.vue';
import titleField from './fields/title.vue';
import descriptionField from './fields/description.vue';
import editActions from './edit_actions.vue';
import descriptionTemplate from './fields/description_template.vue';
+import Autosave from '~/autosave';
+import eventHub from '../event_hub';
export default {
components: {
@@ -68,6 +71,47 @@ export default {
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>
@@ -89,10 +133,11 @@ export default {
'col-12': !hasIssuableTemplates,
}"
>
- <title-field :form-state="formState" :issuable-templates="issuableTemplates" />
+ <title-field ref="title" :form-state="formState" :issuable-templates="issuableTemplates" />
</div>
</div>
<description-field
+ ref="description"
:form-state="formState"
:markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath"
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index e91e8f85515..a07c3f90a91 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -89,7 +89,7 @@ module LabelsHelper
def render_colored_label(label, label_suffix: '', tooltip: true, title: nil)
text_color = text_color_for_bg(label.color)
- title ||= tooltip ? label_tooltip_title(label) : ''
+ title ||= tooltip ? label_tooltip_title(label) : label.name
# Intentionally not using content_tag here so that this method can be called
# by LabelReferenceFilter
diff --git a/changelogs/unreleased/ce-4681-autosave.yml b/changelogs/unreleased/ce-4681-autosave.yml
new file mode 100644
index 00000000000..029954ec92b
--- /dev/null
+++ b/changelogs/unreleased/ce-4681-autosave.yml
@@ -0,0 +1,5 @@
+---
+title: Autosave description in epics
+merge_request: 27296
+author:
+type: added
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js
index dfc889773c1..2770743937e 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/javascripts/issue_show/components/app_spec.js
@@ -470,4 +470,51 @@ describe('Issuable output', () => {
.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);
+ });
+ });
});
diff --git a/spec/javascripts/issue_show/components/fields/description_spec.js b/spec/javascripts/issue_show/components/fields/description_spec.js
index 2c3efc8d4d4..f5f87a6bfbf 100644
--- a/spec/javascripts/issue_show/components/fields/description_spec.js
+++ b/spec/javascripts/issue_show/components/fields/description_spec.js
@@ -63,4 +63,8 @@ describe('Description field component', () => {
expect(eventHub.$emit).toHaveBeenCalled();
});
+
+ it('has a ref named `textarea`', () => {
+ expect(vm.$refs.textarea).not.toBeNull();
+ });
});
diff --git a/spec/javascripts/issue_show/components/fields/title_spec.js b/spec/javascripts/issue_show/components/fields/title_spec.js
index 4b96a1feb29..62dff983250 100644
--- a/spec/javascripts/issue_show/components/fields/title_spec.js
+++ b/spec/javascripts/issue_show/components/fields/title_spec.js
@@ -41,4 +41,8 @@ describe('Title field component', () => {
expect(eventHub.$emit).toHaveBeenCalled();
});
+
+ it('has a ref named `input`', () => {
+ expect(vm.$refs.input).not.toBeNull();
+ });
});
diff --git a/spec/javascripts/issue_show/components/form_spec.js b/spec/javascripts/issue_show/components/form_spec.js
index 523954013cf..b0f4ab2b12d 100644
--- a/spec/javascripts/issue_show/components/form_spec.js
+++ b/spec/javascripts/issue_show/components/form_spec.js
@@ -1,10 +1,17 @@
import Vue from 'vue';
import formComponent from '~/issue_show/components/form.vue';
+import eventHub from '~/issue_show/event_hub';
describe('Inline edit form component', () => {
let vm;
+ let autosave;
+ let autosaveObj;
beforeEach(done => {
+ autosaveObj = { reset: jasmine.createSpy() };
+
+ autosave = spyOnDependency(formComponent, 'Autosave').and.returnValue(autosaveObj);
+
const Component = Vue.extend(formComponent);
vm = new Component({
@@ -53,4 +60,22 @@ describe('Inline edit form component', () => {
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);
+ });
});