Commit 8a21c31b authored by Phil Hughes's avatar Phil Hughes

Merge branch '44846-improve-web-ide-left-panel-and-modes' into ide-sidebar-commit-box

parents 616c1c72 9f2f8679
......@@ -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);
}
});
},
......
<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>
......
<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>
......
......@@ -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';
......@@ -15,6 +16,9 @@ import SuccessMessage from './commit_sidebar/success_message.vue';
import { activityBarViews } from '../constants';
export default {
directives: {
tooltip,
},
components: {
Icon,
PanelResizer,
......@@ -29,6 +33,11 @@ export default {
IdeReview,
SuccessMessage,
},
data() {
return {
showTooltip: false,
};
},
computed: {
...mapState([
'loading',
......@@ -45,6 +54,16 @@ export default {
(this.lastCommitMsg && !this.someUncommitedChanges)
);
},
branchTooltipTitle() {
return this.showTooltip ? this.currentBranchId : undefined;
},
},
watch: {
currentBranchId() {
this.$nextTick(() => {
this.showTooltip = this.$refs.branchId.scrollWidth > this.$refs.branchId.offsetWidth;
});
},
},
};
</script>
......@@ -97,6 +116,9 @@ export default {
</div>
<div
class="sidebar-context-title ide-sidebar-branch-title"
ref="branchId"
v-tooltip
:title="branchTooltipTitle"
>
<icon
name="branch"
......
......@@ -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>
......
......@@ -16,3 +16,9 @@ export const activityBarViews = {
commit: 'commit-section',
review: 'ide-review',
};
export const viewerTypes = {
mr: 'mrdiff',
edit: 'editor',
diff: 'diff',
};
......@@ -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,
......
......@@ -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',
......
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,
......
......@@ -1020,6 +1020,14 @@
.ide-review-header {
flex-direction: column;
align-items: flex-start;
.dropdown {
margin-left: auto;
}
a {
color: $gl-link-color;
}
}
.ide-review-sub-header {
......@@ -1049,3 +1057,23 @@
flex-direction: column;
justify-content: center;
}
.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;
}
}
......@@ -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();
});
});
});
});
......@@ -11,4 +11,5 @@ export const projectData = {
treeId: 'abcproject/master',
},
},
mergeRequests: {},
};
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