Commit 3ac9a973 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '268006-simplify-mr-meta-state-management' into 'master'

Simplify components data flow in merge request meta editor modal

See merge request gitlab-org/gitlab!45576
parents 0174562a 3dd7d772
<script> <script>
import { GlForm, GlFormGroup, GlFormInput, GlFormTextarea } from '@gitlab/ui'; import { GlForm, GlFormGroup, GlFormInput, GlFormTextarea } from '@gitlab/ui';
import AccessorUtilities from '~/lib/utils/accessor';
export default { export default {
components: { components: {
...@@ -19,55 +18,25 @@ export default { ...@@ -19,55 +18,25 @@ export default {
required: true, required: true,
}, },
}, },
data() {
return {
editable: {
title: this.title,
description: this.description,
},
};
},
computed: {
editableStorageKey() {
return this.getId('local-storage', 'editable');
},
hasLocalStorage() {
return AccessorUtilities.isLocalStorageAccessSafe();
},
},
mounted() { mounted() {
this.initCachedEditable();
this.preSelect(); this.preSelect();
}, },
methods: { methods: {
getId(type, key) { getId(type, key) {
return `sse-merge-request-meta-${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() { preSelect() {
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.title.$el.select(); this.$refs.title.$el.select();
}); });
}, },
resetCachedEditable() { onUpdate(field, value) {
if (this.hasLocalStorage) { const payload = {
window.localStorage.removeItem(this.editableStorageKey); title: this.title,
} description: this.description,
}, [field]: value,
onUpdate() { };
const payload = { ...this.editable };
this.$emit('updateSettings', payload); this.$emit('updateSettings', payload);
if (this.hasLocalStorage) {
window.localStorage.setItem(this.editableStorageKey, JSON.stringify(payload));
}
}, },
}, },
}; };
...@@ -83,9 +52,9 @@ export default { ...@@ -83,9 +52,9 @@ export default {
<gl-form-input <gl-form-input
:id="getId('control', 'title')" :id="getId('control', 'title')"
ref="title" ref="title"
v-model.lazy="editable.title" :value="title"
type="text" type="text"
@input="onUpdate" @input="onUpdate('title', $event)"
/> />
</gl-form-group> </gl-form-group>
...@@ -96,8 +65,8 @@ export default { ...@@ -96,8 +65,8 @@ export default {
> >
<gl-form-textarea <gl-form-textarea
:id="getId('control', 'description')" :id="getId('control', 'description')"
v-model.lazy="editable.description" :value="description"
@input="onUpdate" @input="onUpdate('description', $event)"
/> />
</gl-form-group> </gl-form-group>
</gl-form> </gl-form>
......
<script> <script>
import { GlModal } from '@gitlab/ui'; import { GlModal } from '@gitlab/ui';
import { __, s__, sprintf } from '~/locale'; import { __, s__, sprintf } from '~/locale';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import EditMetaControls from './edit_meta_controls.vue'; import EditMetaControls from './edit_meta_controls.vue';
import { MR_META_LOCAL_STORAGE_KEY } from '../constants';
export default { export default {
components: { components: {
GlModal, GlModal,
EditMetaControls, EditMetaControls,
LocalStorageSync,
}, },
props: { props: {
sourcePath: { sourcePath: {
...@@ -17,6 +21,7 @@ export default { ...@@ -17,6 +21,7 @@ export default {
}, },
data() { data() {
return { return {
clearStorage: false,
mergeRequestMeta: { mergeRequestMeta: {
title: sprintf(s__(`StaticSiteEditor|Update %{sourcePath} file`), { title: sprintf(s__(`StaticSiteEditor|Update %{sourcePath} file`), {
sourcePath: this.sourcePath, sourcePath: this.sourcePath,
...@@ -51,7 +56,7 @@ export default { ...@@ -51,7 +56,7 @@ export default {
}, },
onPrimary() { onPrimary() {
this.$emit('primary', this.mergeRequestMeta); this.$emit('primary', this.mergeRequestMeta);
this.$refs.editMetaControls.resetCachedEditable(); this.clearStorage = true;
}, },
onSecondary() { onSecondary() {
this.hide(); this.hide();
...@@ -60,6 +65,7 @@ export default { ...@@ -60,6 +65,7 @@ export default {
this.mergeRequestMeta = { ...mergeRequestMeta }; this.mergeRequestMeta = { ...mergeRequestMeta };
}, },
}, },
storageKey: MR_META_LOCAL_STORAGE_KEY,
}; };
</script> </script>
...@@ -75,6 +81,12 @@ export default { ...@@ -75,6 +81,12 @@ export default {
@secondary="onSecondary" @secondary="onSecondary"
@hide="() => $emit('hide')" @hide="() => $emit('hide')"
> >
<local-storage-sync
v-model="mergeRequestMeta"
:storage-key="$options.storageKey"
:clear="clearStorage"
as-json
/>
<edit-meta-controls <edit-meta-controls
ref="editMetaControls" ref="editMetaControls"
:title="mergeRequestMeta.title" :title="mergeRequestMeta.title"
......
...@@ -21,3 +21,5 @@ export const TRACKING_ACTION_CREATE_MERGE_REQUEST = 'create_merge_request'; ...@@ -21,3 +21,5 @@ export const TRACKING_ACTION_CREATE_MERGE_REQUEST = 'create_merge_request';
export const TRACKING_ACTION_INITIALIZE_EDITOR = 'initialize_editor'; export const TRACKING_ACTION_INITIALIZE_EDITOR = 'initialize_editor';
export const DEFAULT_IMAGE_UPLOAD_PATH = 'source/images/uploads/'; export const DEFAULT_IMAGE_UPLOAD_PATH = 'source/images/uploads/';
export const MR_META_LOCAL_STORAGE_KEY = 'sse-merge-request-meta-storage-key';
...@@ -22,11 +22,21 @@ export default { ...@@ -22,11 +22,21 @@ export default {
required: false, required: false,
default: true, default: true,
}, },
clear: {
type: Boolean,
required: false,
default: false,
},
}, },
watch: { watch: {
value(newVal) { value(newVal) {
this.saveValue(this.serialize(newVal)); this.saveValue(this.serialize(newVal));
}, },
clear(newVal) {
if (newVal) {
localStorage.removeItem(this.storageKey);
}
},
}, },
mounted() { mounted() {
// On mount, trigger update if we actually have a localStorageValue // On mount, trigger update if we actually have a localStorageValue
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import { GlFormInput, GlFormTextarea } from '@gitlab/ui'; import { GlFormInput, GlFormTextarea } from '@gitlab/ui';
import EditMetaControls from '~/static_site_editor/components/edit_meta_controls.vue'; 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 ...@@ -8,8 +7,6 @@ import EditMetaControls from '~/static_site_editor/components/edit_meta_controls
import { mergeRequestMeta } from '../mock_data'; import { mergeRequestMeta } from '../mock_data';
describe('~/static_site_editor/components/edit_meta_controls.vue', () => { describe('~/static_site_editor/components/edit_meta_controls.vue', () => {
useLocalStorageSpy();
let wrapper; let wrapper;
let mockSelect; let mockSelect;
let mockGlFormInputTitleInstance; let mockGlFormInputTitleInstance;
...@@ -86,14 +83,5 @@ describe('~/static_site_editor/components/edit_meta_controls.vue', () => { ...@@ -86,14 +83,5 @@ describe('~/static_site_editor/components/edit_meta_controls.vue', () => {
expect(wrapper.emitted('updateSettings')[0][0]).toMatchObject(newSettings); 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));
});
}); });
}); });
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { GlModal } from '@gitlab/ui'; 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 EditMetaModal from '~/static_site_editor/components/edit_meta_modal.vue';
import EditMetaControls from '~/static_site_editor/components/edit_meta_controls.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'; import { sourcePath, mergeRequestMeta } from '../mock_data';
describe('~/static_site_editor/components/edit_meta_modal.vue', () => { describe('~/static_site_editor/components/edit_meta_modal.vue', () => {
useLocalStorageSpy();
let wrapper; let wrapper;
let resetCachedEditable; let resetCachedEditable;
let mockEditMetaControlsInstance; let mockEditMetaControlsInstance;
...@@ -30,6 +32,11 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => { ...@@ -30,6 +32,11 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => {
const findGlModal = () => wrapper.find(GlModal); const findGlModal = () => wrapper.find(GlModal);
const findEditMetaControls = () => wrapper.find(EditMetaControls); const findEditMetaControls = () => wrapper.find(EditMetaControls);
const findLocalStorageSync = () => wrapper.find(LocalStorageSync);
beforeEach(() => {
localStorage.setItem(MR_META_LOCAL_STORAGE_KEY);
});
beforeEach(() => { beforeEach(() => {
buildWrapper(); buildWrapper();
...@@ -43,6 +50,16 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => { ...@@ -43,6 +50,16 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => {
wrapper = null; 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', () => { it('renders the modal', () => {
expect(findGlModal().exists()).toBe(true); expect(findGlModal().exists()).toBe(true);
}); });
...@@ -63,18 +80,32 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => { ...@@ -63,18 +80,32 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => {
expect(findEditMetaControls().props('description')).toBe(description); expect(findEditMetaControls().props('description')).toBe(description);
}); });
it('emits the primary event with mergeRequestMeta', () => { describe('when save button is clicked', () => {
findGlModal().vm.$emit('primary', mergeRequestMeta); beforeEach(() => {
expect(wrapper.emitted('primary')).toEqual([[mergeRequestMeta]]); findGlModal().vm.$emit('primary', mergeRequestMeta);
}); });
it('calls resetCachedEditable on EditMetaControls when primary emits', () => { it('removes merge request meta from local storage', () => {
findGlModal().vm.$emit('primary', mergeRequestMeta); expect(findLocalStorageSync().props().clear).toBe(true);
expect(mockEditMetaControlsInstance.resetCachedEditable).toHaveBeenCalled(); });
it('emits the primary event with mergeRequestMeta', () => {
expect(wrapper.emitted('primary')).toEqual([[mergeRequestMeta]]);
});
}); });
it('emits the hide event', () => { it('emits the hide event', () => {
findGlModal().vm.$emit('hide'); findGlModal().vm.$emit('hide');
expect(wrapper.emitted('hide')).toEqual([[]]); 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);
});
}); });
...@@ -239,4 +239,30 @@ describe('Local Storage Sync', () => { ...@@ -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);
});
}); });
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