Merge branch '44846-improve-web-ide-left-panel-and-modes' into ide-sidebar-commit-box
This commit is contained in:
commit
8a21c31b8b
12 changed files with 160 additions and 79 deletions
|
@ -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);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
@ -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,48 +25,29 @@ 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')"
|
||||
@click.prevent="changeMode($options.viewerTypes.mr)"
|
||||
:class="{
|
||||
'is-active': viewer === 'mrdiff',
|
||||
'is-active': viewer === $options.viewerTypes.mr,
|
||||
}"
|
||||
>
|
||||
<strong class="dropdown-menu-inner-title">
|
||||
|
@ -90,32 +58,12 @@ export default {
|
|||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
role="separator"
|
||||
class="divider"
|
||||
>
|
||||
</li>
|
||||
</template>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
@click.prevent="changeMode('editor')"
|
||||
@click.prevent="changeMode($options.viewerTypes.diff)"
|
||||
:class="{
|
||||
'is-active': viewer === 'editor',
|
||||
}"
|
||||
>
|
||||
<strong class="dropdown-menu-inner-title">{{ __('Editing') }}</strong>
|
||||
<span class="dropdown-menu-inner-content">
|
||||
{{ __('View and edit lines') }}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
@click.prevent="changeMode('diff')"
|
||||
:class="{
|
||||
'is-active': viewer === 'diff',
|
||||
'is-active': viewer === $options.viewerTypes.diff,
|
||||
}"
|
||||
>
|
||||
<strong class="dropdown-menu-inner-title">{{ __('Reviewing') }}</strong>
|
||||
|
|
|
@ -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"
|
||||
>
|
||||
<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',
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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: {},
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue