Skip to content
Snippets Groups Projects
Commit db739548 authored by Paul Slaughter's avatar Paul Slaughter Committed by Phil Hughes
Browse files

Web IDE context header redesign

parent 379083be
No related branches found
No related tags found
1 merge request!10495Merge Requests - Assignee
Showing
with 227 additions and 219 deletions
Loading
Loading
@@ -13,11 +13,8 @@ export default {
tooltip,
},
computed: {
...mapGetters(['currentProject', 'hasChanges']),
...mapGetters(['hasChanges']),
...mapState(['currentActivityView']),
goBackUrl() {
return document.referrer || this.currentProject.web_url;
},
},
methods: {
...mapActions(['updateActivityBarView']),
Loading
Loading
@@ -36,22 +33,6 @@ export default {
<template>
<nav class="ide-activity-bar">
<ul class="list-unstyled">
<li v-once>
<a
v-tooltip
:href="goBackUrl"
:title="s__('IDE|Go back')"
:aria-label="s__('IDE|Go back')"
data-container="body"
data-placement="right"
class="ide-sidebar-link"
>
<icon
:size="16"
name="go-back"
/>
</a>
</li>
<li>
<button
v-tooltip
Loading
Loading
<script>
import ProjectAvatarDefault from '~/vue_shared/components/project_avatar/default.vue';
export default {
components: {
ProjectAvatarDefault,
},
props: {
project: {
type: Object,
required: true,
},
},
};
</script>
<template>
<div class="context-header ide-context-header">
<a
:href="project.web_url"
:title="s__('IDE|Go to project')"
>
<project-avatar-default
:project="project"
:size="48"
/>
<span class="ide-sidebar-project-title">
<span class="sidebar-context-title">
{{ project.name }}
</span>
<span class="sidebar-context-title text-secondary">
{{ project.path_with_namespace }}
</span>
</span>
</a>
</div>
</template>
<script>
import $ from 'jquery';
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';
import IdeTree from './ide_tree.vue';
import ResizablePanel from './resizable_panel.vue';
import ActivityBar from './activity_bar.vue';
Loading
Loading
@@ -14,43 +8,28 @@ 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 IdeProjectHeader from './ide_project_header.vue';
import { activityBarViews } from '../constants';
 
export default {
directives: {
tooltip,
},
components: {
Icon,
PanelResizer,
SkeletonLoadingContainer,
ResizablePanel,
ActivityBar,
ProjectAvatarImage,
Identicon,
CommitSection,
IdeTree,
CommitForm,
IdeReview,
SuccessMessage,
MergeRequestDropdown,
},
data() {
return {
showTooltip: false,
showMergeRequestsDropdown: false,
};
IdeProjectHeader,
},
computed: {
...mapState([
'loading',
'currentBranchId',
'currentActivityView',
'changedFiles',
'stagedFiles',
'lastCommitMsg',
'currentMergeRequestId',
]),
...mapGetters(['currentProject', 'someUncommitedChanges']),
showSuccessMessage() {
Loading
Loading
@@ -59,46 +38,6 @@ export default {
(this.lastCommitMsg && !this.someUncommitedChanges)
);
},
branchTooltipTitle() {
return this.showTooltip ? this.currentBranchId : undefined;
},
},
watch: {
currentBranchId() {
this.$nextTick(() => {
if (!this.$refs.branchId) return;
this.showTooltip = this.$refs.branchId.scrollWidth > this.$refs.branchId.offsetWidth;
});
},
loading() {
this.$nextTick(() => {
this.addDropdownListeners();
});
},
},
mounted() {
this.addDropdownListeners();
},
beforeDestroy() {
$(this.$refs.mergeRequestDropdown)
.off('show.bs.dropdown')
.off('hide.bs.dropdown');
},
methods: {
addDropdownListeners() {
if (!this.$refs.mergeRequestDropdown) return;
$(this.$refs.mergeRequestDropdown)
.on('show.bs.dropdown', () => {
this.toggleMergeRequestDropdown();
}).on('hide.bs.dropdown', () => {
this.toggleMergeRequestDropdown();
});
},
toggleMergeRequestDropdown() {
this.showMergeRequestsDropdown = !this.showMergeRequestsDropdown;
},
},
};
</script>
Loading
Loading
@@ -108,12 +47,10 @@ export default {
:collapsible="false"
:initial-width="340"
side="left"
class="flex-column"
>
<activity-bar
v-if="!loading"
/>
<div class="multi-file-commit-panel-inner">
<template v-if="loading">
<template v-if="loading">
<div class="multi-file-commit-panel-inner">
<div
v-for="n in 3"
:key="n"
Loading
Loading
@@ -121,81 +58,23 @@ export default {
>
<skeleton-loading-container />
</div>
</template>
<template v-else>
<div
ref="mergeRequestDropdown"
class="context-header ide-context-header dropdown"
>
<button
type="button"
data-toggle="dropdown"
>
<div
v-if="currentProject.avatar_url"
class="avatar-container s40 project-avatar"
>
<project-avatar-image
:link-href="currentProject.path"
:img-src="currentProject.avatar_url"
:img-alt="currentProject.name"
:img-size="40"
class="avatar-container project-avatar"
/>
</div>
<identicon
v-else
:entity-id="currentProject.id"
:entity-name="currentProject.name"
size-class="s40"
</div>
</template>
<template v-else>
<ide-project-header
:project="currentProject"
/>
<div class="ide-context-body d-flex flex-fill">
<activity-bar />
<div class="multi-file-commit-panel-inner">
<div class="multi-file-commit-panel-inner-content">
<component
:is="currentActivityView"
/>
<div class="ide-sidebar-project-title">
<div class="sidebar-context-title">
{{ currentProject.name }}
</div>
<div class="d-flex">
<div
v-tooltip
v-if="currentBranchId"
ref="branchId"
:title="branchTooltipTitle"
class="sidebar-context-title ide-sidebar-branch-title"
>
<icon
name="branch"
css-classes="append-right-5"
/>{{ currentBranchId }}
</div>
<div
v-if="currentMergeRequestId"
:class="{
'prepend-left-8': currentBranchId
}"
class="sidebar-context-title ide-sidebar-branch-title"
>
<icon
name="git-merge"
css-classes="append-right-5"
/>!{{ currentMergeRequestId }}
</div>
</div>
</div>
<icon
class="ml-auto"
name="chevron-down"
/>
</button>
<merge-request-dropdown
:show="showMergeRequestsDropdown"
/>
</div>
<div class="multi-file-commit-panel-inner-scroll">
<component
:is="currentActivityView"
/>
</div>
<commit-form />
</div>
<commit-form />
</template>
</div>
</div>
</template>
</resizable-panel>
</template>
Loading
Loading
@@ -35,7 +35,6 @@ export default {
 
<template>
<ide-tree-list
header-class="d-flex w-100"
viewer-type="editor"
>
<template
Loading
Loading
Loading
Loading
@@ -59,12 +59,16 @@ export default {
>
<slot name="header"></slot>
</header>
<repo-file
v-for="file in currentTree.tree"
:key="file.key"
:file="file"
:level="0"
/>
<div
class="ide-tree-body"
>
<repo-file
v-for="file in currentTree.tree"
:key="file.key"
:file="file"
:level="0"
/>
</div>
</template>
</div>
</template>
<script>
import Identicon from '../identicon.vue';
import ProjectAvatarImage from './image.vue';
export default {
components: {
Identicon,
ProjectAvatarImage,
},
props: {
project: {
type: Object,
required: true,
},
size: {
type: Number,
default: 40,
},
},
computed: {
sizeClass() {
return `s${this.size}`;
},
},
};
</script>
<template>
<span
:class="sizeClass"
class="avatar-container project-avatar"
>
<project-avatar-image
v-if="project.avatar_url"
:link-href="project.path"
:img-src="project.avatar_url"
:img-alt="project.name"
:img-size="size"
/>
<identicon
v-else
:entity-id="project.id"
:entity-name="project.name"
:size-class="sizeClass"
/>
</span>
</template>
Loading
Loading
@@ -78,6 +78,7 @@
&.s26 { font-size: 20px; line-height: 1.33; }
&.s32 { font-size: 20px; line-height: 30px; }
&.s40 { font-size: 16px; line-height: 38px; }
&.s48 { font-size: 20px; line-height: 46px; }
&.s60 { font-size: 32px; line-height: 58px; }
&.s70 { font-size: 34px; line-height: 70px; }
&.s90 { font-size: 36px; line-height: 88px; }
Loading
Loading
Loading
Loading
@@ -55,6 +55,11 @@
.sidebar-context-title {
overflow: hidden;
text-overflow: ellipsis;
&.text-secondary {
font-weight: normal;
font-size: 0.8em;
}
}
}
 
Loading
Loading
@import 'framework/variables';
@import 'framework/mixins';
 
$ide-activity-bar-width: 60px;
$ide-context-header-padding: 10px;
$ide-project-avatar-end: $ide-context-header-padding + 48px;
$ide-tree-padding: $gl-padding;
$ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
.project-refs-form,
.project-refs-target-form {
display: inline-block;
Loading
Loading
@@ -24,7 +30,6 @@
display: flex;
height: calc(100vh - #{$header-height});
margin-top: 0;
border-top: 1px solid $white-dark;
padding-bottom: $ide-statusbar-height;
color: $gl-text-color;
 
Loading
Loading
@@ -41,10 +46,10 @@
}
 
.ide-file-list {
display: flex;
flex-direction: column;
flex: 1;
padding-left: $gl-padding;
padding-right: $gl-padding;
padding-bottom: $grid-size;
overflow: hidden;
 
.file {
height: 32px;
Loading
Loading
@@ -517,17 +522,12 @@
 
> a,
> button {
height: 60px;
text-decoration: none;
padding-top: $gl-padding-8;
padding-bottom: $gl-padding-8;
}
}
 
.projects-sidebar {
min-height: 0;
display: flex;
flex-direction: column;
flex: 1;
}
.multi-file-commit-panel-inner {
position: relative;
display: flex;
Loading
Loading
@@ -537,11 +537,11 @@
width: 100%;
}
 
.multi-file-commit-panel-inner-scroll {
.multi-file-commit-panel-inner-content {
display: flex;
flex: 1;
flex-direction: column;
overflow: auto;
overflow: hidden;
background-color: $white-light;
border-left: 1px solid $white-dark;
border-top: 1px solid $white-dark;
Loading
Loading
@@ -803,12 +803,6 @@
height: calc(100vh - #{$header-height + $flash-height});
}
}
.projects-sidebar {
.multi-file-commit-panel-inner-scroll {
flex: 1;
}
}
}
}
 
Loading
Loading
@@ -964,7 +958,7 @@
 
.ide-activity-bar {
position: relative;
flex: 0 0 60px;
flex: 0 0 $ide-activity-bar-width;
z-index: 1;
}
 
Loading
Loading
@@ -1060,10 +1054,12 @@
}
 
.ide-tree-header {
flex: 0 0 auto;
display: flex;
align-items: center;
margin-bottom: 8px;
padding: 12px 0;
margin-left: $ide-tree-padding;
margin-right: $ide-tree-padding;
border-bottom: 1px solid $white-dark;
 
.ide-new-btn {
Loading
Loading
@@ -1075,6 +1071,12 @@
}
}
 
.ide-tree-body {
overflow: auto;
padding-left: $ide-tree-padding;
padding-right: $ide-tree-padding;
}
.ide-sidebar-branch-title {
font-weight: $gl-font-weight-normal;
 
Loading
Loading
@@ -1163,14 +1165,23 @@
}
 
.ide-context-header {
.avatar {
flex: 0 0 38px;
}
.ide-merge-requests-dropdown.dropdown-menu {
width: 385px;
max-height: initial;
}
.avatar-container {
flex: initial;
margin-right: 0;
}
.ide-sidebar-project-title {
margin-left: $ide-tree-text-start - $ide-project-avatar-end;
}
}
.ide-context-body {
overflow: hidden;
}
 
.ide-sidebar-project-title {
Loading
Loading
@@ -1178,10 +1189,11 @@
 
.sidebar-context-title {
white-space: nowrap;
}
display: block;
 
.ide-sidebar-branch-title {
min-width: 50px;
&.text-secondary {
font-weight: normal;
}
}
}
 
Loading
Loading
---
title: Redesign Web IDE back button and context header
merge_request: 20850
author:
type: changed
Loading
Loading
@@ -2911,7 +2911,7 @@ msgstr ""
msgid "IDE|Edit"
msgstr ""
 
msgid "IDE|Go back"
msgid "IDE|Go to project"
msgstr ""
 
msgid "IDE|Open in file view"
Loading
Loading
Loading
Loading
@@ -24,26 +24,6 @@ describe('IDE activity bar', () => {
resetStore(vm.$store);
});
 
describe('goBackUrl', () => {
it('renders the Go Back link with the referrer when present', () => {
const fakeReferrer = '/example/README.md';
spyOnProperty(document, 'referrer').and.returnValue(fakeReferrer);
vm.$mount();
expect(vm.goBackUrl).toEqual(fakeReferrer);
});
it('renders the Go Back link with the project url when referrer is not present', () => {
const fakeReferrer = '';
spyOnProperty(document, 'referrer').and.returnValue(fakeReferrer);
vm.$mount();
expect(vm.goBackUrl).toEqual('testing');
});
});
describe('updateActivityBarView', () => {
beforeEach(() => {
spyOn(vm, 'updateActivityBarView');
Loading
Loading
import Vue from 'vue';
import ProjectAvatarDefault from '~/vue_shared/components/project_avatar/default.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { projectData } from 'spec/ide/mock_data';
import { getFirstCharacterCapitalized } from '~/lib/utils/text_utility';
import { TEST_HOST } from 'spec/test_constants';
describe('ProjectAvatarDefault component', () => {
const Component = Vue.extend(ProjectAvatarDefault);
let vm;
beforeEach(() => {
vm = mountComponent(Component, {
project: projectData,
});
});
afterEach(() => {
vm.$destroy();
});
it('renders identicon if project has no avatar_url', done => {
const expectedText = getFirstCharacterCapitalized(projectData.name);
vm.project = {
...vm.project,
avatar_url: null,
};
vm.$nextTick()
.then(() => {
const identiconEl = vm.$el.querySelector('.identicon');
expect(identiconEl).not.toBe(null);
expect(identiconEl.textContent.trim()).toEqual(expectedText);
})
.then(done)
.catch(done.fail);
});
it('renders avatar image if project has avatar_url', done => {
const avatarUrl = `${TEST_HOST}/images/home/nasa.svg`;
vm.project = {
...vm.project,
avatar_url: avatarUrl,
};
vm.$nextTick()
.then(() => {
expect(vm.$el).toContainElement('.avatar');
expect(vm.$el).not.toContainElement('.identicon');
expect(vm.$el.querySelector('img')).toHaveAttr('src', avatarUrl);
})
.then(done)
.catch(done.fail);
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment