diff --git a/app/assets/javascripts/static_site_editor/components/edit_meta_controls.vue b/app/assets/javascripts/static_site_editor/components/edit_meta_controls.vue index 9f75c65a316e1ae5b769a04cae76480a39fa8cf3..fb80b158b4602b844ffbdec495d47ebb8e4a2e45 100644 --- a/app/assets/javascripts/static_site_editor/components/edit_meta_controls.vue +++ b/app/assets/javascripts/static_site_editor/components/edit_meta_controls.vue @@ -1,6 +1,5 @@ <script> import { GlForm, GlFormGroup, GlFormInput, GlFormTextarea } from '@gitlab/ui'; -import AccessorUtilities from '~/lib/utils/accessor'; export default { components: { @@ -19,55 +18,25 @@ export default { required: true, }, }, - data() { - return { - editable: { - title: this.title, - description: this.description, - }, - }; - }, - computed: { - editableStorageKey() { - return this.getId('local-storage', 'editable'); - }, - hasLocalStorage() { - return AccessorUtilities.isLocalStorageAccessSafe(); - }, - }, mounted() { - this.initCachedEditable(); this.preSelect(); }, methods: { getId(type, key) { return `sse-merge-request-meta-${type}-${key}`; }, - initCachedEditable() { - if (this.hasLocalStorage) { - const cachedEditable = JSON.parse(localStorage.getItem(this.editableStorageKey)); - if (cachedEditable) { - this.editable = cachedEditable; - } - } - }, preSelect() { this.$nextTick(() => { this.$refs.title.$el.select(); }); }, - resetCachedEditable() { - if (this.hasLocalStorage) { - window.localStorage.removeItem(this.editableStorageKey); - } - }, - onUpdate() { - const payload = { ...this.editable }; + onUpdate(field, value) { + const payload = { + title: this.title, + description: this.description, + [field]: value, + }; this.$emit('updateSettings', payload); - - if (this.hasLocalStorage) { - window.localStorage.setItem(this.editableStorageKey, JSON.stringify(payload)); - } }, }, }; @@ -83,9 +52,9 @@ export default { <gl-form-input :id="getId('control', 'title')" ref="title" - v-model.lazy="editable.title" + :value="title" type="text" - @input="onUpdate" + @input="onUpdate('title', $event)" /> </gl-form-group> @@ -96,8 +65,8 @@ export default { > <gl-form-textarea :id="getId('control', 'description')" - v-model.lazy="editable.description" - @input="onUpdate" + :value="description" + @input="onUpdate('description', $event)" /> </gl-form-group> </gl-form> diff --git a/app/assets/javascripts/static_site_editor/components/edit_meta_modal.vue b/app/assets/javascripts/static_site_editor/components/edit_meta_modal.vue index 4e5245bd892a849687619dcff044a0cb841916a6..e83af198bf544cc6fd3eaa535e582f021da9992f 100644 --- a/app/assets/javascripts/static_site_editor/components/edit_meta_modal.vue +++ b/app/assets/javascripts/static_site_editor/components/edit_meta_modal.vue @@ -1,13 +1,17 @@ <script> import { GlModal } from '@gitlab/ui'; import { __, s__, sprintf } from '~/locale'; +import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; import EditMetaControls from './edit_meta_controls.vue'; +import { MR_META_LOCAL_STORAGE_KEY } from '../constants'; + export default { components: { GlModal, EditMetaControls, + LocalStorageSync, }, props: { sourcePath: { @@ -17,6 +21,7 @@ export default { }, data() { return { + clearStorage: false, mergeRequestMeta: { title: sprintf(s__(`StaticSiteEditor|Update %{sourcePath} file`), { sourcePath: this.sourcePath, @@ -51,7 +56,7 @@ export default { }, onPrimary() { this.$emit('primary', this.mergeRequestMeta); - this.$refs.editMetaControls.resetCachedEditable(); + this.clearStorage = true; }, onSecondary() { this.hide(); @@ -60,6 +65,7 @@ export default { this.mergeRequestMeta = { ...mergeRequestMeta }; }, }, + storageKey: MR_META_LOCAL_STORAGE_KEY, }; </script> @@ -75,6 +81,12 @@ export default { @secondary="onSecondary" @hide="() => $emit('hide')" > + <local-storage-sync + v-model="mergeRequestMeta" + :storage-key="$options.storageKey" + :clear="clearStorage" + as-json + /> <edit-meta-controls ref="editMetaControls" :title="mergeRequestMeta.title" diff --git a/app/assets/javascripts/static_site_editor/constants.js b/app/assets/javascripts/static_site_editor/constants.js index 49db9ab7ca50f05be8dfa2e59399c5e189ca0e0f..78eafe40890ae388fc2952326b2acfb792df55ae 100644 --- a/app/assets/javascripts/static_site_editor/constants.js +++ b/app/assets/javascripts/static_site_editor/constants.js @@ -21,3 +21,5 @@ export const TRACKING_ACTION_CREATE_MERGE_REQUEST = 'create_merge_request'; export const TRACKING_ACTION_INITIALIZE_EDITOR = 'initialize_editor'; export const DEFAULT_IMAGE_UPLOAD_PATH = 'source/images/uploads/'; + +export const MR_META_LOCAL_STORAGE_KEY = 'sse-merge-request-meta-storage-key'; diff --git a/app/assets/javascripts/vue_shared/components/local_storage_sync.vue b/app/assets/javascripts/vue_shared/components/local_storage_sync.vue index 80c03342f11a8692c8f50c0467bf233f2557f801..33e77b6510c1f0d7fc6b26d2a9148f40c3b22f58 100644 --- a/app/assets/javascripts/vue_shared/components/local_storage_sync.vue +++ b/app/assets/javascripts/vue_shared/components/local_storage_sync.vue @@ -22,11 +22,21 @@ export default { required: false, default: true, }, + clear: { + type: Boolean, + required: false, + default: false, + }, }, watch: { value(newVal) { this.saveValue(this.serialize(newVal)); }, + clear(newVal) { + if (newVal) { + localStorage.removeItem(this.storageKey); + } + }, }, mounted() { // On mount, trigger update if we actually have a localStorageValue diff --git a/spec/frontend/static_site_editor/components/edit_meta_controls_spec.js b/spec/frontend/static_site_editor/components/edit_meta_controls_spec.js index 191f91be07603e0542bb8934aadfd647f852cbf6..3f99768aa08f47595f4b629d5ea3106da25db505 100644 --- a/spec/frontend/static_site_editor/components/edit_meta_controls_spec.js +++ b/spec/frontend/static_site_editor/components/edit_meta_controls_spec.js @@ -1,6 +1,5 @@ import { shallowMount } from '@vue/test-utils'; -import { useLocalStorageSpy } from 'helpers/local_storage_helper'; import { GlFormInput, GlFormTextarea } from '@gitlab/ui'; import EditMetaControls from '~/static_site_editor/components/edit_meta_controls.vue'; @@ -8,8 +7,6 @@ import EditMetaControls from '~/static_site_editor/components/edit_meta_controls import { mergeRequestMeta } from '../mock_data'; describe('~/static_site_editor/components/edit_meta_controls.vue', () => { - useLocalStorageSpy(); - let wrapper; let mockSelect; let mockGlFormInputTitleInstance; @@ -86,14 +83,5 @@ describe('~/static_site_editor/components/edit_meta_controls.vue', () => { expect(wrapper.emitted('updateSettings')[0][0]).toMatchObject(newSettings); }); - - it('should remember the input changes', () => { - findGlFormInputTitle().vm.$emit('input', newTitle); - findGlFormTextAreaDescription().vm.$emit('input', newDescription); - - const newSettings = { title: newTitle, description: newDescription }; - - expect(localStorage.setItem).toHaveBeenCalledWith(storageKey, JSON.stringify(newSettings)); - }); }); }); diff --git a/spec/frontend/static_site_editor/components/edit_meta_modal_spec.js b/spec/frontend/static_site_editor/components/edit_meta_modal_spec.js index 7a5685033f3b67ef02999c5bd7a5b3ce92eee672..da4c225d25b35c54d909262649a58281bcfa5b4d 100644 --- a/spec/frontend/static_site_editor/components/edit_meta_modal_spec.js +++ b/spec/frontend/static_site_editor/components/edit_meta_modal_spec.js @@ -1,13 +1,15 @@ import { shallowMount } from '@vue/test-utils'; - import { GlModal } from '@gitlab/ui'; - +import { useLocalStorageSpy } from 'helpers/local_storage_helper'; +import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; import EditMetaModal from '~/static_site_editor/components/edit_meta_modal.vue'; import EditMetaControls from '~/static_site_editor/components/edit_meta_controls.vue'; - +import { MR_META_LOCAL_STORAGE_KEY } from '~/static_site_editor/constants'; import { sourcePath, mergeRequestMeta } from '../mock_data'; describe('~/static_site_editor/components/edit_meta_modal.vue', () => { + useLocalStorageSpy(); + let wrapper; let resetCachedEditable; let mockEditMetaControlsInstance; @@ -30,6 +32,11 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => { const findGlModal = () => wrapper.find(GlModal); const findEditMetaControls = () => wrapper.find(EditMetaControls); + const findLocalStorageSync = () => wrapper.find(LocalStorageSync); + + beforeEach(() => { + localStorage.setItem(MR_META_LOCAL_STORAGE_KEY); + }); beforeEach(() => { buildWrapper(); @@ -43,6 +50,16 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => { wrapper = null; }); + it('initializes initial merge request meta with local storage data', async () => { + const localStorageMeta = { title: 'stored title', description: 'stored description' }; + + findLocalStorageSync().vm.$emit('input', localStorageMeta); + + await wrapper.vm.$nextTick(); + + expect(findEditMetaControls().props()).toEqual(localStorageMeta); + }); + it('renders the modal', () => { expect(findGlModal().exists()).toBe(true); }); @@ -63,18 +80,32 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => { expect(findEditMetaControls().props('description')).toBe(description); }); - it('emits the primary event with mergeRequestMeta', () => { - findGlModal().vm.$emit('primary', mergeRequestMeta); - expect(wrapper.emitted('primary')).toEqual([[mergeRequestMeta]]); - }); + describe('when save button is clicked', () => { + beforeEach(() => { + findGlModal().vm.$emit('primary', mergeRequestMeta); + }); - it('calls resetCachedEditable on EditMetaControls when primary emits', () => { - findGlModal().vm.$emit('primary', mergeRequestMeta); - expect(mockEditMetaControlsInstance.resetCachedEditable).toHaveBeenCalled(); + it('removes merge request meta from local storage', () => { + expect(findLocalStorageSync().props().clear).toBe(true); + }); + + it('emits the primary event with mergeRequestMeta', () => { + expect(wrapper.emitted('primary')).toEqual([[mergeRequestMeta]]); + }); }); it('emits the hide event', () => { findGlModal().vm.$emit('hide'); expect(wrapper.emitted('hide')).toEqual([[]]); }); + + it('stores merge request meta changes in local storage when changes happen', async () => { + const newMeta = { title: 'new title', description: 'new description' }; + + findEditMetaControls().vm.$emit('updateSettings', newMeta); + + await wrapper.vm.$nextTick(); + + expect(findLocalStorageSync().props('value')).toEqual(newMeta); + }); }); diff --git a/spec/frontend/vue_shared/components/local_storage_sync_spec.js b/spec/frontend/vue_shared/components/local_storage_sync_spec.js index efa9b5796fb500215c97738d4ea8cab83f4f51a4..464fe3411dd4f824aeeb8bc5e27b6fa510472b91 100644 --- a/spec/frontend/vue_shared/components/local_storage_sync_spec.js +++ b/spec/frontend/vue_shared/components/local_storage_sync_spec.js @@ -239,4 +239,30 @@ describe('Local Storage Sync', () => { }); }); }); + + it('clears localStorage when clear property is true', async () => { + const storageKey = 'key'; + const value = 'initial'; + + createComponent({ + props: { + storageKey, + }, + }); + wrapper.setProps({ + value, + }); + + await wrapper.vm.$nextTick(); + + expect(localStorage.getItem(storageKey)).toBe(value); + + wrapper.setProps({ + clear: true, + }); + + await wrapper.vm.$nextTick(); + + expect(localStorage.getItem(storageKey)).toBe(null); + }); });