Commit aa686d44 authored by Donald Cook's avatar Donald Cook Committed by Jose Ivan Vargas

Warning template for issue description out of sync

Added and updated deprecated tests
parent 94c22179
...@@ -418,6 +418,7 @@ export default { ...@@ -418,6 +418,7 @@ export default {
<div v-if="canUpdate && showForm"> <div v-if="canUpdate && showForm">
<form-component <form-component
:form-state="formState" :form-state="formState"
:initial-description-text="initialDescriptionText"
:can-destroy="canDestroy" :can-destroy="canDestroy"
:issuable-templates="issuableTemplates" :issuable-templates="issuableTemplates"
:markdown-docs-path="markdownDocsPath" :markdown-docs-path="markdownDocsPath"
......
<script> <script>
import { GlAlert } from '@gitlab/ui';
import $ from 'jquery'; import $ from 'jquery';
import Autosave from '~/autosave'; import Autosave from '~/autosave';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
...@@ -15,6 +16,7 @@ export default { ...@@ -15,6 +16,7 @@ export default {
descriptionField, descriptionField,
descriptionTemplate, descriptionTemplate,
editActions, editActions,
GlAlert,
}, },
props: { props: {
canDestroy: { canDestroy: {
...@@ -69,6 +71,16 @@ export default { ...@@ -69,6 +71,16 @@ export default {
required: false, required: false,
default: true, default: true,
}, },
initialDescriptionText: {
type: String,
required: false,
default: '',
},
},
data() {
return {
showOutdatedDescriptionWarning: false,
};
}, },
computed: { computed: {
hasIssuableTemplates() { hasIssuableTemplates() {
...@@ -102,11 +114,17 @@ export default { ...@@ -102,11 +114,17 @@ export default {
}, },
} = this.$refs; } = this.$refs;
this.autosaveDescription = new Autosave($(textarea), [ this.autosaveDescription = new Autosave(
document.location.pathname, $(textarea),
document.location.search, [document.location.pathname, document.location.search, 'description'],
'description', null,
]); this.formState.lock_version,
);
const savedLockVersion = this.autosaveDescription.getSavedLockVersion();
this.showOutdatedDescriptionWarning =
savedLockVersion && `${this.formState.lock_version}` !== savedLockVersion;
this.autosaveTitle = new Autosave($(input), [ this.autosaveTitle = new Autosave($(input), [
document.location.pathname, document.location.pathname,
...@@ -118,6 +136,27 @@ export default { ...@@ -118,6 +136,27 @@ export default {
this.autosaveDescription.reset(); this.autosaveDescription.reset();
this.autosaveTitle.reset(); this.autosaveTitle.reset();
}, },
keepAutosave() {
const {
description: {
$refs: { textarea },
},
} = this.$refs;
textarea.focus();
this.showOutdatedDescriptionWarning = false;
},
discardAutosave() {
const {
description: {
$refs: { textarea },
},
} = this.$refs;
textarea.value = this.initialDescriptionText;
textarea.focus();
this.showOutdatedDescriptionWarning = false;
},
}, },
}; };
</script> </script>
...@@ -125,6 +164,21 @@ export default { ...@@ -125,6 +164,21 @@ export default {
<template> <template>
<form> <form>
<locked-warning v-if="showLockedWarning" /> <locked-warning v-if="showLockedWarning" />
<gl-alert
v-if="showOutdatedDescriptionWarning"
class="mb-3"
variant="warning"
primary-button-text="Keep"
secondary-button-text="Discard"
:dismissible="false"
@primaryAction="keepAutosave"
@secondaryAction="discardAutosave"
>{{
__(
'The comment you are editing has been changed by another user. Would you like to keep your changes and overwrite the new description or discard your changes?',
)
}}</gl-alert
>
<div class="row"> <div class="row">
<div v-if="hasIssuableTemplates" class="col-sm-4 col-lg-3"> <div v-if="hasIssuableTemplates" class="col-sm-4 col-lg-3">
<description-template <description-template
......
...@@ -31740,6 +31740,9 @@ msgstr "" ...@@ -31740,6 +31740,9 @@ msgstr ""
msgid "The collection of events added to the data gathered for that stage." msgid "The collection of events added to the data gathered for that stage."
msgstr "" msgstr ""
msgid "The comment you are editing has been changed by another user. Would you like to keep your changes and overwrite the new description or discard your changes?"
msgstr ""
msgid "The commit does not exist" msgid "The commit does not exist"
msgstr "" msgstr ""
......
import { GlAlert } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue'; import Vue from 'vue';
import mountComponent from 'helpers/vue_mount_component_helper';
import Autosave from '~/autosave'; import Autosave from '~/autosave';
import DescriptionTemplate from '~/issue_show/components/fields/description_template.vue';
import formComponent from '~/issue_show/components/form.vue'; import formComponent from '~/issue_show/components/form.vue';
import LockedWarning from '~/issue_show/components/locked_warning.vue';
import eventHub from '~/issue_show/event_hub'; import eventHub from '~/issue_show/event_hub';
jest.mock('~/autosave'); jest.mock('~/autosave');
describe('Inline edit form component', () => { describe('Inline edit form component', () => {
let vm; let wrapper;
const defaultProps = { const defaultProps = {
canDestroy: true, canDestroy: true,
formState: { formState: {
...@@ -24,22 +27,31 @@ describe('Inline edit form component', () => { ...@@ -24,22 +27,31 @@ describe('Inline edit form component', () => {
}; };
afterEach(() => { afterEach(() => {
vm.$destroy(); if (wrapper) {
wrapper.destroy();
wrapper = null;
}
}); });
const createComponent = (props) => { const createComponent = (props) => {
const Component = Vue.extend(formComponent); const Component = Vue.extend(formComponent);
vm = mountComponent(Component, { wrapper = shallowMount(Component, {
propsData: {
...defaultProps, ...defaultProps,
...props, ...props,
},
}); });
}; };
const findDescriptionTemplate = () => wrapper.findComponent(DescriptionTemplate);
const findLockedWarning = () => wrapper.findComponent(LockedWarning);
const findAlert = () => wrapper.findComponent(GlAlert);
it('does not render template selector if no templates exist', () => { it('does not render template selector if no templates exist', () => {
createComponent(); createComponent();
expect(vm.$el.querySelector('.js-issuable-selector-wrap')).toBeNull(); expect(findDescriptionTemplate().exists()).toBe(false);
}); });
it('renders template selector when templates as array exists', () => { it('renders template selector when templates as array exists', () => {
...@@ -49,7 +61,7 @@ describe('Inline edit form component', () => { ...@@ -49,7 +61,7 @@ describe('Inline edit form component', () => {
], ],
}); });
expect(vm.$el.querySelector('.js-issuable-selector-wrap')).not.toBeNull(); expect(findDescriptionTemplate().exists()).toBe(true);
}); });
it('renders template selector when templates as hash exists', () => { it('renders template selector when templates as hash exists', () => {
...@@ -59,19 +71,19 @@ describe('Inline edit form component', () => { ...@@ -59,19 +71,19 @@ describe('Inline edit form component', () => {
}, },
}); });
expect(vm.$el.querySelector('.js-issuable-selector-wrap')).not.toBeNull(); expect(findDescriptionTemplate().exists()).toBe(true);
}); });
it('hides locked warning by default', () => { it('hides locked warning by default', () => {
createComponent(); createComponent();
expect(vm.$el.querySelector('.alert')).toBeNull(); expect(findLockedWarning().exists()).toBe(false);
}); });
it('shows locked warning if formState is different', () => { it('shows locked warning if formState is different', () => {
createComponent({ formState: { ...defaultProps.formState, lockedWarningVisible: true } }); createComponent({ formState: { ...defaultProps.formState, lockedWarningVisible: true } });
expect(vm.$el.querySelector('.alert')).not.toBeNull(); expect(findLockedWarning().exists()).toBe(true);
}); });
it('hides locked warning when currently saving', () => { it('hides locked warning when currently saving', () => {
...@@ -79,7 +91,7 @@ describe('Inline edit form component', () => { ...@@ -79,7 +91,7 @@ describe('Inline edit form component', () => {
formState: { ...defaultProps.formState, updateLoading: true, lockedWarningVisible: true }, formState: { ...defaultProps.formState, updateLoading: true, lockedWarningVisible: true },
}); });
expect(vm.$el.querySelector('.alert')).toBeNull(); expect(findLockedWarning().exists()).toBe(false);
}); });
describe('autosave', () => { describe('autosave', () => {
...@@ -110,5 +122,23 @@ describe('Inline edit form component', () => { ...@@ -110,5 +122,23 @@ describe('Inline edit form component', () => {
expect(spy).toHaveBeenCalledTimes(6); expect(spy).toHaveBeenCalledTimes(6);
}); });
describe('outdated description', () => {
it('does not show warning if lock version from server is the same as the local lock version', () => {
createComponent();
expect(findAlert().exists()).toBe(false);
});
it('shows warning if lock version from server differs than the local lock version', async () => {
Autosave.prototype.getSavedLockVersion.mockResolvedValue('lock version from local storage');
createComponent({
formState: { ...defaultProps.formState, lock_version: 'lock version from server' },
});
await wrapper.vm.$nextTick();
expect(findAlert().exists()).toBe(true);
});
});
}); });
}); });
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