summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Hughes <me@iamphill.com>2018-06-01 17:03:34 +0100
committerPhil Hughes <me@iamphill.com>2018-06-06 11:18:30 +0100
commit524ebff5d17fd9111f2277843b384d204ff87a47 (patch)
tree3a59c38f778542b019fe39ce6d2d1536866aae49
parentaf9cc234f2bf854de38e9730266a411f261918da (diff)
downloadgitlab-ce-524ebff5d17fd9111f2277843b384d204ff87a47.tar.gz
Show merge requests in IDE
Closes #45184
-rw-r--r--app/assets/javascripts/ide/components/ide_side_bar.vue46
-rw-r--r--app/assets/javascripts/ide/components/merge_requests/dropdown.vue78
-rw-r--r--app/assets/javascripts/ide/components/merge_requests/item.vue59
-rw-r--r--app/assets/javascripts/ide/components/merge_requests/list.vue91
-rw-r--r--app/assets/javascripts/ide/stores/modules/merge_requests/actions.js2
-rw-r--r--app/assets/javascripts/ide/stores/modules/merge_requests/mutation_types.js2
-rw-r--r--app/assets/javascripts/ide/stores/modules/merge_requests/mutations.js3
-rw-r--r--app/assets/javascripts/ide/stores/modules/merge_requests/state.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/tabs/tabs.js17
9 files changed, 285 insertions, 15 deletions
diff --git a/app/assets/javascripts/ide/components/ide_side_bar.vue b/app/assets/javascripts/ide/components/ide_side_bar.vue
index 3f980203911..e99305d0bc0 100644
--- a/app/assets/javascripts/ide/components/ide_side_bar.vue
+++ b/app/assets/javascripts/ide/components/ide_side_bar.vue
@@ -13,6 +13,7 @@ import CommitSection from './repo_commit_section.vue';
import CommitForm from './commit_sidebar/form.vue';
import IdeReview from './ide_review.vue';
import SuccessMessage from './commit_sidebar/success_message.vue';
+import MergeRequestDropdown from './merge_requests/dropdown.vue';
import { activityBarViews } from '../constants';
export default {
@@ -32,6 +33,7 @@ export default {
CommitForm,
IdeReview,
SuccessMessage,
+ MergeRequestDropdown,
},
data() {
return {
@@ -46,6 +48,7 @@ export default {
'changedFiles',
'stagedFiles',
'lastCommitMsg',
+ 'currentMergeRequestId',
]),
...mapGetters(['currentProject', 'someUncommitedChanges']),
showSuccessMessage() {
@@ -88,9 +91,12 @@ export default {
</div>
</template>
<template v-else>
- <div class="context-header ide-context-header">
+ <div class="context-header ide-context-header dropdown">
<a
- :href="currentProject.web_url"
+ href="#"
+ role="button"
+ @click.prevent
+ data-toggle="dropdown"
>
<div
v-if="currentProject.avatar_url"
@@ -114,19 +120,35 @@ export default {
<div class="sidebar-context-title">
{{ currentProject.name }}
</div>
- <div
- class="sidebar-context-title ide-sidebar-branch-title"
- ref="branchId"
- v-tooltip
- :title="branchTooltipTitle"
- >
- <icon
- name="branch"
- css-classes="append-right-5"
- />{{ currentBranchId }}
+ <div class="d-flex">
+ <div
+ class="sidebar-context-title ide-sidebar-branch-title"
+ ref="branchId"
+ v-tooltip
+ :title="branchTooltipTitle"
+ >
+ <icon
+ name="branch"
+ css-classes="append-right-5"
+ />{{ currentBranchId }}
+ </div>
+ <div
+ v-if="currentMergeRequestId"
+ class="sidebar-context-title ide-sidebar-branch-title prepend-left-8"
+ >
+ <icon
+ name="git-merge"
+ css-classes="append-right-5"
+ />!{{ currentMergeRequestId }}
+ </div>
</div>
</div>
+ <icon
+ class="ml-auto"
+ name="chevron-down"
+ />
</a>
+ <merge-request-dropdown />
</div>
<div class="multi-file-commit-panel-inner-scroll">
<component
diff --git a/app/assets/javascripts/ide/components/merge_requests/dropdown.vue b/app/assets/javascripts/ide/components/merge_requests/dropdown.vue
new file mode 100644
index 00000000000..106f66b764a
--- /dev/null
+++ b/app/assets/javascripts/ide/components/merge_requests/dropdown.vue
@@ -0,0 +1,78 @@
+<script>
+import { mapActions, mapState } from 'vuex';
+import Tabs from '../../../vue_shared/components/tabs/tabs';
+import Tab from '../../../vue_shared/components/tabs/tab.vue';
+import List from './list.vue';
+import { scopes } from '../../stores/modules/merge_requests/constants';
+
+export default {
+ components: {
+ Tabs,
+ Tab,
+ List,
+ },
+ data() {
+ return {
+ activeTabIndex: 0,
+ };
+ },
+ computed: {
+ ...mapState('mergeRequests', ['isLoading', 'mergeRequests']),
+ ...mapState(['currentMergeRequestId']),
+ tabScope() {
+ return this.activeTabIndex === 0 ? scopes.createdByMe : scopes.assignedToMe;
+ },
+ },
+ mounted() {
+ this.fetchMergeRequests();
+ },
+ methods: {
+ ...mapActions('mergeRequests', ['fetchMergeRequests', 'setScope']),
+ updateActiveTab(index) {
+ this.activeTabIndex = index;
+
+ this.setScope(this.tabScope);
+ this.fetchMergeRequests();
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="dropdown-menu">
+ <tabs
+ stop-propagation
+ @changed="updateActiveTab"
+ >
+ <tab
+ :title="__('Created by me')"
+ active
+ >
+ <list
+ v-if="activeTabIndex === 0"
+ :is-loading="isLoading"
+ :items="mergeRequests"
+ :current-id="currentMergeRequestId"
+ @search="fetchMergeRequests"
+ />
+ </tab>
+ <tab :title="__('Assigned to me')">
+ <list
+ v-if="activeTabIndex === 1"
+ :is-loading="isLoading"
+ :items="mergeRequests"
+ :current-id="currentMergeRequestId"
+ @search="fetchMergeRequests"
+ />
+ </tab>
+ </tabs>
+ </div>
+</template>
+
+<style scoped>
+.dropdown-menu {
+ width: 400px;
+ padding: 0;
+ max-height: initial !important;
+}
+</style>
diff --git a/app/assets/javascripts/ide/components/merge_requests/item.vue b/app/assets/javascripts/ide/components/merge_requests/item.vue
new file mode 100644
index 00000000000..11eb5c8b31b
--- /dev/null
+++ b/app/assets/javascripts/ide/components/merge_requests/item.vue
@@ -0,0 +1,59 @@
+<script>
+import Icon from '../../../vue_shared/components/icon.vue';
+
+export default {
+ components: {
+ Icon,
+ },
+ props: {
+ item: {
+ type: Object,
+ required: true,
+ },
+ currentId: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ isActive() {
+ return this.item.iid === parseInt(this.currentId, 10);
+ },
+ pathWithID() {
+ return `${this.item.projectPathWithNamespace}!${this.item.iid}`;
+ },
+ },
+ methods: {
+ clickItem() {
+ this.$emit('click', this.item);
+ },
+ },
+};
+</script>
+
+<template>
+ <button
+ type="button"
+ class="d-flex align-items-center"
+ @click="clickItem"
+ >
+ <span
+ class="d-flex append-right-default"
+ style="min-width: 18px"
+ >
+ <icon
+ v-if="isActive"
+ name="mobile-issue-close"
+ :size="18"
+ />
+ </span>
+ <span>
+ <strong>
+ {{ item.title }}
+ </strong>
+ <span class="d-block mt-1">
+ {{ pathWithID }}
+ </span>
+ </span>
+ </button>
+</template>
diff --git a/app/assets/javascripts/ide/components/merge_requests/list.vue b/app/assets/javascripts/ide/components/merge_requests/list.vue
new file mode 100644
index 00000000000..04b3848f3e2
--- /dev/null
+++ b/app/assets/javascripts/ide/components/merge_requests/list.vue
@@ -0,0 +1,91 @@
+<script>
+import _ from 'underscore';
+import LoadingIcon from '../../../vue_shared/components/loading_icon.vue';
+import Item from './item.vue';
+
+export default {
+ components: {
+ LoadingIcon,
+ Item,
+ },
+ props: {
+ isLoading: {
+ type: Boolean,
+ required: true,
+ },
+ items: {
+ type: Array,
+ required: true,
+ },
+ currentId: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ search: '',
+ };
+ },
+ watch: {
+ isLoading() {
+ this.focusSearch();
+ },
+ },
+ methods: {
+ viewMergeRequest(item) {
+ this.$router.push(`/project/${item.projectPathWithNamespace}/merge_requests/${item.iid}`);
+ },
+ searchMergeRequests: _.debounce(function debounceSearch() {
+ this.$emit('search', this.search);
+ }, 250),
+ focusSearch() {
+ if (!this.isLoading) {
+ this.$nextTick(() => {
+ this.$refs.searchInput.focus();
+ });
+ }
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <loading-icon
+ class="mt-3 mb-3"
+ v-if="isLoading"
+ size="2"
+ />
+ <template v-else>
+ <div class="dropdown-input mt-3 pb-3 mb-3 border-bottom">
+ <input
+ type="search"
+ class="dropdown-input-field"
+ placeholder="Search merge requests"
+ v-model="search"
+ @input="searchMergeRequests"
+ ref="searchInput"
+ />
+ <i
+ aria-hidden="true"
+ class="fa fa-search dropdown-input-search"
+ ></i>
+ </div>
+ <div class="dropdown-content">
+ <ul class="mb-3">
+ <li
+ v-for="item in items"
+ :key="item.id"
+ >
+ <item
+ :item="item"
+ :current-id="currentId"
+ @click="viewMergeRequest"
+ />
+ </li>
+ </ul>
+ </div>
+ </template>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js b/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
index d3050183bd3..2b32c0c0b55 100644
--- a/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
@@ -22,4 +22,6 @@ export const fetchMergeRequests = ({ dispatch, state: { scope, state } }, search
export const resetMergeRequests = ({ commit }) => commit(types.RESET_MERGE_REQUESTS);
+export const setScope = ({ commit }, scope) => commit(types.SET_SCOPE, scope);
+
export default () => {};
diff --git a/app/assets/javascripts/ide/stores/modules/merge_requests/mutation_types.js b/app/assets/javascripts/ide/stores/modules/merge_requests/mutation_types.js
index 0badddcbae7..6d163491209 100644
--- a/app/assets/javascripts/ide/stores/modules/merge_requests/mutation_types.js
+++ b/app/assets/javascripts/ide/stores/modules/merge_requests/mutation_types.js
@@ -3,3 +3,5 @@ export const RECEIVE_MERGE_REQUESTS_ERROR = 'RECEIVE_MERGE_REQUESTS_ERROR';
export const RECEIVE_MERGE_REQUESTS_SUCCESS = 'RECEIVE_MERGE_REQUESTS_SUCCESS';
export const RESET_MERGE_REQUESTS = 'RESET_MERGE_REQUESTS';
+
+export const SET_SCOPE = 'SET_SCOPE';
diff --git a/app/assets/javascripts/ide/stores/modules/merge_requests/mutations.js b/app/assets/javascripts/ide/stores/modules/merge_requests/mutations.js
index 98102a68e08..5c0c502b0d1 100644
--- a/app/assets/javascripts/ide/stores/modules/merge_requests/mutations.js
+++ b/app/assets/javascripts/ide/stores/modules/merge_requests/mutations.js
@@ -23,4 +23,7 @@ export default {
[types.RESET_MERGE_REQUESTS](state) {
state.mergeRequests = [];
},
+ [types.SET_SCOPE](state, scope) {
+ state.scope = scope;
+ },
};
diff --git a/app/assets/javascripts/ide/stores/modules/merge_requests/state.js b/app/assets/javascripts/ide/stores/modules/merge_requests/state.js
index 2947b686c1c..64ca24b1af6 100644
--- a/app/assets/javascripts/ide/stores/modules/merge_requests/state.js
+++ b/app/assets/javascripts/ide/stores/modules/merge_requests/state.js
@@ -3,6 +3,6 @@ import { scopes, states } from './constants';
export default () => ({
isLoading: false,
mergeRequests: [],
- scope: scopes.assignedToMe,
+ scope: scopes.createdByMe,
state: states.opened,
});
diff --git a/app/assets/javascripts/vue_shared/components/tabs/tabs.js b/app/assets/javascripts/vue_shared/components/tabs/tabs.js
index 4362264caa5..8497c3e851f 100644
--- a/app/assets/javascripts/vue_shared/components/tabs/tabs.js
+++ b/app/assets/javascripts/vue_shared/components/tabs/tabs.js
@@ -1,4 +1,11 @@
export default {
+ props: {
+ stopPropagation: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
data() {
return {
currentIndex: 0,
@@ -13,11 +20,17 @@ export default {
this.tabs = this.$children.filter(child => child.isTab);
this.currentIndex = this.tabs.findIndex(tab => tab.localActive);
},
- setTab(index) {
+ setTab(e, index) {
+ if (this.stopPropagation) {
+ e.stopPropagation();
+ }
+
this.tabs[this.currentIndex].localActive = false;
this.tabs[index].localActive = true;
this.currentIndex = index;
+
+ this.$emit('changed', this.currentIndex);
},
},
render(h) {
@@ -36,7 +49,7 @@ export default {
href: '#',
},
on: {
- click: () => this.setTab(i),
+ click: e => this.setTab(e, i),
},
},
tab.$slots.title || tab.title,