Commit 3dd7d772 authored by Enrique Alcantara's avatar Enrique Alcantara

Simplify MR meta editor state management

Simplifies the data flow of the modal that
contains a form to edit the Merge Request
description and title in the Static Site Editor
parent d802e948
<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>
......
<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"
......
......@@ -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';
......@@ -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
......
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));
});
});
});
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);
});
});
......@@ -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