Commit e5b27582 authored by Martin Wortschack's avatar Martin Wortschack

Merge branch '196070-move-mr-from-right-sidebar-to-bottom-bar' into 'master'

Resolve "Move Merge Request from right sidebar of Web IDE to bottom bar"

See merge request gitlab-org/gitlab!24746
parents f503bfec 43639278
......@@ -2,6 +2,7 @@
/* eslint-disable @gitlab/vue-i18n/no-bare-strings */
import { mapActions, mapState, mapGetters } from 'vuex';
import IdeStatusList from 'ee_else_ce/ide/components/ide_status_list.vue';
import IdeStatusMr from './ide_status_mr.vue';
import icon from '~/vue_shared/components/icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
import timeAgoMixin from '~/vue_shared/mixins/timeago';
......@@ -15,6 +16,7 @@ export default {
userAvatarImage,
CiIcon,
IdeStatusList,
IdeStatusMr,
},
directives: {
tooltip,
......@@ -27,7 +29,7 @@ export default {
},
computed: {
...mapState(['currentBranchId', 'currentProjectId']),
...mapGetters(['currentProject', 'lastCommit']),
...mapGetters(['currentProject', 'lastCommit', 'currentMergeRequest']),
...mapState('pipelines', ['latestPipeline']),
},
watch: {
......@@ -121,6 +123,12 @@ export default {
>{{ lastCommitFormattedAge }}</time
>
</div>
<ide-status-mr
v-if="currentMergeRequest"
class="mx-3"
:url="currentMergeRequest.web_url"
:text="currentMergeRequest.references.short"
/>
<ide-status-list class="ml-auto" />
</footer>
</template>
<script>
import { GlIcon, GlLink } from '@gitlab/ui';
export default {
components: {
GlIcon,
GlLink,
},
props: {
text: {
type: String,
required: true,
},
url: {
type: String,
required: true,
},
},
};
</script>
<template>
<div class="d-flex-center flex-nowrap text-nowrap js-ide-status-mr">
<gl-icon name="merge-request" />
<span class="ml-1 d-none d-sm-block">{{ s__('WebIDE|Merge request') }}</span>
<gl-link class="ml-1" :href="url">{{ text }}</gl-link>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import Icon from '../../../vue_shared/components/icon.vue';
import TitleComponent from '../../../issue_show/components/title.vue';
import DescriptionComponent from '../../../issue_show/components/description.vue';
export default {
components: {
Icon,
TitleComponent,
DescriptionComponent,
},
computed: {
...mapGetters(['currentMergeRequest']),
},
};
</script>
<template>
<div class="ide-merge-request-info h-100 d-flex flex-column">
<div class="detail-page-header">
<icon name="git-merge" class="align-self-center append-right-8" />
<strong> !{{ currentMergeRequest.iid }} </strong>
</div>
<div class="issuable-details">
<title-component
:issuable-ref="currentMergeRequest.iid"
:title-html="currentMergeRequest.title_html"
:title-text="currentMergeRequest.title"
/>
<description-component
:description-html="currentMergeRequest.description_html"
:description-text="currentMergeRequest.description"
:can-update="false"
/>
</div>
</div>
</template>
......@@ -3,7 +3,6 @@ import { mapGetters, mapState } from 'vuex';
import { __ } from '~/locale';
import CollapsibleSidebar from './collapsible_sidebar.vue';
import { rightSidebarViews } from '../../constants';
import MergeRequestInfo from '../merge_requests/info.vue';
import PipelinesList from '../pipelines/list.vue';
import JobsDetail from '../jobs/detail.vue';
import Clientside from '../preview/clientside.vue';
......@@ -28,12 +27,6 @@ export default {
},
rightExtensionTabs() {
return [
{
show: Boolean(this.currentMergeRequestId),
title: __('Merge Request'),
views: [{ component: MergeRequestInfo, ...rightSidebarViews.mergeRequestInfo }],
icon: 'text-description',
},
{
show: true,
title: __('Pipelines'),
......
......@@ -44,9 +44,7 @@ export const getMergeRequestData = (
new Promise((resolve, reject) => {
if (!state.projects[projectId].mergeRequests[mergeRequestId] || force) {
service
.getProjectMergeRequestData(targetProjectId || projectId, mergeRequestId, {
render_html: true,
})
.getProjectMergeRequestData(targetProjectId || projectId, mergeRequestId)
.then(({ data }) => {
commit(types.SET_MERGE_REQUEST, {
projectPath: projectId,
......
---
title: Move Merge Request from right sidebar of Web IDE to bottom bar
merge_request: 24746
author:
type: changed
......@@ -21435,6 +21435,9 @@ msgstr ""
msgid "Web terminal"
msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
msgid "Webhooks"
msgstr ""
......
import { shallowMount } from '@vue/test-utils';
import { GlIcon, GlLink } from '@gitlab/ui';
import { TEST_HOST } from 'helpers/test_constants';
import IdeStatusMr from '~/ide/components/ide_status_mr.vue';
const TEST_TEXT = '!9001';
const TEST_URL = `${TEST_HOST}merge-requests/9001`;
describe('ide/components/ide_status_mr', () => {
let wrapper;
const createComponent = props => {
wrapper = shallowMount(IdeStatusMr, {
propsData: props,
});
};
const findIcon = () => wrapper.find(GlIcon);
const findLink = () => wrapper.find(GlLink);
afterEach(() => {
wrapper.destroy();
});
describe('when mounted', () => {
beforeEach(() => {
createComponent({
text: TEST_TEXT,
url: TEST_URL,
});
});
it('renders icon', () => {
const icon = findIcon();
expect(icon.exists()).toBe(true);
expect(icon.props()).toEqual(
expect.objectContaining({
name: 'merge-request',
}),
);
});
it('renders link', () => {
const link = findLink();
expect(link.exists()).toBe(true);
expect(link.attributes()).toEqual(
expect.objectContaining({
href: TEST_URL,
}),
);
expect(link.text()).toEqual(TEST_TEXT);
});
it('renders text', () => {
expect(wrapper.text()).toBe(`Merge request ${TEST_TEXT}`);
});
});
});
......@@ -75,28 +75,6 @@ describe('ide/components/panes/right.vue', () => {
});
});
describe('merge request tab', () => {
it('is shown if there is a currentMergeRequestId', () => {
store.state.currentMergeRequestId = 1;
createComponent();
expect(wrapper.find(CollapsibleSidebar).props('extensionTabs')).toEqual(
expect.arrayContaining([
expect.objectContaining({
show: true,
title: 'Merge Request',
views: expect.arrayContaining([
expect.objectContaining({
name: rightSidebarViews.mergeRequestInfo.name,
}),
]),
}),
]),
);
});
});
describe('clientside live preview tab', () => {
it('is shown if there is a packageJson and clientsidePreviewEnabled', () => {
Vue.set(store.state.entries, 'package.json', {
......
import Vue from 'vue';
import _ from 'lodash';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import store from '~/ide/stores';
import ideStatusBar from '~/ide/components/ide_status_bar.vue';
import { TEST_HOST } from 'spec/test_constants';
import { createStore } from '~/ide/stores';
import IdeStatusBar from '~/ide/components/ide_status_bar.vue';
import { rightSidebarViews } from '~/ide/constants';
import { resetStore } from '../helpers';
import { projectData } from '../mock_data';
const TEST_PROJECT_ID = 'abcproject';
const TEST_MERGE_REQUEST_ID = '9001';
const TEST_MERGE_REQUEST_URL = `${TEST_HOST}merge-requests/${TEST_MERGE_REQUEST_ID}`;
describe('ideStatusBar', () => {
let store;
let vm;
beforeEach(() => {
const Component = Vue.extend(ideStatusBar);
const createComponent = () => {
vm = createComponentWithStore(Vue.extend(IdeStatusBar), store).$mount();
};
const findMRStatus = () => vm.$el.querySelector('.js-ide-status-mr');
store.state.currentProjectId = 'abcproject';
store.state.projects.abcproject = projectData;
beforeEach(() => {
store = createStore();
store.state.currentProjectId = TEST_PROJECT_ID;
store.state.projects[TEST_PROJECT_ID] = _.clone(projectData);
store.state.currentBranchId = 'master';
vm = createComponentWithStore(Component, store).$mount();
});
afterEach(() => {
vm.$destroy();
resetStore(vm.$store);
});
it('renders the statusbar', () => {
expect(vm.$el.className).toBe('ide-status-bar');
});
describe('default', () => {
beforeEach(() => {
createComponent();
});
describe('mounted', () => {
it('triggers a setInterval', () => {
expect(vm.intervalId).not.toBe(null);
});
});
describe('commitAgeUpdate', () => {
beforeEach(function() {
jasmine.clock().install();
spyOn(vm, 'commitAgeUpdate').and.callFake(() => {});
vm.startTimer();
it('renders the statusbar', () => {
expect(vm.$el.className).toBe('ide-status-bar');
});
afterEach(function() {
jasmine.clock().uninstall();
});
describe('commitAgeUpdate', () => {
beforeEach(function() {
jasmine.clock().install();
spyOn(vm, 'commitAgeUpdate').and.callFake(() => {});
vm.startTimer();
});
it('gets called every second', () => {
expect(vm.commitAgeUpdate).not.toHaveBeenCalled();
afterEach(function() {
jasmine.clock().uninstall();
});
jasmine.clock().tick(1100);
it('gets called every second', () => {
expect(vm.commitAgeUpdate).not.toHaveBeenCalled();
expect(vm.commitAgeUpdate.calls.count()).toEqual(1);
jasmine.clock().tick(1100);
jasmine.clock().tick(1000);
expect(vm.commitAgeUpdate.calls.count()).toEqual(1);
expect(vm.commitAgeUpdate.calls.count()).toEqual(2);
jasmine.clock().tick(1000);
expect(vm.commitAgeUpdate.calls.count()).toEqual(2);
});
});
});
describe('getCommitPath', () => {
it('returns the path to the commit details', () => {
expect(vm.getCommitPath('abc123de')).toBe('/commit/abc123de');
describe('getCommitPath', () => {
it('returns the path to the commit details', () => {
expect(vm.getCommitPath('abc123de')).toBe('/commit/abc123de');
});
});
describe('pipeline status', () => {
it('opens right sidebar on clicking icon', done => {
spyOn(vm, 'openRightPane');
Vue.set(vm.$store.state.pipelines, 'latestPipeline', {
details: {
status: {
text: 'success',
details_path: 'test',
icon: 'status_success',
},
},
commit: {
author_gravatar_url: 'www',
},
});
vm.$nextTick()
.then(() => {
vm.$el.querySelector('.ide-status-pipeline button').click();
expect(vm.openRightPane).toHaveBeenCalledWith(rightSidebarViews.pipelines);
})
.then(done)
.catch(done.fail);
});
});
it('does not show merge request status', () => {
expect(findMRStatus()).toBe(null);
});
});
describe('pipeline status', () => {
it('opens right sidebar on clicking icon', done => {
spyOn(vm, 'openRightPane');
Vue.set(vm.$store.state.pipelines, 'latestPipeline', {
details: {
status: {
text: 'success',
details_path: 'test',
icon: 'status_success',
describe('with merge request in store', () => {
beforeEach(() => {
store.state.projects[TEST_PROJECT_ID].mergeRequests = {
[TEST_MERGE_REQUEST_ID]: {
web_url: TEST_MERGE_REQUEST_URL,
references: {
short: `!${TEST_MERGE_REQUEST_ID}`,
},
},
commit: {
author_gravatar_url: 'www',
},
});
};
store.state.currentMergeRequestId = TEST_MERGE_REQUEST_ID;
vm.$nextTick()
.then(() => {
vm.$el.querySelector('.ide-status-pipeline button').click();
createComponent();
});
expect(vm.openRightPane).toHaveBeenCalledWith(rightSidebarViews.pipelines);
})
.then(done)
.catch(done.fail);
it('shows merge request status', () => {
expect(findMRStatus().textContent.trim()).toEqual(`Merge request !${TEST_MERGE_REQUEST_ID}`);
expect(findMRStatus().querySelector('a').href).toEqual(TEST_MERGE_REQUEST_URL);
});
});
});
import Vue from 'vue';
import '~/behaviors/markdown/render_gfm';
import { createStore } from '~/ide/stores';
import Info from '~/ide/components/merge_requests/info.vue';
import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
describe('IDE merge request details', () => {
let Component;
let vm;
beforeAll(() => {
Component = Vue.extend(Info);
});
beforeEach(() => {
const store = createStore();
store.state.currentProjectId = 'gitlab-ce';
store.state.currentMergeRequestId = 1;
store.state.projects['gitlab-ce'] = {
mergeRequests: {
1: {
iid: 1,
title: 'Testing',
title_html: '<span class="title-html">Testing</span>',
description: 'Description',
description_html: '<p class="description-html">Description HTML</p>',
},
},
};
vm = createComponentWithStore(Component, store).$mount();
});
afterEach(() => {
vm.$destroy();
});
it('renders merge request IID', () => {
expect(vm.$el.querySelector('.detail-page-header').textContent).toContain('!1');
});
it('renders title as HTML', () => {
expect(vm.$el.querySelector('.title-html')).not.toBe(null);
expect(vm.$el.querySelector('.title').textContent).toContain('Testing');
});
it('renders description as HTML', () => {
expect(vm.$el.querySelector('.description-html')).not.toBe(null);
expect(vm.$el.querySelector('.description').textContent).toContain('Description HTML');
});
});
......@@ -137,9 +137,7 @@ describe('IDE store merge request actions', () => {
store
.dispatch('getMergeRequestData', { projectId: TEST_PROJECT, mergeRequestId: 1 })
.then(() => {
expect(service.getProjectMergeRequestData).toHaveBeenCalledWith(TEST_PROJECT, 1, {
render_html: true,
});
expect(service.getProjectMergeRequestData).toHaveBeenCalledWith(TEST_PROJECT, 1);
done();
})
......
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