improvements to the design

This commit is contained in:
Phil Hughes 2018-04-17 16:27:46 +01:00
parent 8de5cea076
commit 201afb0f85
No known key found for this signature in database
GPG Key ID: 32245528C52E0F9F
14 changed files with 116 additions and 206 deletions

View File

@ -1,47 +0,0 @@
<script>
import icon from '~/vue_shared/components/icon.vue';
import repoTree from './ide_repo_tree.vue';
import newDropdown from './new_dropdown/index.vue';
export default {
components: {
repoTree,
icon,
newDropdown,
},
props: {
projectId: {
type: String,
required: true,
},
branch: {
type: Object,
required: true,
},
},
};
</script>
<template>
<div class="branch-container">
<div class="branch-header">
<div class="branch-header-title str-truncated ref-name">
<icon
name="branch"
:size="12"
/>
{{ branch.name }}
</div>
<div class="branch-header-btns">
<new-dropdown
:project-id="projectId"
:branch="branch.name"
path=""
/>
</div>
</div>
<repo-tree
:tree="branch.tree"
/>
</div>
</template>

View File

@ -1,24 +0,0 @@
<script>
import { mapGetters } from 'vuex';
import BranchesTree from './ide_project_branches_tree.vue';
export default {
components: {
BranchesTree,
},
computed: {
...mapGetters(['currentProjectWithTree']),
},
};
</script>
<template>
<div class="projects-sidebar">
<branches-tree
v-for="branch in currentProjectWithTree.branches"
:key="branch.name"
:project-id="currentProjectWithTree.path_with_namespace"
:branch="branch"
/>
</div>
</template>

View File

@ -5,14 +5,13 @@ import icon from '~/vue_shared/components/icon.vue';
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 projectTree from './ide_project_tree.vue';
import IdeTree from './ide_tree.vue';
import ResizablePanel from './resizable_panel.vue';
import ActivityBar from './activity_bar.vue';
import CommitSection from './repo_commit_section.vue';
export default {
components: {
projectTree,
icon,
panelResizer,
skeletonLoadingContainer,
@ -21,10 +20,11 @@ export default {
ProjectAvatarImage,
Identicon,
CommitSection,
IdeTree,
},
computed: {
...mapState(['loading']),
...mapGetters(['currentProjectWithTree', 'activityBarComponent']),
...mapState(['loading', 'currentBranchId']),
...mapGetters(['currentProject', 'activityBarComponent']),
},
};
</script>
@ -51,29 +51,39 @@ export default {
<template v-else>
<div class="context-header">
<a
:title="currentProjectWithTree.name"
:href="currentProjectWithTree.web_url"
:href="currentProject.web_url"
>
<div
v-if="currentProjectWithTree.avatar_url"
v-if="currentProject.avatar_url"
class="avatar-container s40 project-avatar"
>
<project-avatar-image
class="avatar-container project-avatar"
:link-href="currentProjectWithTree.path"
:img-src="currentProjectWithTree.avatar_url"
:img-alt="currentProjectWithTree.name"
:link-href="currentProject.path"
:img-src="currentProject.avatar_url"
:img-alt="currentProject.name"
:img-size="40"
/>
</div>
<identicon
v-else
size-class="s40"
:entity-id="currentProjectWithTree.id"
:entity-name="currentProjectWithTree.name"
:entity-id="currentProject.id"
:entity-name="currentProject.name"
/>
<div class="sidebar-context-title">
{{ currentProjectWithTree.name }}
<div class="ide-sidebar-project-title">
<div class="sidebar-context-title">
{{ currentProject.name }}
</div>
<div
v-if="currentBranchId !== ''"
class="sidebar-context-title ide-sidebar-branch-title"
>
<icon
name="branch"
css-classes="append-right-5"
/>{{ currentBranchId }}
</div>
</div>
</a>
</div>
@ -86,3 +96,15 @@ export default {
</div>
</resizable-panel>
</template>
<style>
.ide-sidebar-branch-title {
font-weight: normal;
}
.ide-sidebar-branch-title svg {
position: relative;
top: 3px;
margin-top: -1px;
}
</style>

View File

@ -1,17 +1,20 @@
<script>
import { mapGetters, mapState } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue';
import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
import RepoFile from './repo_file.vue';
import NewDropdown from './new_dropdown/index.vue';
export default {
components: {
Icon,
RepoFile,
SkeletonLoadingContainer,
NewDropdown,
},
props: {
tree: {
type: Object,
required: true,
},
computed: {
...mapState(['currentBranchId']),
...mapGetters(['currentProject', 'currentTree']),
},
};
</script>
@ -20,7 +23,7 @@ export default {
<div
class="ide-file-list"
>
<template v-if="tree.loading">
<template v-if="!currentTree || currentTree.loading">
<div
class="multi-file-loading-container"
v-for="n in 3"
@ -30,8 +33,16 @@ export default {
</div>
</template>
<template v-else>
<header class="ide-tree-header">
{{ __('Edit') }}
<new-dropdown
:project-id="currentProject.name_with_namespace"
:branch="currentBranchId"
path=""
/>
</header>
<repo-file
v-for="file in tree.tree"
v-for="file in currentTree.tree"
:key="file.key"
:file="file"
:level="0"

View File

@ -63,6 +63,8 @@ router.beforeEach((to, from, next) => {
const fullProjectId = `${to.params.namespace}/${to.params.project}`;
if (to.params.branch) {
store.commit('SET_CURRENT_BRANCH', to.params.branch);
store.dispatch('getBranchData', {
projectId: fullProjectId,
branchId: to.params.branch,

View File

@ -23,18 +23,6 @@ export const projectsWithTrees = state =>
};
});
export const currentProjectWithTree = state => ({
...state.projects[state.currentProjectId],
branches: Object.keys(state.projects[state.currentProjectId].branches).map(branchId => {
const branch = state.projects[state.currentProjectId].branches[branchId];
return {
...branch,
tree: state.trees[branch.treeId],
};
}),
});
export const currentMergeRequest = state => {
if (state.projects[state.currentProjectId]) {
return state.projects[state.currentProjectId].mergeRequests[state.currentMergeRequestId];
@ -44,6 +32,9 @@ export const currentMergeRequest = state => {
export const currentProject = state => state.projects[state.currentProjectId];
export const currentTree = state =>
state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
// eslint-disable-next-line no-confusing-arrow
export const currentIcon = state =>
state.rightPanelCollapsed ? 'angle-double-left' : 'angle-double-right';
@ -55,7 +46,7 @@ export const hasMergeRequest = state => !!state.currentMergeRequestId;
export const activityBarComponent = state => {
switch (state.currentActivityView) {
case ActivityBarViews.edit:
return 'project-tree';
return 'ide-tree';
case ActivityBarViews.commit:
return 'commit-section';
default:

View File

@ -446,25 +446,6 @@
border-top: 1px solid $white-dark;
border-top-left-radius: $border-radius-small;
}
.branch-header {
background: $white-dark;
display: flex;
}
.branch-header-title {
flex: 1;
padding: $grid-size $gl-padding;
font-weight: $gl-font-weight-bold;
svg {
vertical-align: middle;
}
}
.branch-header-btns {
padding: $gl-vert-padding $gl-padding;
}
}
.multi-file-context-bar-icon {
@ -901,3 +882,16 @@
background: transparent;
resize: none;
}
.ide-tree-header {
display: flex;
align-items: center;
padding: 10px 0;
margin-left: 10px;
margin-right: 10px;
border-bottom: 1px solid $white-dark;
.ide-new-btn {
margin-left: auto;
}
}

View File

@ -44,6 +44,8 @@ feature 'Multi-file editor new directory', :js do
wait_for_requests
find('.js-ide-commit-mode').click
fill_in('commit-message', with: 'commit message ide')
click_button('Commit')

View File

@ -34,6 +34,8 @@ feature 'Multi-file editor new file', :js do
wait_for_requests
find('.js-ide-commit-mode').click
fill_in('commit-message', with: 'commit message ide')
click_button('Commit')

View File

@ -1,39 +0,0 @@
import Vue from 'vue';
import ProjectTree from '~/ide/components/ide_project_tree.vue';
import createComponent from 'spec/helpers/vue_mount_component_helper';
describe('IDE project tree', () => {
const Component = Vue.extend(ProjectTree);
let vm;
beforeEach(() => {
vm = createComponent(Component, {
project: {
id: 1,
name: 'test',
web_url: gl.TEST_HOST,
avatar_url: '',
branches: [],
},
});
});
afterEach(() => {
vm.$destroy();
});
it('renders identicon when projct has no avatar', () => {
expect(vm.$el.querySelector('.identicon')).not.toBeNull();
});
it('renders avatar image if project has avatar', done => {
vm.project.avatar_url = gl.TEST_HOST;
vm.$nextTick(() => {
expect(vm.$el.querySelector('.identicon')).toBeNull();
expect(vm.$el.querySelector('img.avatar')).not.toBeNull();
done();
});
});
});

View File

@ -3,6 +3,7 @@ import store from '~/ide/stores';
import ideSidebar from '~/ide/components/ide_side_bar.vue';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { resetStore } from '../helpers';
import { projectData } from '../mock_data';
describe('IdeSidebar', () => {
let vm;
@ -10,6 +11,10 @@ describe('IdeSidebar', () => {
beforeEach(() => {
const Component = Vue.extend(ideSidebar);
store.state.currentProjectId = 'abcproject';
store.state.projects.abcproject = projectData;
store.state.currentActivityView = null;
vm = createComponentWithStore(Component, store).$mount();
});
@ -20,21 +25,15 @@ describe('IdeSidebar', () => {
});
it('renders a sidebar', () => {
expect(
vm.$el.querySelector('.multi-file-commit-panel-inner'),
).not.toBeNull();
expect(vm.$el.querySelector('.multi-file-commit-panel-inner')).not.toBeNull();
});
it('renders loading icon component', done => {
vm.$store.state.loading = true;
vm.$nextTick(() => {
expect(
vm.$el.querySelector('.multi-file-loading-container'),
).not.toBeNull();
expect(
vm.$el.querySelectorAll('.multi-file-loading-container').length,
).toBe(3);
expect(vm.$el.querySelector('.multi-file-loading-container')).not.toBeNull();
expect(vm.$el.querySelectorAll('.multi-file-loading-container').length).toBe(3);
done();
});

View File

@ -1,5 +1,5 @@
import Vue from 'vue';
import ideRepoTree from '~/ide/components/ide_repo_tree.vue';
import IdeTree from '~/ide/components/ide_tree.vue';
import createComponent from '../../helpers/vue_mount_component_helper';
import { file } from '../helpers';
@ -8,7 +8,7 @@ describe('IdeRepoTree', () => {
let tree;
beforeEach(() => {
const IdeRepoTree = Vue.extend(ideRepoTree);
const IdeRepoTree = Vue.extend(IdeTree);
tree = {
tree: [file()],
@ -33,9 +33,7 @@ describe('IdeRepoTree', () => {
tree.loading = true;
vm.$nextTick(() => {
expect(
vm.$el.querySelectorAll('.multi-file-loading-container').length,
).toEqual(3);
expect(vm.$el.querySelectorAll('.multi-file-loading-container').length).toEqual(3);
done();
});

View File

@ -12,10 +12,10 @@ describe('RepoCommitSection', () => {
function createComponent() {
const Component = Vue.extend(repoCommitSection);
vm = createComponentWithStore(Component, store, {
noChangesStateSvgPath: 'svg',
committedStateSvgPath: 'commitsvg',
});
store.state.noChangesStateSvgPath = 'svg';
store.state.committedStateSvgPath = 'commitsvg';
vm = createComponentWithStore(Component, store);
vm.$store.state.currentProjectId = 'abcproject';
vm.$store.state.currentBranchId = 'master';
@ -75,33 +75,25 @@ describe('RepoCommitSection', () => {
resetStore(vm.$store);
const Component = Vue.extend(repoCommitSection);
vm = createComponentWithStore(Component, store, {
noChangesStateSvgPath: 'nochangessvg',
committedStateSvgPath: 'svg',
}).$mount();
store.state.noChangesStateSvgPath = 'nochangessvg';
store.state.committedStateSvgPath = 'svg';
expect(
vm.$el.querySelector('.js-empty-state').textContent.trim(),
).toContain('No changes');
expect(
vm.$el.querySelector('.js-empty-state img').getAttribute('src'),
).toBe('nochangessvg');
vm = createComponentWithStore(Component, store).$mount();
expect(vm.$el.querySelector('.js-empty-state').textContent.trim()).toContain('No changes');
expect(vm.$el.querySelector('.js-empty-state img').getAttribute('src')).toBe('nochangessvg');
});
});
it('renders a commit section', () => {
const changedFileElements = [
...vm.$el.querySelectorAll('.multi-file-commit-list li'),
];
const changedFileElements = [...vm.$el.querySelectorAll('.multi-file-commit-list li')];
const submitCommit = vm.$el.querySelector('form .btn');
expect(vm.$el.querySelector('.multi-file-commit-form')).not.toBeNull();
expect(changedFileElements.length).toEqual(2);
changedFileElements.forEach((changedFile, i) => {
expect(changedFile.textContent.trim()).toContain(
vm.$store.state.changedFiles[i].path,
);
expect(changedFile.textContent.trim()).toContain(vm.$store.state.changedFiles[i].path);
});
expect(submitCommit.disabled).toBeTruthy();
@ -117,9 +109,7 @@ describe('RepoCommitSection', () => {
getSetTimeoutPromise()
.then(() => {
expect(vm.$store.state.commit.commitMessage).toBe(
'testing commit message',
);
expect(vm.$store.state.commit.commitMessage).toBe('testing commit message');
})
.then(done)
.catch(done.fail);
@ -127,9 +117,7 @@ describe('RepoCommitSection', () => {
describe('discard draft button', () => {
it('hidden when commitMessage is empty', () => {
expect(
vm.$el.querySelector('.multi-file-commit-form .btn-default'),
).toBeNull();
expect(vm.$el.querySelector('.multi-file-commit-form .btn-default')).toBeNull();
});
it('resets commitMessage when clicking discard button', done => {
@ -141,9 +129,7 @@ describe('RepoCommitSection', () => {
})
.then(Vue.nextTick)
.then(() => {
expect(vm.$store.state.commit.commitMessage).not.toBe(
'testing commit message',
);
expect(vm.$store.state.commit.commitMessage).not.toBe('testing commit message');
})
.then(done)
.catch(done.fail);

View File

@ -0,0 +1,13 @@
// eslint-disable-next-line import/prefer-default-export
export const projectData = {
id: 1,
name: 'abcproject',
web_url: '',
avatar_url: '',
path: '',
branches: {
master: {
treeId: 'abcproject/master',
},
},
};