diff options
author | Filipa Lacerda <filipa@gitlab.com> | 2018-05-04 13:40:38 +0000 |
---|---|---|
committer | Filipa Lacerda <filipa@gitlab.com> | 2018-05-04 13:40:38 +0000 |
commit | 9f2f8679bec1e2d4161c429c2e6db80a20e376c9 (patch) | |
tree | 3e6f89e5dc2cb6e33c281fbae32ebe4e52d1968d | |
parent | 805302c0c09f0c6a3226efd2d3fca1b46015f9e0 (diff) | |
parent | 14b8ac3e2a1c29c7f86391118f6916959a1337c6 (diff) | |
download | gitlab-ce-9f2f8679bec1e2d4161c429c2e6db80a20e376c9.tar.gz |
Merge branch 'ide-sidebar-merge-request' into '44846-improve-web-ide-left-panel-and-modes'
Added merge request review to IDE sidebar
See merge request gitlab-org/gitlab-ce!18709
-rw-r--r-- | app/assets/javascripts/ide/components/commit_sidebar/list_item.vue | 3 | ||||
-rw-r--r-- | app/assets/javascripts/ide/components/editor_mode_dropdown.vue | 80 | ||||
-rw-r--r-- | app/assets/javascripts/ide/components/ide_review.vue | 42 | ||||
-rw-r--r-- | app/assets/javascripts/ide/components/ide_side_bar.vue | 22 | ||||
-rw-r--r-- | app/assets/javascripts/ide/components/repo_editor.vue | 9 | ||||
-rw-r--r-- | app/assets/javascripts/ide/constants.js | 6 | ||||
-rw-r--r-- | app/assets/javascripts/ide/ide_router.js | 5 | ||||
-rw-r--r-- | app/assets/javascripts/ide/stores/actions/file.js | 9 | ||||
-rw-r--r-- | app/assets/javascripts/ide/stores/state.js | 4 | ||||
-rw-r--r-- | app/assets/stylesheets/pages/repo.scss | 28 | ||||
-rw-r--r-- | spec/javascripts/ide/components/ide_review_spec.js | 36 | ||||
-rw-r--r-- | spec/javascripts/ide/mock_data.js | 1 |
12 files changed, 165 insertions, 80 deletions
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue index 872302840e2..03f3e4de83c 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue @@ -3,6 +3,7 @@ import { mapActions } from 'vuex'; import Icon from '~/vue_shared/components/icon.vue'; import StageButton from './stage_button.vue'; import UnstageButton from './unstage_button.vue'; +import { viewerTypes } from '../../constants'; export default { components: { @@ -53,7 +54,7 @@ export default { keyPrefix: this.keyPrefix.toLowerCase(), }).then(changeViewer => { if (changeViewer) { - this.updateViewer('diff'); + this.updateViewer(viewerTypes.diff); } }); }, diff --git a/app/assets/javascripts/ide/components/editor_mode_dropdown.vue b/app/assets/javascripts/ide/components/editor_mode_dropdown.vue index 0c44a755f56..b9af4d27145 100644 --- a/app/assets/javascripts/ide/components/editor_mode_dropdown.vue +++ b/app/assets/javascripts/ide/components/editor_mode_dropdown.vue @@ -1,28 +1,15 @@ <script> -import Icon from '~/vue_shared/components/icon.vue'; import { __, sprintf } from '~/locale'; +import { viewerTypes } from '../constants'; export default { - components: { - Icon, - }, props: { - hasChanges: { - type: Boolean, - required: false, - default: false, - }, - mergeRequestId: { - type: String, - required: false, - default: '', - }, viewer: { type: String, required: true, }, - showShadow: { - type: Boolean, + mergeRequestId: { + type: Number, required: true, }, }, @@ -38,84 +25,45 @@ export default { this.$emit('click', mode); }, }, + viewerTypes, }; </script> <template> <div class="dropdown" - :class="{ - shadow: showShadow, - }" > <button type="button" - class="btn btn-primary btn-sm" - :class="{ - 'btn-inverted': hasChanges, - }" + class="btn btn-link" data-toggle="dropdown" > - <template v-if="viewer === 'mrdiff' && mergeRequestId"> - {{ mergeReviewLine }} - </template> - <template v-else-if="viewer === 'editor'"> - {{ __('Editing') }} - </template> - <template v-else> - {{ __('Reviewing') }} - </template> - <icon - name="angle-down" - :size="12" - css-classes="caret-down" - /> + {{ __('Edit') }} </button> <div class="dropdown-menu dropdown-menu-selectable dropdown-open-left"> <ul> - <template v-if="mergeRequestId"> - <li> - <a - href="#" - @click.prevent="changeMode('mrdiff')" - :class="{ - 'is-active': viewer === 'mrdiff', - }" - > - <strong class="dropdown-menu-inner-title"> - {{ mergeReviewLine }} - </strong> - <span class="dropdown-menu-inner-content"> - {{ __('Compare changes with the merge request target branch') }} - </span> - </a> - </li> - <li - role="separator" - class="divider" - > - </li> - </template> <li> <a href="#" - @click.prevent="changeMode('editor')" + @click.prevent="changeMode($options.viewerTypes.mr)" :class="{ - 'is-active': viewer === 'editor', + 'is-active': viewer === $options.viewerTypes.mr, }" > - <strong class="dropdown-menu-inner-title">{{ __('Editing') }}</strong> + <strong class="dropdown-menu-inner-title"> + {{ mergeReviewLine }} + </strong> <span class="dropdown-menu-inner-content"> - {{ __('View and edit lines') }} + {{ __('Compare changes with the merge request target branch') }} </span> </a> </li> <li> <a href="#" - @click.prevent="changeMode('diff')" + @click.prevent="changeMode($options.viewerTypes.diff)" :class="{ - 'is-active': viewer === 'diff', + 'is-active': viewer === $options.viewerTypes.diff, }" > <strong class="dropdown-menu-inner-title">{{ __('Reviewing') }}</strong> diff --git a/app/assets/javascripts/ide/components/ide_review.vue b/app/assets/javascripts/ide/components/ide_review.vue index ede1a753f17..0c9ec3b00f0 100644 --- a/app/assets/javascripts/ide/components/ide_review.vue +++ b/app/assets/javascripts/ide/components/ide_review.vue @@ -1,25 +1,61 @@ <script> +import { mapGetters, mapState, mapActions } from 'vuex'; import IdeTreeList from './ide_tree_list.vue'; +import EditorModeDropdown from './editor_mode_dropdown.vue'; +import { viewerTypes } from '../constants'; export default { components: { IdeTreeList, + EditorModeDropdown, + }, + computed: { + ...mapGetters(['currentMergeRequest']), + ...mapState(['viewer']), + showLatestChangesText() { + return !this.currentMergeRequest || this.viewer === viewerTypes.diff; + }, + showMergeRequestText() { + return this.currentMergeRequest && this.viewer === viewerTypes.mr; + }, + }, + mounted() { + this.$nextTick(() => { + this.updateViewer(this.currentMergeRequest ? viewerTypes.mr : viewerTypes.diff); + }); + }, + methods: { + ...mapActions(['updateViewer']), }, }; </script> <template> <ide-tree-list - viewer-type="diff" + :viewer-type="viewer" header-class="ide-review-header" :disable-action-dropdown="true" > <template slot="header" > - {{ __('Review') }} + <div class="ide-review-button-holder"> + {{ __('Review') }} + <editor-mode-dropdown + v-if="currentMergeRequest" + :viewer="viewer" + :merge-request-id="currentMergeRequest.iid" + @click="updateViewer" + /> + </div> <div class="prepend-top-5 ide-review-sub-header"> - {{ __('Lastest changes') }} + <template v-if="showLatestChangesText"> + {{ __('Latest changes') }} + </template> + <template v-else-if="showMergeRequestText"> + {{ __('Merge request') }} + (<a :href="currentMergeRequest.web_url">!{{ currentMergeRequest.iid }}</a>) + </template> </div> </template> </ide-tree-list> diff --git a/app/assets/javascripts/ide/components/ide_side_bar.vue b/app/assets/javascripts/ide/components/ide_side_bar.vue index 3fb9b8b241d..2872d4e8687 100644 --- a/app/assets/javascripts/ide/components/ide_side_bar.vue +++ b/app/assets/javascripts/ide/components/ide_side_bar.vue @@ -2,6 +2,7 @@ import { mapState, mapGetters } from 'vuex'; import ProjectAvatarImage from '~/vue_shared/components/project_avatar/image.vue'; import Icon from '~/vue_shared/components/icon.vue'; +import tooltip from '~/vue_shared/directives/tooltip'; import PanelResizer from '~/vue_shared/components/panel_resizer.vue'; import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue'; import Identicon from '../../vue_shared/components/identicon.vue'; @@ -12,6 +13,9 @@ import CommitSection from './repo_commit_section.vue'; import IdeReview from './ide_review.vue'; export default { + directives: { + tooltip, + }, components: { Icon, PanelResizer, @@ -24,9 +28,24 @@ export default { IdeTree, IdeReview, }, + data() { + return { + showTooltip: false, + }; + }, computed: { ...mapState(['loading', 'currentBranchId', 'currentActivityView']), ...mapGetters(['currentProject']), + branchTooltipTitle() { + return this.showTooltip ? this.currentBranchId : undefined; + }, + }, + watch: { + currentBranchId() { + this.$nextTick(() => { + this.showTooltip = this.$refs.branchId.scrollWidth > this.$refs.branchId.offsetWidth; + }); + }, }, }; </script> @@ -79,6 +98,9 @@ export default { </div> <div class="sidebar-context-title ide-sidebar-branch-title" + ref="branchId" + v-tooltip + :title="branchTooltipTitle" > <icon name="branch" diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue index 8a08f9fc7db..ff7e546fb9c 100644 --- a/app/assets/javascripts/ide/components/repo_editor.vue +++ b/app/assets/javascripts/ide/components/repo_editor.vue @@ -3,7 +3,7 @@ import { mapState, mapGetters, mapActions } from 'vuex'; import flash from '~/flash'; import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue'; -import { activityBarViews } from '../constants'; +import { activityBarViews, viewerTypes } from '../constants'; import monacoLoader from '../monaco_loader'; import Editor from '../lib/editor'; import IdeFileButtons from './ide_file_buttons.vue'; @@ -121,7 +121,7 @@ export default { this.editor.dispose(); this.$nextTick(() => { - if (this.viewer === 'editor') { + if (this.viewer === viewerTypes.edit) { this.editor.createInstance(this.$refs.editor); } else { this.editor.createDiffInstance(this.$refs.editor, !this.isReviewModeActive); @@ -140,7 +140,7 @@ export default { this.file.staged && this.file.key.indexOf('unstaged-') === 0 ? head : null, ); - if (this.viewer === 'mrdiff') { + if (this.viewer === viewerTypes.mr) { this.editor.attachMergeRequestModel(this.model); } else { this.editor.attachModel(this.model); @@ -181,6 +181,7 @@ export default { }); }, }, + viewerTypes, }; </script> @@ -199,7 +200,7 @@ export default { href="javascript:void(0);" role="button" @click.prevent="setFileViewMode({ file, viewMode: 'edit' })"> - <template v-if="viewer === 'editor'"> + <template v-if="viewer === $options.viewerTypes.edit"> {{ __('Edit') }} </template> <template v-else> diff --git a/app/assets/javascripts/ide/constants.js b/app/assets/javascripts/ide/constants.js index 16110b2f5a4..86c5f0c4421 100644 --- a/app/assets/javascripts/ide/constants.js +++ b/app/assets/javascripts/ide/constants.js @@ -12,3 +12,9 @@ export const activityBarViews = { commit: 'commit-section', review: 'ide-review', }; + +export const viewerTypes = { + mr: 'mrdiff', + edit: 'editor', + diff: 'diff', +}; diff --git a/app/assets/javascripts/ide/ide_router.js b/app/assets/javascripts/ide/ide_router.js index ee30e5074b3..adca85dc65b 100644 --- a/app/assets/javascripts/ide/ide_router.js +++ b/app/assets/javascripts/ide/ide_router.js @@ -2,6 +2,7 @@ import Vue from 'vue'; import VueRouter from 'vue-router'; import flash from '~/flash'; import store from './stores'; +import { activityBarViews } from './constants'; Vue.use(VueRouter); @@ -101,14 +102,14 @@ router.beforeEach((to, from, next) => { throw e; }); } else if (to.params.mrid) { - store.dispatch('updateViewer', 'mrdiff'); - store .dispatch('getMergeRequestData', { projectId: fullProjectId, mergeRequestId: to.params.mrid, }) .then(mr => { + store.dispatch('updateActivityBarView', activityBarViews.review); + store.dispatch('getBranchData', { projectId: fullProjectId, branchId: mr.source_branch, diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js index 4fe6f5334e3..8076de1b7e3 100644 --- a/app/assets/javascripts/ide/stores/actions/file.js +++ b/app/assets/javascripts/ide/stores/actions/file.js @@ -5,6 +5,7 @@ import service from '../../services'; import * as types from '../mutation_types'; import router from '../../ide_router'; import { setPageTitle } from '../utils'; +import { viewerTypes } from '../../constants'; export const closeFile = ({ commit, state, dispatch }, file) => { const path = file.path; @@ -23,7 +24,7 @@ export const closeFile = ({ commit, state, dispatch }, file) => { const nextFileToOpen = state.openFiles[nextIndexToOpen]; if (nextFileToOpen.pending) { - dispatch('updateViewer', 'diff'); + dispatch('updateViewer', viewerTypes.diff); dispatch('openPendingTab', { file: nextFileToOpen, keyPrefix: nextFileToOpen.staged ? 'staged' : 'unstaged', @@ -194,7 +195,11 @@ export const unstageChange = ({ commit }, path) => { }; export const openPendingTab = ({ commit, getters, dispatch, state }, { file, keyPrefix }) => { - if (getters.activeFile && getters.activeFile.path === file.path && state.viewer === 'diff') { + if ( + getters.activeFile && + getters.activeFile.path === file.path && + state.viewer === viewerTypes.diff + ) { return false; } diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js index fd31fcd14ab..e7411f16a4f 100644 --- a/app/assets/javascripts/ide/stores/state.js +++ b/app/assets/javascripts/ide/stores/state.js @@ -1,4 +1,4 @@ -import { activityBarViews } from '../constants'; +import { activityBarViews, viewerTypes } from '../constants'; export default () => ({ currentProjectId: '', @@ -18,7 +18,7 @@ export default () => ({ rightPanelCollapsed: false, panelResizing: false, entries: {}, - viewer: 'editor', + viewer: viewerTypes.edit, delayViewerUpdated: false, currentActivityView: activityBarViews.edit, unusedSeal: true, diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index 9c374cda3f3..d3330cda8c6 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -972,6 +972,14 @@ .ide-review-header { flex-direction: column; align-items: flex-start; + + .dropdown { + margin-left: auto; + } + + a { + color: $gl-link-color; + } } .ide-review-sub-header { @@ -987,3 +995,23 @@ .ide-new-modal-label { line-height: 34px; } + +.ide-review-button-holder { + display: flex; + width: 100%; + align-items: center; +} + +.ide-context-header { + .avatar { + flex: 0 0 40px; + } +} + +.ide-sidebar-project-title { + min-width: 0; + + .sidebar-context-title { + white-space: nowrap; + } +} diff --git a/spec/javascripts/ide/components/ide_review_spec.js b/spec/javascripts/ide/components/ide_review_spec.js index 13753c6b6fb..b9ee22b7c1a 100644 --- a/spec/javascripts/ide/components/ide_review_spec.js +++ b/spec/javascripts/ide/components/ide_review_spec.js @@ -2,6 +2,7 @@ import Vue from 'vue'; import IdeReview from '~/ide/components/ide_review.vue'; import store from '~/ide/stores'; import { createComponentWithStore } from '../../helpers/vue_mount_component_helper'; +import { trimText } from '../../helpers/vue_component_helper'; import { resetStore, file } from '../helpers'; import { projectData } from '../mock_data'; @@ -30,4 +31,39 @@ describe('IDE review mode', () => { it('renders list of files', () => { expect(vm.$el.textContent).toContain('fileName'); }); + + describe('merge request', () => { + beforeEach(done => { + store.state.currentMergeRequestId = '1'; + store.state.projects.abcproject.mergeRequests['1'] = { + iid: 123, + web_url: 'testing123', + }; + + vm.$nextTick(done); + }); + + it('renders edit dropdown', () => { + expect(vm.$el.querySelector('.btn')).not.toBe(null); + }); + + it('renders merge request link & IID', () => { + const link = vm.$el.querySelector('.ide-review-sub-header'); + + expect(link.querySelector('a').getAttribute('href')).toBe('testing123'); + expect(trimText(link.textContent)).toBe('Merge request (!123)'); + }); + + it('changes text to latest changes when viewer is not mrdiff', done => { + store.state.viewer = 'diff'; + + vm.$nextTick(() => { + expect(trimText(vm.$el.querySelector('.ide-review-sub-header').textContent)).toBe( + 'Latest changes', + ); + + done(); + }); + }); + }); }); diff --git a/spec/javascripts/ide/mock_data.js b/spec/javascripts/ide/mock_data.js index c8206778639..3c6d75ab5e4 100644 --- a/spec/javascripts/ide/mock_data.js +++ b/spec/javascripts/ide/mock_data.js @@ -11,4 +11,5 @@ export const projectData = { treeId: 'abcproject/master', }, }, + mergeRequests: {}, }; |