Commit 6a0965e3 authored by Derek Knox's avatar Derek Knox Committed by Enrique Alcántara

Initial edit-meta UI implementation

Added modal and controls components for
overall editing merge request metadata
parent 3a98d7a0
<script>
import { GlForm, GlFormGroup, GlFormInput, GlFormTextarea } from '@gitlab/ui';
import AccessorUtilities from '~/lib/utils/accessor';
export default {
components: {
......@@ -26,12 +27,47 @@ export default {
},
};
},
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() {
this.$emit('updateSettings', { ...this.editable });
const payload = { ...this.editable };
this.$emit('updateSettings', payload);
if (this.hasLocalStorage) {
window.localStorage.setItem(this.editableStorageKey, JSON.stringify(payload));
}
},
},
};
......@@ -46,6 +82,7 @@ export default {
>
<gl-form-input
:id="getId('control', 'title')"
ref="title"
v-model.lazy="editable.title"
type="text"
@input="onUpdate"
......
......@@ -35,6 +35,12 @@ export default {
attributes: [{ variant: 'success' }, { disabled: this.disabled }],
};
},
secondaryProps() {
return {
text: __('Keep editing'),
attributes: [{ variant: 'default' }],
};
},
},
methods: {
hide() {
......@@ -43,6 +49,13 @@ export default {
show() {
this.$refs.modal.show();
},
onPrimary() {
this.$emit('primary', this.mergeRequestMeta);
this.$refs.editMetaControls.resetCachedEditable();
},
onSecondary() {
this.hide();
},
onUpdateSettings(mergeRequestMeta) {
this.mergeRequestMeta = { ...mergeRequestMeta };
},
......@@ -56,11 +69,14 @@ export default {
modal-id="edit-meta-modal"
:title="__('Submit your changes')"
:action-primary="primaryProps"
:action-secondary="secondaryProps"
size="sm"
@primary="() => $emit('primary', mergeRequestMeta)"
@primary="onPrimary"
@secondary="onSecondary"
@hide="() => $emit('hide')"
>
<edit-meta-controls
ref="editMetaControls"
:title="mergeRequestMeta.title"
:description="mergeRequestMeta.description"
@updateSettings="onUpdateSettings"
......
---
title: Preserve the merge request title and description in the static site editor upon modal close
merge_request: 44512
author:
type: added
......@@ -14750,6 +14750,9 @@ msgstr ""
msgid "Keep divergent refs"
msgstr ""
msgid "Keep editing"
msgstr ""
msgid "Kerberos access denied"
msgstr ""
......
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';
import { mergeRequestMeta } from '../mock_data';
describe('~/static_site_editor/components/edit_meta_modal.vue', () => {
describe('~/static_site_editor/components/edit_meta_controls.vue', () => {
useLocalStorageSpy();
let wrapper;
let mockSelect;
let mockGlFormInputTitleInstance;
const { title, description } = mergeRequestMeta;
const newTitle = 'New title';
const newDescription = 'New description';
const buildWrapper = (propsData = {}) => {
wrapper = shallowMount(EditMetaControls, {
......@@ -20,11 +27,18 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => {
});
};
const buildMocks = () => {
mockSelect = jest.fn();
mockGlFormInputTitleInstance = { $el: { select: mockSelect } };
wrapper.vm.$refs.title = mockGlFormInputTitleInstance;
};
const findGlFormInputTitle = () => wrapper.find(GlFormInput);
const findGlFormTextAreaDescription = () => wrapper.find(GlFormTextarea);
beforeEach(() => {
buildWrapper();
buildMocks();
return wrapper.vm.$nextTick();
});
......@@ -50,23 +64,36 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => {
expect(findGlFormTextAreaDescription().attributes().value).toBe(description);
});
it('emits updated settings when title input updates', () => {
const newTitle = 'New title';
it('calls select on the title input when mounted', () => {
expect(mockGlFormInputTitleInstance.$el.select).toHaveBeenCalled();
});
findGlFormInputTitle().vm.$emit('input', newTitle);
describe('when inputs change', () => {
const storageKey = 'sse-merge-request-meta-local-storage-editable';
const newSettings = { description, title: newTitle };
afterEach(() => {
localStorage.removeItem(storageKey);
});
expect(wrapper.emitted('updateSettings')[0][0]).toMatchObject(newSettings);
});
it.each`
findFn | key | value
${findGlFormInputTitle} | ${'title'} | ${newTitle}
${findGlFormTextAreaDescription} | ${'description'} | ${newDescription}
`('emits updated settings when $findFn input updates', ({ key, value, findFn }) => {
findFn().vm.$emit('input', value);
const newSettings = { ...mergeRequestMeta, [key]: value };
it('emits updated settings when description textarea updates', () => {
const newDescription = 'New description';
expect(wrapper.emitted('updateSettings')[0][0]).toMatchObject(newSettings);
});
findGlFormTextAreaDescription().vm.$emit('input', newDescription);
it('should remember the input changes', () => {
findGlFormInputTitle().vm.$emit('input', newTitle);
findGlFormTextAreaDescription().vm.$emit('input', newDescription);
const newSettings = { description: newDescription, title };
const newSettings = { title: newTitle, description: newDescription };
expect(wrapper.emitted('updateSettings')[0][0]).toMatchObject(newSettings);
expect(localStorage.setItem).toHaveBeenCalledWith(storageKey, JSON.stringify(newSettings));
});
});
});
......@@ -9,6 +9,8 @@ import { sourcePath, mergeRequestMeta } from '../mock_data';
describe('~/static_site_editor/components/edit_meta_modal.vue', () => {
let wrapper;
let resetCachedEditable;
let mockEditMetaControlsInstance;
const { title, description } = mergeRequestMeta;
const buildWrapper = (propsData = {}) => {
......@@ -20,11 +22,18 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => {
});
};
const buildMocks = () => {
resetCachedEditable = jest.fn();
mockEditMetaControlsInstance = { resetCachedEditable };
wrapper.vm.$refs.editMetaControls = mockEditMetaControlsInstance;
};
const findGlModal = () => wrapper.find(GlModal);
const findEditMetaControls = () => wrapper.find(EditMetaControls);
beforeEach(() => {
buildWrapper();
buildMocks();
return wrapper.vm.$nextTick();
});
......@@ -59,6 +68,11 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => {
expect(wrapper.emitted('primary')).toEqual([[mergeRequestMeta]]);
});
it('calls resetCachedEditable on EditMetaControls when primary emits', () => {
findGlModal().vm.$emit('primary', mergeRequestMeta);
expect(mockEditMetaControlsInstance.resetCachedEditable).toHaveBeenCalled();
});
it('emits the hide event', () => {
findGlModal().vm.$emit('hide');
expect(wrapper.emitted('hide')).toEqual([[]]);
......
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