Resolve "Clean up bottom status bar Web IDE"
This commit is contained in:
parent
68b71df67f
commit
b7f3d7470b
|
@ -65,61 +65,63 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="ide-view"
|
||||
>
|
||||
<find-file
|
||||
v-show="fileFindVisible"
|
||||
/>
|
||||
<ide-sidebar />
|
||||
<article class="ide">
|
||||
<div
|
||||
class="multi-file-edit-pane"
|
||||
class="ide-view"
|
||||
>
|
||||
<template
|
||||
v-if="activeFile"
|
||||
<find-file
|
||||
v-show="fileFindVisible"
|
||||
/>
|
||||
<ide-sidebar />
|
||||
<div
|
||||
class="multi-file-edit-pane"
|
||||
>
|
||||
<repo-tabs
|
||||
:active-file="activeFile"
|
||||
:files="openFiles"
|
||||
:viewer="viewer"
|
||||
:has-changes="hasChanges"
|
||||
:merge-request-id="currentMergeRequestId"
|
||||
/>
|
||||
<repo-editor
|
||||
class="multi-file-edit-pane-content"
|
||||
:file="activeFile"
|
||||
/>
|
||||
<ide-status-bar
|
||||
:file="activeFile"
|
||||
/>
|
||||
</template>
|
||||
<template
|
||||
v-else
|
||||
>
|
||||
<div
|
||||
v-once
|
||||
class="ide-empty-state"
|
||||
<template
|
||||
v-if="activeFile"
|
||||
>
|
||||
<div class="row js-empty-state">
|
||||
<div class="col-xs-12">
|
||||
<div class="svg-content svg-250">
|
||||
<img :src="emptyStateSvgPath" />
|
||||
<repo-tabs
|
||||
:active-file="activeFile"
|
||||
:files="openFiles"
|
||||
:viewer="viewer"
|
||||
:has-changes="hasChanges"
|
||||
:merge-request-id="currentMergeRequestId"
|
||||
/>
|
||||
<repo-editor
|
||||
class="multi-file-edit-pane-content"
|
||||
:file="activeFile"
|
||||
/>
|
||||
</template>
|
||||
<template
|
||||
v-else
|
||||
>
|
||||
<div
|
||||
v-once
|
||||
class="ide-empty-state"
|
||||
>
|
||||
<div class="row js-empty-state">
|
||||
<div class="col-xs-12">
|
||||
<div class="svg-content svg-250">
|
||||
<img :src="emptyStateSvgPath" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12">
|
||||
<div class="text-content text-center">
|
||||
<h4>
|
||||
Welcome to the GitLab IDE
|
||||
</h4>
|
||||
<p>
|
||||
You can select a file in the left sidebar to begin
|
||||
editing and use the right sidebar to commit your changes.
|
||||
</p>
|
||||
<div class="col-xs-12">
|
||||
<div class="text-content text-center">
|
||||
<h4>
|
||||
Welcome to the GitLab IDE
|
||||
</h4>
|
||||
<p>
|
||||
You can select a file in the left sidebar to begin
|
||||
editing and use the right sidebar to commit your changes.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ide-status-bar
|
||||
:file="activeFile"
|
||||
/>
|
||||
</article>
|
||||
</template>
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import icon from '~/vue_shared/components/icon.vue';
|
||||
import tooltip from '~/vue_shared/directives/tooltip';
|
||||
import timeAgoMixin from '~/vue_shared/mixins/timeago';
|
||||
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
icon,
|
||||
userAvatarImage,
|
||||
},
|
||||
directives: {
|
||||
tooltip,
|
||||
|
@ -14,40 +17,93 @@ export default {
|
|||
props: {
|
||||
file: {
|
||||
type: Object,
|
||||
required: true,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
lastCommitFormatedAge: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['currentProject', 'lastCommit']),
|
||||
},
|
||||
mounted() {
|
||||
this.startTimer();
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.intervalId) {
|
||||
clearInterval(this.intervalId);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
startTimer() {
|
||||
this.intervalId = setInterval(() => {
|
||||
this.commitAgeUpdate();
|
||||
}, 1000);
|
||||
},
|
||||
commitAgeUpdate() {
|
||||
if (this.lastCommit) {
|
||||
this.lastCommitFormatedAge = this.timeFormated(this.lastCommit.committed_date);
|
||||
}
|
||||
},
|
||||
getCommitPath(shortSha) {
|
||||
return `${this.currentProject.web_url}/commit/${shortSha}`;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ide-status-bar">
|
||||
<div>
|
||||
<div v-if="file.lastCommit && file.lastCommit.id">
|
||||
Last commit:
|
||||
<a
|
||||
v-tooltip
|
||||
:title="file.lastCommit.message"
|
||||
:href="file.lastCommit.url"
|
||||
>
|
||||
{{ timeFormated(file.lastCommit.updatedAt) }} by
|
||||
{{ file.lastCommit.author }}
|
||||
</a>
|
||||
</div>
|
||||
<footer class="ide-status-bar">
|
||||
<div
|
||||
class="ide-status-branch"
|
||||
v-if="lastCommit && lastCommitFormatedAge"
|
||||
>
|
||||
<icon
|
||||
name="commit"
|
||||
/>
|
||||
<a
|
||||
v-tooltip
|
||||
class="commit-sha"
|
||||
:title="lastCommit.message"
|
||||
:href="getCommitPath(lastCommit.short_id)"
|
||||
>{{ lastCommit.short_id }}</a>
|
||||
by
|
||||
{{ lastCommit.author_name }}
|
||||
<time
|
||||
v-tooltip
|
||||
data-placement="top"
|
||||
data-container="body"
|
||||
:datetime="lastCommit.committed_date"
|
||||
:title="tooltipTitle(lastCommit.committed_date)"
|
||||
>
|
||||
{{ lastCommitFormatedAge }}
|
||||
</time>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div
|
||||
v-if="file"
|
||||
class="ide-status-file"
|
||||
>
|
||||
{{ file.name }}
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div
|
||||
v-if="file"
|
||||
class="ide-status-file"
|
||||
>
|
||||
{{ file.eol }}
|
||||
</div>
|
||||
<div
|
||||
class="text-right"
|
||||
v-if="!file.binary">
|
||||
class="ide-status-file"
|
||||
v-if="file && !file.binary">
|
||||
{{ file.editorRow }}:{{ file.editorColumn }}
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div
|
||||
v-if="file"
|
||||
class="ide-status-file"
|
||||
>
|
||||
{{ file.fileLanguage }}
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
|
|
|
@ -72,3 +72,26 @@ export const getBranchData = (
|
|||
resolve(state.projects[`${projectId}`].branches[branchId]);
|
||||
}
|
||||
});
|
||||
|
||||
export const refreshLastCommitData = (
|
||||
{ commit, state, dispatch },
|
||||
{ projectId, branchId } = {},
|
||||
) => service
|
||||
.getBranchData(projectId, branchId)
|
||||
.then(({ data }) => {
|
||||
commit(types.SET_BRANCH_COMMIT, {
|
||||
projectId,
|
||||
branchId,
|
||||
commit: data.commit,
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
flash(
|
||||
'Error loading last commit.',
|
||||
'alert',
|
||||
document,
|
||||
null,
|
||||
false,
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
|
|
@ -81,5 +81,11 @@ export const getUnstagedFilesCountForPath = state => path =>
|
|||
export const getStagedFilesCountForPath = state => path =>
|
||||
getChangesCountForFiles(state.stagedFiles, path);
|
||||
|
||||
export const lastCommit = (state, getters) => {
|
||||
const branch = getters.currentProject && getters.currentProject.branches[state.currentBranchId];
|
||||
|
||||
return branch ? branch.commit : null;
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
@ -210,7 +210,11 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo
|
|||
);
|
||||
}
|
||||
})
|
||||
.then(() => dispatch('updateCommitAction', consts.COMMIT_TO_CURRENT_BRANCH));
|
||||
.then(() => dispatch('updateCommitAction', consts.COMMIT_TO_CURRENT_BRANCH))
|
||||
.then(() => dispatch('refreshLastCommitData', {
|
||||
projectId: rootState.currentProjectId,
|
||||
branchId: rootState.currentBranchId,
|
||||
}, { root: true }));
|
||||
})
|
||||
.catch(err => {
|
||||
let errMsg = __('Error committing changes. Please try again.');
|
||||
|
|
|
@ -20,6 +20,7 @@ export const SET_MERGE_REQUEST_VERSIONS = 'SET_MERGE_REQUEST_VERSIONS';
|
|||
|
||||
// Branch Mutation Types
|
||||
export const SET_BRANCH = 'SET_BRANCH';
|
||||
export const SET_BRANCH_COMMIT = 'SET_BRANCH_COMMIT';
|
||||
export const SET_BRANCH_WORKING_REFERENCE = 'SET_BRANCH_WORKING_REFERENCE';
|
||||
export const TOGGLE_BRANCH_OPEN = 'TOGGLE_BRANCH_OPEN';
|
||||
|
||||
|
|
|
@ -23,4 +23,9 @@ export default {
|
|||
workingReference: reference,
|
||||
});
|
||||
},
|
||||
[types.SET_BRANCH_COMMIT](state, { projectId, branchId, commit }) {
|
||||
Object.assign(state.projects[projectId].branches[branchId], {
|
||||
commit,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
@ -230,6 +230,7 @@ $row-hover: $blue-50;
|
|||
$row-hover-border: $blue-200;
|
||||
$progress-color: #c0392b;
|
||||
$header-height: 40px;
|
||||
$ide-statusbar-height: 27px;
|
||||
$fixed-layout-width: 1280px;
|
||||
$limited-layout-width: 990px;
|
||||
$limited-layout-width-sm: 790px;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
margin-top: 0;
|
||||
border-top: 1px solid $white-dark;
|
||||
border-bottom: 1px solid $white-dark;
|
||||
padding-bottom: $ide-statusbar-height;
|
||||
|
||||
&.is-collapsed {
|
||||
.ide-file-list {
|
||||
|
@ -375,7 +376,13 @@
|
|||
padding: $gl-bar-padding $gl-padding;
|
||||
background: $white-light;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
justify-content: space-between;
|
||||
height: $ide-statusbar-height;
|
||||
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
|
||||
> div + div {
|
||||
padding-left: $gl-padding;
|
||||
|
@ -386,6 +393,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
.ide-status-file {
|
||||
text-align: right;
|
||||
|
||||
.ide-status-branch + &,
|
||||
&:first-child {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
// Not great, but this is to deal with our current output
|
||||
.multi-file-preview-holder {
|
||||
height: 100%;
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Clean up WebIDE status bar and add useful info
|
||||
merge_request:
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,63 @@
|
|||
import Vue from 'vue';
|
||||
import store from '~/ide/stores';
|
||||
import ideStatusBar from '~/ide/components/ide_status_bar.vue';
|
||||
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
|
||||
import { resetStore } from '../helpers';
|
||||
import { projectData } from '../mock_data';
|
||||
|
||||
describe('ideStatusBar', () => {
|
||||
let vm;
|
||||
|
||||
beforeEach(() => {
|
||||
const Component = Vue.extend(ideStatusBar);
|
||||
|
||||
store.state.currentProjectId = 'abcproject';
|
||||
store.state.projects.abcproject = projectData;
|
||||
|
||||
vm = createComponentWithStore(Component, store).$mount();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
|
||||
resetStore(vm.$store);
|
||||
});
|
||||
|
||||
it('renders the statusbar', () => {
|
||||
expect(vm.$el.className).toBe('ide-status-bar');
|
||||
});
|
||||
|
||||
describe('mounted', () => {
|
||||
it('triggers a setInterval', () => {
|
||||
expect(vm.intervalId).not.toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('commitAgeUpdate', () => {
|
||||
beforeEach(function() {
|
||||
jasmine.clock().install();
|
||||
spyOn(vm, 'commitAgeUpdate').and.callFake(() => {});
|
||||
vm.startTimer();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
jasmine.clock().uninstall();
|
||||
});
|
||||
|
||||
it('gets called every second', () => {
|
||||
expect(vm.commitAgeUpdate).not.toHaveBeenCalled();
|
||||
|
||||
jasmine.clock().tick(1100);
|
||||
expect(vm.commitAgeUpdate.calls.count()).toEqual(1);
|
||||
|
||||
jasmine.clock().tick(1000);
|
||||
expect(vm.commitAgeUpdate.calls.count()).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCommitPath', () => {
|
||||
it('returns the path to the commit details', () => {
|
||||
expect(vm.getCommitPath('abc123de')).toBe('/commit/abc123de');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,71 @@
|
|||
import {
|
||||
refreshLastCommitData,
|
||||
} from '~/ide/stores/actions';
|
||||
import store from '~/ide/stores';
|
||||
import service from '~/ide/services';
|
||||
import { resetStore } from '../../helpers';
|
||||
import testAction from '../../../helpers/vuex_action_helper';
|
||||
|
||||
describe('IDE store project actions', () => {
|
||||
beforeEach(() => {
|
||||
store.state.projects.abcproject = {};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetStore(store);
|
||||
});
|
||||
|
||||
describe('refreshLastCommitData', () => {
|
||||
beforeEach(() => {
|
||||
store.state.currentProjectId = 'abcproject';
|
||||
store.state.currentBranchId = 'master';
|
||||
store.state.projects.abcproject = {
|
||||
branches: {
|
||||
master: {
|
||||
commit: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
it('calls the service', done => {
|
||||
spyOn(service, 'getBranchData').and.returnValue(
|
||||
Promise.resolve({
|
||||
data: {
|
||||
commit: { id: '123' },
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
store
|
||||
.dispatch('refreshLastCommitData', {
|
||||
projectId: store.state.currentProjectId,
|
||||
branchId: store.state.currentBranchId,
|
||||
})
|
||||
.then(() => {
|
||||
expect(service.getBranchData).toHaveBeenCalledWith('abcproject', 'master');
|
||||
|
||||
done();
|
||||
})
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('commits getBranchData', done => {
|
||||
testAction(
|
||||
refreshLastCommitData,
|
||||
{},
|
||||
{},
|
||||
[{
|
||||
type: 'SET_BRANCH_COMMIT',
|
||||
payload: {
|
||||
projectId: 'abcproject',
|
||||
branchId: 'master',
|
||||
commit: { id: '123' },
|
||||
},
|
||||
}], // mutations
|
||||
[], // action
|
||||
done,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -141,4 +141,24 @@ describe('IDE store getters', () => {
|
|||
expect(getters.getChangesInFolder(localState)('test')).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('lastCommit', () => {
|
||||
it('returns the last commit of the current branch on the current project', () => {
|
||||
const commitTitle = 'Example commit title';
|
||||
const localGetters = {
|
||||
currentProject: {
|
||||
branches: {
|
||||
'example-branch': {
|
||||
commit: {
|
||||
title: commitTitle,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
localState.currentBranchId = 'example-branch';
|
||||
|
||||
expect(getters.lastCommit(localState, localGetters).title).toBe(commitTitle);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -15,4 +15,26 @@ describe('Multi-file store branch mutations', () => {
|
|||
expect(localState.currentBranchId).toBe('master');
|
||||
});
|
||||
});
|
||||
|
||||
describe('SET_BRANCH_COMMIT', () => {
|
||||
it('sets the last commit on current project', () => {
|
||||
localState.projects = {
|
||||
Example: {
|
||||
branches: {
|
||||
master: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mutations.SET_BRANCH_COMMIT(localState, {
|
||||
projectId: 'Example',
|
||||
branchId: 'master',
|
||||
commit: {
|
||||
title: 'Example commit',
|
||||
},
|
||||
});
|
||||
|
||||
expect(localState.projects.Example.branches.master.commit.title).toBe('Example commit');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue