Skip to content
Snippets Groups Projects
Unverified Commit 039117de authored by Phil Hughes's avatar Phil Hughes
Browse files

Render branch divergance graph with Vue

parent 88c8d177
No related branches found
No related tags found
No related merge requests found
Showing with 366 additions and 54 deletions
<script>
import { sprintf, __ } from '~/locale';
import GraphBar from './graph_bar.vue';
import { MAX_COMMIT_COUNT } from '../constants';
export default {
components: {
GraphBar,
},
props: {
defaultBranch: {
type: String,
required: true,
},
distance: {
type: Number,
required: false,
default: null,
},
aheadCount: {
type: Number,
required: true,
},
behindCount: {
type: Number,
required: true,
},
maxCommits: {
type: Number,
required: true,
},
},
computed: {
title() {
if (this.distance) {
return sprintf(
__('More than %{number_commits_distance} commits different with %{default_branch}'),
{
number_commits_distance:
this.distance >= MAX_COMMIT_COUNT ? `${MAX_COMMIT_COUNT - 1}+` : this.distance,
default_branch: this.defaultBranch,
},
);
}
return sprintf(
__(
'%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead',
),
{
number_commits_behind: this.behindCount,
number_commits_ahead: this.aheadCount,
default_branch: this.defaultBranch,
},
);
},
},
};
</script>
<template>
<div :title="title" class="divergence-graph px-2 d-none d-md-block">
<template v-if="distance">
<graph-bar :count="distance" :max-commits="maxCommits" position="full" />
</template>
<template v-else>
<graph-bar :count="behindCount" :max-commits="maxCommits" position="left" />
<div class="graph-separator pull-left mt-1"></div>
<graph-bar :count="aheadCount" :max-commits="maxCommits" position="right" />
</template>
</div>
</template>
<script>
import { SIDES, MAX_COMMIT_COUNT } from '../constants';
export default {
props: {
position: {
type: String,
required: true,
},
count: {
type: Number,
required: true,
},
maxCommits: {
type: Number,
required: true,
},
},
computed: {
label() {
if (this.count >= MAX_COMMIT_COUNT) {
return `${MAX_COMMIT_COUNT - 1}+`;
}
return this.count;
},
barGraphWidthFactor() {
return this.maxCommits > 0 ? 100 / this.maxCommits : 0;
},
style() {
return {
width: `${this.count * this.barGraphWidthFactor}%`,
};
},
isFullWidth() {
return this.position === SIDES.full;
},
isLeftSide() {
return this.position === SIDES.left;
},
roundedClass() {
if (this.isFullWidth) return 'rounded';
return `rounded-${this.position}`;
},
textAlignmentClass() {
if (this.isFullWidth) return 'text-center';
return `text-${this.isLeftSide ? SIDES.right : SIDES.left}`;
},
positionSideClass() {
return `position-${this.isLeftSide ? SIDES.right : SIDES.left}-0`;
},
},
};
</script>
<template>
<div :class="{ full: isFullWidth }" class="position-relative pull-left pt-1 graph-side h-100">
<div
:style="style"
:class="[roundedClass, positionSideClass]"
class="position-absolute bar js-graph-bar"
></div>
<span :class="textAlignmentClass" class="d-block pt-1 pr-1 count js-graph-count">
{{ label }}
</span>
</div>
</template>
export const SIDES = {
full: 'full',
left: 'left',
right: 'right',
};
export const MAX_COMMIT_COUNT = 1000;
import Vue from 'vue';
import DivergenceGraph from './components/divergence_graph.vue';
export default () => {
document.querySelectorAll('.js-branch-divergence-graph').forEach(el => {
const { distance, aheadCount, behindCount, defaultBranch, maxCommits } = el.dataset;
return new Vue({
el,
render(h) {
return h(DivergenceGraph, {
props: {
defaultBranch,
distance: distance ? parseInt(distance, 10) : null,
aheadCount: parseInt(aheadCount, 10),
behindCount: parseInt(behindCount, 10),
maxCommits: parseInt(maxCommits, 10),
},
});
},
});
});
};
import AjaxLoadingSpinner from '~/ajax_loading_spinner';
import DeleteModal from '~/branches/branches_delete_modal';
import initDiverganceGraph from '~/branches/divergence_graph';
 
document.addEventListener('DOMContentLoaded', () => {
AjaxLoadingSpinner.init();
new DeleteModal(); // eslint-disable-line no-new
initDiverganceGraph();
});
Loading
Loading
@@ -14,62 +14,26 @@
$graph-side-width: 80px;
$graph-separator-width: 1px;
 
padding: 0 6px;
.graph-side {
position: relative;
width: $graph-side-width;
height: 22px;
padding: 5px 0 13px;
float: left;
 
&.full {
width: $graph-side-width * 2 + $graph-separator-width;
display: flex;
justify-content: center;
}
 
.bar {
position: absolute;
height: 4px;
background-color: $gl-gray-200;
}
 
.bar-behind {
right: 0;
border-radius: 3px 0 0 3px;
}
.bar-ahead {
left: 0;
border-radius: 0 3px 3px 0;
}
.count {
padding-top: 6px;
padding-bottom: 0;
font-size: 12px;
color: $gl-text-color;
display: block;
}
.count-behind {
padding-right: 4px;
text-align: right;
}
.count-ahead {
padding-left: 4px;
text-align: left;
}
}
 
.graph-separator {
position: relative;
width: $graph-separator-width;
height: 18px;
margin: 5px 0 0;
float: left;
background-color: $gl-gray-200;
}
}
Loading
Loading
- merged = local_assigns.fetch(:merged, false)
- commit = @repository.commit(branch.dereferenced_target)
- bar_graph_width_factor = @max_commits > 0 ? 100.0/@max_commits : 0
- diverging_commit_counts = @repository.diverging_commit_counts(branch)
- number_commits_distance = diverging_commit_counts[:distance]
- number_commits_behind = diverging_commit_counts[:behind]
Loading
Loading
@@ -31,23 +30,7 @@
= s_('Branches|Cant find HEAD commit for this branch')
 
- if branch.name != @repository.root_ref
- if number_commits_distance.nil?
.divergence-graph.d-none.d-md-block{ title: s_('%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead') % { number_commits_behind: diverging_count_label(number_commits_behind),
default_branch: @repository.root_ref,
number_commits_ahead: diverging_count_label(number_commits_ahead) } }
.graph-side
.bar.bar-behind{ style: "width: #{number_commits_behind * bar_graph_width_factor}%" }
%span.count.count-behind= diverging_count_label(number_commits_behind)
.graph-separator
.graph-side
.bar.bar-ahead{ style: "width: #{number_commits_ahead * bar_graph_width_factor}%" }
%span.count.count-ahead= diverging_count_label(number_commits_ahead)
- else
.divergence-graph.d-none.d-md-block{ title: s_('More than %{number_commits_distance} commits different with %{default_branch}') % { number_commits_distance: diverging_count_label(number_commits_distance),
default_branch: @repository.root_ref} }
.graph-side.full
.bar{ style: "width: #{number_commits_distance * bar_graph_width_factor}%" }
%span.count= diverging_count_label(number_commits_distance)
.js-branch-divergence-graph{ data: { distance: number_commits_distance, ahead_count: number_commits_ahead, behind_count: number_commits_behind, default_branch: @repository.root_ref, max_commits: @max_commits } }
 
.controls.d-none.d-md-block<
- if merge_project && create_mr_button?(@repository.root_ref, branch.name)
Loading
Loading
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Branch divergence graph component renders ahead and behind count 1`] = `
<div
class="divergence-graph px-2 d-none d-md-block"
title="10 commits behind master, 10 commits ahead"
>
<graphbar-stub
count="10"
maxcommits="100"
position="left"
/>
<div
class="graph-separator pull-left mt-1"
/>
<graphbar-stub
count="10"
maxcommits="100"
position="right"
/>
</div>
`;
exports[`Branch divergence graph component renders distance count 1`] = `
<div
class="divergence-graph px-2 d-none d-md-block"
title="More than 900 commits different with master"
>
<graphbar-stub
count="900"
maxcommits="100"
position="full"
/>
</div>
`;
import { shallowMount } from '@vue/test-utils';
import DivergenceGraph from '~/branches/components/divergence_graph.vue';
import GraphBar from '~/branches/components/graph_bar.vue';
let vm;
function factory(propsData = {}) {
vm = shallowMount(DivergenceGraph, { propsData });
}
describe('Branch divergence graph component', () => {
afterEach(() => {
vm.destroy();
});
it('renders ahead and behind count', () => {
factory({
defaultBranch: 'master',
aheadCount: 10,
behindCount: 10,
maxCommits: 100,
});
expect(vm.findAll(GraphBar).length).toBe(2);
expect(vm.element).toMatchSnapshot();
});
it('sets title for ahead and behind count', () => {
factory({
defaultBranch: 'master',
aheadCount: 10,
behindCount: 10,
maxCommits: 100,
});
expect(vm.attributes('title')).toBe('10 commits behind master, 10 commits ahead');
});
it('renders distance count', () => {
factory({
defaultBranch: 'master',
aheadCount: 0,
behindCount: 0,
distance: 900,
maxCommits: 100,
});
expect(vm.findAll(GraphBar).length).toBe(1);
expect(vm.element).toMatchSnapshot();
});
it.each`
distance | titleText
${900} | ${'900'}
${1100} | ${'999+'}
`('sets title for $distance as $titleText', ({ distance, titleText }) => {
factory({
defaultBranch: 'master',
aheadCount: 0,
behindCount: 0,
distance,
maxCommits: 100,
});
expect(vm.attributes('title')).toBe(`More than ${titleText} commits different with master`);
});
});
import { shallowMount } from '@vue/test-utils';
import GraphBar from '~/branches/components/graph_bar.vue';
let vm;
function factory(propsData = {}) {
vm = shallowMount(GraphBar, { propsData });
}
describe('Branch divergence graph bar component', () => {
afterEach(() => {
vm.destroy();
});
it.each`
position | positionClass
${'left'} | ${'position-right-0'}
${'right'} | ${'position-left-0'}
${'full'} | ${'position-left-0'}
`(
'sets position class as $positionClass for position $position',
({ position, positionClass }) => {
factory({
position,
count: 10,
maxCommits: 100,
});
expect(vm.find('.js-graph-bar').classes()).toContain(positionClass);
},
);
it.each`
position | textAlignmentClass
${'left'} | ${'text-right'}
${'right'} | ${'text-left'}
${'full'} | ${'text-center'}
`(
'sets text alignment class as $textAlignmentClass for position $position',
({ position, textAlignmentClass }) => {
factory({
position,
count: 10,
maxCommits: 100,
});
expect(vm.find('.js-graph-count').classes()).toContain(textAlignmentClass);
},
);
it.each`
position | roundedClass
${'left'} | ${'rounded-left'}
${'right'} | ${'rounded-right'}
${'full'} | ${'rounded'}
`('sets rounded class as $roundedClass for position $position', ({ position, roundedClass }) => {
factory({
position,
count: 10,
maxCommits: 100,
});
expect(vm.find('.js-graph-bar').classes()).toContain(roundedClass);
});
it.each`
count | label
${100} | ${'100'}
${1000} | ${'999+'}
`('renders label as $roundedClass for $count', ({ count, label }) => {
factory({
position: 'left',
count,
maxCommits: 1000,
});
expect(vm.find('.js-graph-count').text()).toContain(label);
});
it('sets width of bar', () => {
factory({
position: 'left',
count: 100,
maxCommits: 1000,
});
expect(vm.find('.js-graph-bar').attributes('style')).toEqual('width: 10%;');
});
});
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