Commit c741466d 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 ccf9b3bc
<script>
import { GlForm, GlFormGroup, GlFormInput, GlFormTextarea } from '@gitlab/ui';
export default {
components: {
GlForm,
GlFormGroup,
GlFormInput,
GlFormTextarea,
},
props: {
title: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
},
data() {
return {
editable: {
title: this.title,
description: this.description,
},
};
},
methods: {
getId(type, key) {
return `sse-merge-request-meta-${type}-${key}`;
},
onUpdate() {
this.$emit('updateSettings', { ...this.editable });
},
},
};
</script>
<template>
<gl-form>
<gl-form-group
key="title"
:label="__('Brief title about the change')"
:label-for="getId('control', 'title')"
>
<gl-form-input
:id="getId('control', 'title')"
v-model.lazy="editable.title"
type="text"
@input="onUpdate"
/>
</gl-form-group>
<gl-form-group
key="description"
:label="__('Goal of the changes and what reviewers should be aware of')"
:label-for="getId('control', 'description')"
>
<gl-form-textarea
:id="getId('control', 'description')"
v-model.lazy="editable.description"
@input="onUpdate"
/>
</gl-form-group>
</gl-form>
</template>
<script>
import { GlModal } from '@gitlab/ui';
import { __, s__, sprintf } from '~/locale';
import EditMetaControls from './edit_meta_controls.vue';
export default {
components: {
GlModal,
EditMetaControls,
},
props: {
sourcePath: {
type: String,
required: true,
},
},
data() {
return {
mergeRequestMeta: {
title: sprintf(s__(`StaticSiteEditor|Update %{sourcePath} file`), {
sourcePath: this.sourcePath,
}),
description: s__('StaticSiteEditor|Copy update'),
},
};
},
computed: {
disabled() {
return this.mergeRequestMeta.title === '';
},
primaryProps() {
return {
text: __('Submit changes'),
attributes: [{ variant: 'success' }, { disabled: this.disabled }],
};
},
},
methods: {
hide() {
this.$refs.modal.hide();
},
show() {
this.$refs.modal.show();
},
onUpdateSettings(mergeRequestMeta) {
this.mergeRequestMeta = { ...mergeRequestMeta };
},
},
};
</script>
<template>
<gl-modal
ref="modal"
modal-id="edit-meta-modal"
:title="__('Submit your changes')"
:action-primary="primaryProps"
size="sm"
@primary="() => $emit('primary', mergeRequestMeta)"
@hide="() => $emit('hide')"
>
<edit-meta-controls
:title="mergeRequestMeta.title"
:description="mergeRequestMeta.description"
@updateSettings="onUpdateSettings"
/>
</gl-modal>
</template>
...@@ -50,7 +50,7 @@ export default { ...@@ -50,7 +50,7 @@ export default {
:loading="savingChanges" :loading="savingChanges"
@click="$emit('submit')" @click="$emit('submit')"
> >
{{ __('Submit changes') }} {{ __('Submit changes...') }}
</gl-button> </gl-button>
</div> </div>
</div> </div>
......
<script> <script>
import { deprecatedCreateFlash as createFlash } from '~/flash'; import { deprecatedCreateFlash as createFlash } from '~/flash';
import { s__, sprintf } from '~/locale';
import Tracking from '~/tracking'; import Tracking from '~/tracking';
import SkeletonLoader from '../components/skeleton_loader.vue'; import SkeletonLoader from '../components/skeleton_loader.vue';
import EditArea from '../components/edit_area.vue'; import EditArea from '../components/edit_area.vue';
import EditMetaModal from '../components/edit_meta_modal.vue';
import InvalidContentMessage from '../components/invalid_content_message.vue'; import InvalidContentMessage from '../components/invalid_content_message.vue';
import SubmitChangesError from '../components/submit_changes_error.vue'; import SubmitChangesError from '../components/submit_changes_error.vue';
import appDataQuery from '../graphql/queries/app_data.query.graphql'; import appDataQuery from '../graphql/queries/app_data.query.graphql';
...@@ -18,6 +18,7 @@ export default { ...@@ -18,6 +18,7 @@ export default {
components: { components: {
SkeletonLoader, SkeletonLoader,
EditArea, EditArea,
EditMetaModal,
InvalidContentMessage, InvalidContentMessage,
SubmitChangesError, SubmitChangesError,
}, },
...@@ -51,6 +52,7 @@ export default { ...@@ -51,6 +52,7 @@ export default {
data() { data() {
return { return {
content: null, content: null,
images: null,
submitChangesError: null, submitChangesError: null,
isSavingChanges: false, isSavingChanges: false,
}; };
...@@ -67,16 +69,21 @@ export default { ...@@ -67,16 +69,21 @@ export default {
Tracking.event(document.body.dataset.page, TRACKING_ACTION_INITIALIZE_EDITOR); Tracking.event(document.body.dataset.page, TRACKING_ACTION_INITIALIZE_EDITOR);
}, },
methods: { methods: {
onHideModal() {
this.isSavingChanges = false;
this.$refs.editMetaModal.hide();
},
onDismissError() { onDismissError() {
this.submitChangesError = null; this.submitChangesError = null;
}, },
onSubmit({ content, images }) { onPrepareSubmit({ content, images }) {
this.content = content; this.content = content;
this.submitChanges(images); this.images = images;
},
submitChanges(images) {
this.isSavingChanges = true;
this.isSavingChanges = true;
this.$refs.editMetaModal.show();
},
onSubmit(mergeRequestMeta) {
// eslint-disable-next-line promise/catch-or-return // eslint-disable-next-line promise/catch-or-return
this.$apollo this.$apollo
.mutate({ .mutate({
...@@ -100,13 +107,8 @@ export default { ...@@ -100,13 +107,8 @@ export default {
username: this.appData.username, username: this.appData.username,
sourcePath: this.appData.sourcePath, sourcePath: this.appData.sourcePath,
content: this.content, content: this.content,
images, images: this.images,
mergeRequestMeta: { mergeRequestMeta,
title: sprintf(s__(`StaticSiteEditor|Update %{sourcePath} file`), {
sourcePath: this.appData.sourcePath,
}),
description: s__('StaticSiteEditor|Copy update'),
},
}, },
}, },
}) })
...@@ -127,7 +129,7 @@ export default { ...@@ -127,7 +129,7 @@ export default {
<submit-changes-error <submit-changes-error
v-if="submitChangesError" v-if="submitChangesError"
:error="submitChangesError" :error="submitChangesError"
@retry="submitChanges" @retry="onSubmit"
@dismiss="onDismissError" @dismiss="onDismissError"
/> />
<edit-area <edit-area
...@@ -136,7 +138,13 @@ export default { ...@@ -136,7 +138,13 @@ export default {
:content="sourceContent.content" :content="sourceContent.content"
:saving-changes="isSavingChanges" :saving-changes="isSavingChanges"
:return-url="appData.returnUrl" :return-url="appData.returnUrl"
@submit="onSubmit" @submit="onPrepareSubmit"
/>
<edit-meta-modal
ref="editMetaModal"
:source-path="appData.sourcePath"
@primary="onSubmit"
@hide="onHideModal"
/> />
</template> </template>
......
---
title: Add merge request title and description UI to Static Site Editor submission flow
merge_request: 44071
author:
type: added
...@@ -4335,6 +4335,9 @@ msgstr "" ...@@ -4335,6 +4335,9 @@ msgstr ""
msgid "Branches|protected" msgid "Branches|protected"
msgstr "" msgstr ""
msgid "Brief title about the change"
msgstr ""
msgid "Broadcast Message was successfully created." msgid "Broadcast Message was successfully created."
msgstr "" msgstr ""
...@@ -12418,6 +12421,9 @@ msgstr "" ...@@ -12418,6 +12421,9 @@ msgstr ""
msgid "Go to your snippets" msgid "Go to your snippets"
msgstr "" msgstr ""
msgid "Goal of the changes and what reviewers should be aware of"
msgstr ""
msgid "Google Cloud Platform" msgid "Google Cloud Platform"
msgstr "" msgstr ""
...@@ -24869,6 +24875,9 @@ msgstr "" ...@@ -24869,6 +24875,9 @@ msgstr ""
msgid "Submit changes" msgid "Submit changes"
msgstr "" msgstr ""
msgid "Submit changes..."
msgstr ""
msgid "Submit feedback" msgid "Submit feedback"
msgstr "" msgstr ""
...@@ -24884,6 +24893,9 @@ msgstr "" ...@@ -24884,6 +24893,9 @@ msgstr ""
msgid "Submit the current review." msgid "Submit the current review."
msgstr "" msgstr ""
msgid "Submit your changes"
msgstr ""
msgid "Submitted the current review." msgid "Submitted the current review."
msgstr "" msgstr ""
......
import { shallowMount } from '@vue/test-utils';
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', () => {
let wrapper;
const { title, description } = mergeRequestMeta;
const buildWrapper = (propsData = {}) => {
wrapper = shallowMount(EditMetaControls, {
propsData: {
title,
description,
...propsData,
},
});
};
const findGlFormInputTitle = () => wrapper.find(GlFormInput);
const findGlFormTextAreaDescription = () => wrapper.find(GlFormTextarea);
beforeEach(() => {
buildWrapper();
return wrapper.vm.$nextTick();
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('renders the title input', () => {
expect(findGlFormInputTitle().exists()).toBe(true);
});
it('renders the description input', () => {
expect(findGlFormTextAreaDescription().exists()).toBe(true);
});
it('forwards the title prop to the title input', () => {
expect(findGlFormInputTitle().attributes().value).toBe(title);
});
it('forwards the description prop to the description input', () => {
expect(findGlFormTextAreaDescription().attributes().value).toBe(description);
});
it('emits updated settings when title input updates', () => {
const newTitle = 'New title';
findGlFormInputTitle().vm.$emit('input', newTitle);
const newSettings = { description, title: newTitle };
expect(wrapper.emitted('updateSettings')[0][0]).toMatchObject(newSettings);
});
it('emits updated settings when description textarea updates', () => {
const newDescription = 'New description';
findGlFormTextAreaDescription().vm.$emit('input', newDescription);
const newSettings = { description: newDescription, title };
expect(wrapper.emitted('updateSettings')[0][0]).toMatchObject(newSettings);
});
});
import { shallowMount } from '@vue/test-utils';
import { GlModal } from '@gitlab/ui';
import EditMetaModal from '~/static_site_editor/components/edit_meta_modal.vue';
import EditMetaControls from '~/static_site_editor/components/edit_meta_controls.vue';
import { sourcePath, mergeRequestMeta } from '../mock_data';
describe('~/static_site_editor/components/edit_meta_modal.vue', () => {
let wrapper;
const { title, description } = mergeRequestMeta;
const buildWrapper = (propsData = {}) => {
wrapper = shallowMount(EditMetaModal, {
propsData: {
sourcePath,
...propsData,
},
});
};
const findGlModal = () => wrapper.find(GlModal);
const findEditMetaControls = () => wrapper.find(EditMetaControls);
beforeEach(() => {
buildWrapper();
return wrapper.vm.$nextTick();
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('renders the modal', () => {
expect(findGlModal().exists()).toBe(true);
});
it('renders the edit meta controls', () => {
expect(findEditMetaControls().exists()).toBe(true);
});
it('contains the sourcePath in the title', () => {
expect(findEditMetaControls().props('title')).toContain(sourcePath);
});
it('forwards the title prop', () => {
expect(findEditMetaControls().props('title')).toBe(title);
});
it('forwards the description prop', () => {
expect(findEditMetaControls().props('description')).toBe(description);
});
it('emits the primary event with mergeRequestMeta', () => {
findGlModal().vm.$emit('primary', mergeRequestMeta);
expect(wrapper.emitted('primary')).toEqual([[mergeRequestMeta]]);
});
it('emits the hide event', () => {
findGlModal().vm.$emit('hide');
expect(wrapper.emitted('hide')).toEqual([[]]);
});
});
...@@ -3,6 +3,7 @@ import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; ...@@ -3,6 +3,7 @@ import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import Home from '~/static_site_editor/pages/home.vue'; import Home from '~/static_site_editor/pages/home.vue';
import SkeletonLoader from '~/static_site_editor/components/skeleton_loader.vue'; import SkeletonLoader from '~/static_site_editor/components/skeleton_loader.vue';
import EditArea from '~/static_site_editor/components/edit_area.vue'; import EditArea from '~/static_site_editor/components/edit_area.vue';
import EditMetaModal from '~/static_site_editor/components/edit_meta_modal.vue';
import InvalidContentMessage from '~/static_site_editor/components/invalid_content_message.vue'; import InvalidContentMessage from '~/static_site_editor/components/invalid_content_message.vue';
import SubmitChangesError from '~/static_site_editor/components/submit_changes_error.vue'; import SubmitChangesError from '~/static_site_editor/components/submit_changes_error.vue';
import submitContentChangesMutation from '~/static_site_editor/graphql/mutations/submit_content_changes.mutation.graphql'; import submitContentChangesMutation from '~/static_site_editor/graphql/mutations/submit_content_changes.mutation.graphql';
...@@ -21,6 +22,7 @@ import { ...@@ -21,6 +22,7 @@ import {
savedContentMeta, savedContentMeta,
submitChangesError, submitChangesError,
trackingCategory, trackingCategory,
images,
} from '../mock_data'; } from '../mock_data';
const localVue = createLocalVue(); const localVue = createLocalVue();
...@@ -85,6 +87,7 @@ describe('static_site_editor/pages/home', () => { ...@@ -85,6 +87,7 @@ describe('static_site_editor/pages/home', () => {
}; };
const findEditArea = () => wrapper.find(EditArea); const findEditArea = () => wrapper.find(EditArea);
const findEditMetaModal = () => wrapper.find(EditMetaModal);
const findInvalidContentMessage = () => wrapper.find(InvalidContentMessage); const findInvalidContentMessage = () => wrapper.find(InvalidContentMessage);
const findSkeletonLoader = () => wrapper.find(SkeletonLoader); const findSkeletonLoader = () => wrapper.find(SkeletonLoader);
const findSubmitChangesError = () => wrapper.find(SubmitChangesError); const findSubmitChangesError = () => wrapper.find(SubmitChangesError);
...@@ -152,17 +155,38 @@ describe('static_site_editor/pages/home', () => { ...@@ -152,17 +155,38 @@ describe('static_site_editor/pages/home', () => {
}); });
it('displays invalid content message when content is not supported', () => { it('displays invalid content message when content is not supported', () => {
buildWrapper({ appData: { isSupportedContent: false } }); buildWrapper({ appData: { ...defaultAppData, isSupportedContent: false } });
expect(findInvalidContentMessage().exists()).toBe(true); expect(findInvalidContentMessage().exists()).toBe(true);
}); });
it('does not display invalid content message when content is supported', () => { it('does not display invalid content message when content is supported', () => {
buildWrapper({ appData: { isSupportedContent: true } }); buildWrapper();
expect(findInvalidContentMessage().exists()).toBe(false); expect(findInvalidContentMessage().exists()).toBe(false);
}); });
it('renders an EditMetaModal component', () => {
buildWrapper();
expect(findEditMetaModal().exists()).toBe(true);
});
describe('when preparing submission', () => {
it('calls the show method when the edit-area submit event is emitted', () => {
buildWrapper();
const mockInstance = { show: jest.fn() };
wrapper.vm.$refs.editMetaModal = mockInstance;
findEditArea().vm.$emit('submit', { content });
return wrapper.vm.$nextTick().then(() => {
expect(mockInstance.show).toHaveBeenCalled();
});
});
});
describe('when submitting changes fails', () => { describe('when submitting changes fails', () => {
const setupMutateMock = () => { const setupMutateMock = () => {
mutateMock mutateMock
...@@ -173,8 +197,8 @@ describe('static_site_editor/pages/home', () => { ...@@ -173,8 +197,8 @@ describe('static_site_editor/pages/home', () => {
beforeEach(() => { beforeEach(() => {
setupMutateMock(); setupMutateMock();
buildWrapper(); buildWrapper({ content });
findEditArea().vm.$emit('submit', { content }); findEditMetaModal().vm.$emit('primary', mergeRequestMeta);
return wrapper.vm.$nextTick(); return wrapper.vm.$nextTick();
}); });
...@@ -200,12 +224,6 @@ describe('static_site_editor/pages/home', () => { ...@@ -200,12 +224,6 @@ describe('static_site_editor/pages/home', () => {
}); });
}); });
it('does not display submit changes error when an error does not exist', () => {
buildWrapper();
expect(findSubmitChangesError().exists()).toBe(false);
});
describe('when submitting changes succeeds', () => { describe('when submitting changes succeeds', () => {
const newContent = `new ${content}`; const newContent = `new ${content}`;
...@@ -216,8 +234,8 @@ describe('static_site_editor/pages/home', () => { ...@@ -216,8 +234,8 @@ describe('static_site_editor/pages/home', () => {
}, },
}); });
buildWrapper(); buildWrapper({ content: newContent, images });
findEditArea().vm.$emit('submit', { content: newContent }); findEditMetaModal().vm.$emit('primary', mergeRequestMeta);
return wrapper.vm.$nextTick(); return wrapper.vm.$nextTick();
}); });
...@@ -242,7 +260,7 @@ describe('static_site_editor/pages/home', () => { ...@@ -242,7 +260,7 @@ describe('static_site_editor/pages/home', () => {
project, project,
sourcePath, sourcePath,
username, username,
images: undefined, images,
mergeRequestMeta, mergeRequestMeta,
}, },
}, },
...@@ -254,6 +272,12 @@ describe('static_site_editor/pages/home', () => { ...@@ -254,6 +272,12 @@ describe('static_site_editor/pages/home', () => {
}); });
}); });
it('does not display submit changes error when an error does not exist', () => {
buildWrapper();
expect(findSubmitChangesError().exists()).toBe(false);
});
it('tracks when editor is initialized on the mounted lifecycle hook', () => { it('tracks when editor is initialized on the mounted lifecycle hook', () => {
buildWrapper(); buildWrapper();
expect(trackingSpy).toHaveBeenCalledWith( expect(trackingSpy).toHaveBeenCalledWith(
......
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