Commit f53b05cf authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'divergence-graph-fe-rendering' into 'master'

Render branch divergence graph with Vue

See merge request gitlab-org/gitlab-ce!29743
parents 108c3cf1 039117de
<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 AjaxLoadingSpinner from '~/ajax_loading_spinner';
import DeleteModal from '~/branches/branches_delete_modal'; import DeleteModal from '~/branches/branches_delete_modal';
import initDiverganceGraph from '~/branches/divergence_graph';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
AjaxLoadingSpinner.init(); AjaxLoadingSpinner.init();
new DeleteModal(); // eslint-disable-line no-new new DeleteModal(); // eslint-disable-line no-new
initDiverganceGraph();
}); });
...@@ -14,62 +14,26 @@ ...@@ -14,62 +14,26 @@
$graph-side-width: 80px; $graph-side-width: 80px;
$graph-separator-width: 1px; $graph-separator-width: 1px;
padding: 0 6px;
.graph-side { .graph-side {
position: relative;
width: $graph-side-width; width: $graph-side-width;
height: 22px;
padding: 5px 0 13px;
float: left;
&.full { &.full {
width: $graph-side-width * 2 + $graph-separator-width; width: $graph-side-width * 2 + $graph-separator-width;
display: flex;
justify-content: center;
} }
.bar { .bar {
position: absolute;
height: 4px; height: 4px;
background-color: $gl-gray-200; 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 { .count {
padding-top: 6px;
padding-bottom: 0;
font-size: 12px; 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 { .graph-separator {
position: relative;
width: $graph-separator-width; width: $graph-separator-width;
height: 18px; height: 18px;
margin: 5px 0 0;
float: left;
background-color: $gl-gray-200; background-color: $gl-gray-200;
} }
} }
......
- merged = local_assigns.fetch(:merged, false) - merged = local_assigns.fetch(:merged, false)
- commit = @repository.commit(branch.dereferenced_target) - 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) - diverging_commit_counts = @repository.diverging_commit_counts(branch)
- number_commits_distance = diverging_commit_counts[:distance] - number_commits_distance = diverging_commit_counts[:distance]
- number_commits_behind = diverging_commit_counts[:behind] - number_commits_behind = diverging_commit_counts[:behind]
...@@ -31,23 +30,7 @@ ...@@ -31,23 +30,7 @@
= s_('Branches|Cant find HEAD commit for this branch') = s_('Branches|Cant find HEAD commit for this branch')
- if branch.name != @repository.root_ref - if branch.name != @repository.root_ref
- if number_commits_distance.nil? .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 } }
.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)
.controls.d-none.d-md-block< .controls.d-none.d-md-block<
- if merge_project && create_mr_button?(@repository.root_ref, branch.name) - if merge_project && create_mr_button?(@repository.root_ref, branch.name)
......
// 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%;');
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment