Commit 3c407657 authored by Enrique Alcántara's avatar Enrique Alcántara

Merge branch '263252-mr-template-selection' into 'master'

Initial mr template selection in SSE

See merge request gitlab-org/gitlab!46381
parents ab333de7 134f5073
<script> <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 { export default {
components: { components: {
GlDropdown,
GlDropdownDivider,
GlDropdownItem,
GlForm, GlForm,
GlFormGroup, GlFormGroup,
GlFormInput, GlFormInput,
...@@ -17,6 +30,24 @@ export default { ...@@ -17,6 +30,24 @@ export default {
type: String, type: String,
required: true, 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() { mounted() {
this.preSelect(); this.preSelect();
...@@ -30,6 +61,9 @@ export default { ...@@ -30,6 +61,9 @@ export default {
this.$refs.title.$el.select(); this.$refs.title.$el.select();
}); });
}, },
onChangeTemplate(template) {
this.$emit('changeTemplate', template || null);
},
onUpdate(field, value) { onUpdate(field, value) {
const payload = { const payload = {
title: this.title, title: this.title,
...@@ -58,6 +92,29 @@ export default { ...@@ -58,6 +92,29 @@ export default {
/> />
</gl-form-group> </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 <gl-form-group
key="description" key="description"
:label="__('Goal of the changes and what reviewers should be aware of')" :label="__('Goal of the changes and what reviewers should be aware of')"
......
...@@ -22,6 +22,8 @@ export default { ...@@ -22,6 +22,8 @@ export default {
data() { data() {
return { return {
clearStorage: false, clearStorage: false,
currentTemplate: null,
mergeRequestTemplates: null,
mergeRequestMeta: { mergeRequestMeta: {
title: sprintf(s__(`StaticSiteEditor|Update %{sourcePath} file`), { title: sprintf(s__(`StaticSiteEditor|Update %{sourcePath} file`), {
sourcePath: this.sourcePath, sourcePath: this.sourcePath,
...@@ -61,6 +63,13 @@ export default { ...@@ -61,6 +63,13 @@ export default {
onSecondary() { onSecondary() {
this.hide(); this.hide();
}, },
onChangeTemplate(template) {
this.currentTemplate = template;
const description = this.currentTemplate ? this.currentTemplate.content : '';
const mergeRequestMeta = { ...this.mergeRequestMeta, description };
this.onUpdateSettings(mergeRequestMeta);
},
onUpdateSettings(mergeRequestMeta) { onUpdateSettings(mergeRequestMeta) {
this.mergeRequestMeta = { ...mergeRequestMeta }; this.mergeRequestMeta = { ...mergeRequestMeta };
}, },
...@@ -91,7 +100,10 @@ export default { ...@@ -91,7 +100,10 @@ export default {
ref="editMetaControls" ref="editMetaControls"
:title="mergeRequestMeta.title" :title="mergeRequestMeta.title"
:description="mergeRequestMeta.description" :description="mergeRequestMeta.description"
:templates="mergeRequestTemplates"
:current-template="currentTemplate"
@updateSettings="onUpdateSettings" @updateSettings="onUpdateSettings"
@changeTemplate="onChangeTemplate"
/> />
</gl-modal> </gl-modal>
</template> </template>
...@@ -9109,6 +9109,9 @@ msgstr "" ...@@ -9109,6 +9109,9 @@ msgstr ""
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}" msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr "" 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." msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
msgstr "" msgstr ""
......
import { shallowMount } from '@vue/test-utils'; 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 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', () => { describe('~/static_site_editor/components/edit_meta_controls.vue', () => {
let wrapper; let wrapper;
...@@ -19,6 +19,8 @@ describe('~/static_site_editor/components/edit_meta_controls.vue', () => { ...@@ -19,6 +19,8 @@ describe('~/static_site_editor/components/edit_meta_controls.vue', () => {
propsData: { propsData: {
title, title,
description, description,
templates: mergeRequestTemplates,
currentTemplate: null,
...propsData, ...propsData,
}, },
}); });
...@@ -31,6 +33,10 @@ describe('~/static_site_editor/components/edit_meta_controls.vue', () => { ...@@ -31,6 +33,10 @@ describe('~/static_site_editor/components/edit_meta_controls.vue', () => {
}; };
const findGlFormInputTitle = () => wrapper.find(GlFormInput); 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); const findGlFormTextAreaDescription = () => wrapper.find(GlFormTextarea);
beforeEach(() => { beforeEach(() => {
...@@ -49,6 +55,10 @@ describe('~/static_site_editor/components/edit_meta_controls.vue', () => { ...@@ -49,6 +55,10 @@ describe('~/static_site_editor/components/edit_meta_controls.vue', () => {
expect(findGlFormInputTitle().exists()).toBe(true); expect(findGlFormInputTitle().exists()).toBe(true);
}); });
it('renders the description template dropdown', () => {
expect(findGlDropdownDescriptionTemplate().exists()).toBe(true);
});
it('renders the description input', () => { it('renders the description input', () => {
expect(findGlFormTextAreaDescription().exists()).toBe(true); expect(findGlFormTextAreaDescription().exists()).toBe(true);
}); });
...@@ -65,6 +75,11 @@ describe('~/static_site_editor/components/edit_meta_controls.vue', () => { ...@@ -65,6 +75,11 @@ describe('~/static_site_editor/components/edit_meta_controls.vue', () => {
expect(mockGlFormInputTitleInstance.$el.select).toHaveBeenCalled(); 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', () => { describe('when inputs change', () => {
const storageKey = 'sse-merge-request-meta-local-storage-editable'; const storageKey = 'sse-merge-request-meta-local-storage-editable';
...@@ -84,4 +99,17 @@ describe('~/static_site_editor/components/edit_meta_controls.vue', () => { ...@@ -84,4 +99,17 @@ 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);
}); });
}); });
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'; ...@@ -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 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 { 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', () => { describe('~/static_site_editor/components/edit_meta_modal.vue', () => {
useLocalStorageSpy(); useLocalStorageSpy();
...@@ -15,12 +15,13 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => { ...@@ -15,12 +15,13 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => {
let mockEditMetaControlsInstance; let mockEditMetaControlsInstance;
const { title, description } = mergeRequestMeta; const { title, description } = mergeRequestMeta;
const buildWrapper = (propsData = {}) => { const buildWrapper = (propsData = {}, data = {}) => {
wrapper = shallowMount(EditMetaModal, { wrapper = shallowMount(EditMetaModal, {
propsData: { propsData: {
sourcePath, sourcePath,
...propsData, ...propsData,
}, },
data: () => data,
}); });
}; };
...@@ -51,7 +52,12 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => { ...@@ -51,7 +52,12 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => {
}); });
it('initializes initial merge request meta with local storage data', async () => { 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); findLocalStorageSync().vm.$emit('input', localStorageMeta);
...@@ -80,6 +86,14 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => { ...@@ -80,6 +86,14 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => {
expect(findEditMetaControls().props('description')).toBe(description); 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', () => { describe('when save button is clicked', () => {
beforeEach(() => { beforeEach(() => {
findGlModal().vm.$emit('primary', mergeRequestMeta); findGlModal().vm.$emit('primary', mergeRequestMeta);
...@@ -94,6 +108,36 @@ describe('~/static_site_editor/components/edit_meta_modal.vue', () => { ...@@ -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', () => { it('emits the hide event', () => {
findGlModal().vm.$emit('hide'); findGlModal().vm.$emit('hide');
expect(wrapper.emitted('hide')).toEqual([[]]); expect(wrapper.emitted('hide')).toEqual([[]]);
......
...@@ -48,6 +48,10 @@ export const savedContentMeta = { ...@@ -48,6 +48,10 @@ export const savedContentMeta = {
url: 'foobar/-/merge_requests/123', 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 submitChangesError = 'Could not save changes';
export const commitBranchResponse = { 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