Commit 134f5073 authored by Derek Knox's avatar Derek Knox Committed by Enrique Alcántara

Initial mr template selection in SSE

Add basic UI for template dropdown
parent 369065d7
<script>
import { GlForm, GlFormGroup, GlFormInput, GlFormTextarea } from '@gitlab/ui';
import {
GlDropdown,
GlDropdownDivider,
GlDropdownItem,
GlForm,
GlFormGroup,
GlFormInput,
GlFormTextarea,
} from '@gitlab/ui';
import { __ } from '~/locale';
export default {
components: {
GlDropdown,
GlDropdownDivider,
GlDropdownItem,
GlForm,
GlFormGroup,
GlFormInput,
......@@ -17,6 +30,24 @@ export default {
type: String,
required: true,
},
templates: {
type: Array,
required: false,
default: null,
},
currentTemplate: {
type: Object,
required: false,
default: null,
},
},
computed: {
dropdownLabel() {
return this.currentTemplate ? this.currentTemplate.name : __('None');
},
hasTemplates() {
return this.templates?.length > 0;
},
},
mounted() {
this.preSelect();
......@@ -30,6 +61,9 @@ export default {
this.$refs.title.$el.select();
});
},
onChangeTemplate(template) {
this.$emit('changeTemplate', template || null);
},
onUpdate(field, value) {
const payload = {
title: this.title,
......@@ -58,6 +92,29 @@ export default {
/>
</gl-form-group>
<gl-form-group
v-if="hasTemplates"
key="template"
:label="__('Description template')"
:label-for="getId('control', 'template')"
>
<gl-dropdown :text="dropdownLabel">
<gl-dropdown-item key="none" @click="onChangeTemplate(null)">
{{ __('None') }}
</gl-dropdown-item>
<gl-dropdown-divider />
<gl-dropdown-item
v-for="template in templates"
:key="template.key"
@click="onChangeTemplate(template)"
>
{{ template.name }}
</gl-dropdown-item>
</gl-dropdown>
</gl-form-group>
<gl-form-group
key="description"
:label="__('Goal of the changes and what reviewers should be aware of')"
......
......@@ -22,6 +22,8 @@ export default {
data() {
return {
clearStorage: false,
currentTemplate: null,
mergeRequestTemplates: null,
mergeRequestMeta: {
title: sprintf(s__(`StaticSiteEditor|Update %{sourcePath} file`), {
sourcePath: this.sourcePath,
......@@ -61,6 +63,13 @@ export default {
onSecondary() {
this.hide();
},
onChangeTemplate(template) {
this.currentTemplate = template;
const description = this.currentTemplate ? this.currentTemplate.content : '';
const mergeRequestMeta = { ...this.mergeRequestMeta, description };
this.onUpdateSettings(mergeRequestMeta);
},
onUpdateSettings(mergeRequestMeta) {
this.mergeRequestMeta = { ...mergeRequestMeta };
},
......@@ -91,7 +100,10 @@ export default {
ref="editMetaControls"
:title="mergeRequestMeta.title"
:description="mergeRequestMeta.description"
:templates="mergeRequestTemplates"
:current-template="currentTemplate"
@updateSettings="onUpdateSettings"
@changeTemplate="onChangeTemplate"
/>
</gl-modal>
</template>
......@@ -9109,6 +9109,9 @@ msgstr ""
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
msgid "Description template"
msgstr ""
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
msgstr ""
......
import { shallowMount } from '@vue/test-utils';
import { GlFormInput, GlFormTextarea } from '@gitlab/ui';
import { GlDropdown, GlDropdownItem, GlFormInput, GlFormTextarea } from '@gitlab/ui';
import EditMetaControls from '~/static_site_editor/components/edit_meta_controls.vue';
import { mergeRequestMeta } from '../mock_data';
import { mergeRequestMeta, mergeRequestTemplates } from '../mock_data';
describe('~/static_site_editor/components/edit_meta_controls.vue', () => {
let wrapper;
......@@ -19,6 +19,8 @@ describe('~/static_site_editor/components/edit_meta_controls.vue', () => {
propsData: {
title,
description,
templates: mergeRequestTemplates,
currentTemplate: null,
...propsData,
},
});
......@@ -31,6 +33,10 @@ describe('~/static_site_editor/components/edit_meta_controls.vue', () => {
};
const findGlFormInputTitle = () => wrapper.find(GlFormInput);
const findGlDropdownDescriptionTemplate = () => wrapper.find(GlDropdown);
const findAllDropdownItems = () => wrapper.findAll(GlDropdownItem);
const findDropdownItemByIndex = index => findAllDropdownItems().at(index);
const findGlFormTextAreaDescription = () => wrapper.find(GlFormTextarea);
beforeEach(() => {
......@@ -49,6 +55,10 @@ describe('~/static_site_editor/components/edit_meta_controls.vue', () => {
expect(findGlFormInputTitle().exists()).toBe(true);
});
it('renders the description template dropdown', () => {
expect(findGlDropdownDescriptionTemplate().exists()).toBe(true);
});
it('renders the description input', () => {
expect(findGlFormTextAreaDescription().exists()).toBe(true);
});
......@@ -65,6 +75,11 @@ describe('~/static_site_editor/components/edit_meta_controls.vue', () => {
expect(mockGlFormInputTitleInstance.$el.select).toHaveBeenCalled();
});
it('renders a GlDropdownItem per template plus one (for the starting none option)', () => {
expect(findDropdownItemByIndex(0).text()).toBe('None');
expect(findAllDropdownItems().length).toBe(mergeRequestTemplates.length + 1);
});
describe('when inputs change', () => {
const storageKey = 'sse-merge-request-meta-local-storage-editable';
......@@ -84,4 +99,17 @@ describe('~/static_site_editor/components/edit_meta_controls.vue', () => {
expect(wrapper.emitted('updateSettings')[0][0]).toMatchObject(newSettings);
});
});
describe('when templates change', () => {
it.each`
index | value
${0} | ${null}
${1} | ${mergeRequestTemplates[0]}
${2} | ${mergeRequestTemplates[1]}
`('emits a change template event when $index is clicked', ({ index, value }) => {
findDropdownItemByIndex(index).vm.$emit('click');
expect(wrapper.emitted('changeTemplate')[0][0]).toBe(value);
});
});
});
......@@ -5,7 +5,7 @@ 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';
import { sourcePath, mergeRequestMeta, mergeRequestTemplates } from '../mock_data';
describe('~/static_site_editor/components/edit_meta_modal.vue', () => {
useLocalStorageSpy();
......@@ -15,12 +15,13 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => {
let mockEditMetaControlsInstance;
const { title, description } = mergeRequestMeta;
const buildWrapper = (propsData = {}) => {
const buildWrapper = (propsData = {}, data = {}) => {
wrapper = shallowMount(EditMetaModal, {
propsData: {
sourcePath,
...propsData,
},
data: () => data,
});
};
......@@ -51,7 +52,12 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => {
});
it('initializes initial merge request meta with local storage data', async () => {
const localStorageMeta = { title: 'stored title', description: 'stored description' };
const localStorageMeta = {
title: 'stored title',
description: 'stored description',
templates: null,
currentTemplate: null,
};
findLocalStorageSync().vm.$emit('input', localStorageMeta);
......@@ -80,6 +86,14 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => {
expect(findEditMetaControls().props('description')).toBe(description);
});
it('forwards the templates prop', () => {
expect(findEditMetaControls().props('templates')).toBe(null);
});
it('forwards the currentTemplate prop', () => {
expect(findEditMetaControls().props('currentTemplate')).toBe(null);
});
describe('when save button is clicked', () => {
beforeEach(() => {
findGlModal().vm.$emit('primary', mergeRequestMeta);
......@@ -94,6 +108,36 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => {
});
});
describe('when templates exist', () => {
const template1 = mergeRequestTemplates[0];
beforeEach(() => {
buildWrapper({}, { templates: mergeRequestTemplates, currentTemplate: null });
});
it('sets the currentTemplate on the changeTemplate event', async () => {
findEditMetaControls().vm.$emit('changeTemplate', template1);
await wrapper.vm.$nextTick();
expect(findEditMetaControls().props().currentTemplate).toBe(template1);
findEditMetaControls().vm.$emit('changeTemplate', null);
await wrapper.vm.$nextTick();
expect(findEditMetaControls().props().currentTemplate).toBe(null);
});
it('updates the description on the changeTemplate event', async () => {
findEditMetaControls().vm.$emit('changeTemplate', template1);
await wrapper.vm.$nextTick();
expect(findEditMetaControls().props().description).toEqual(template1.content);
});
});
it('emits the hide event', () => {
findGlModal().vm.$emit('hide');
expect(wrapper.emitted('hide')).toEqual([[]]);
......
......@@ -48,6 +48,10 @@ export const savedContentMeta = {
url: 'foobar/-/merge_requests/123',
},
};
export const mergeRequestTemplates = [
{ key: 'Template1', name: 'Template 1', content: 'This is template 1!' },
{ key: 'Template2', name: 'Template 2', content: 'This is template 2!' },
];
export const submitChangesError = 'Could not save changes';
export const commitBranchResponse = {
......
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