Commit a3458c7a authored by Nathan Friend's avatar Nathan Friend

Add merge requests stats to release progress view

This commit updates the release progress view on each release block to
show statistics about related merge requests (similar to the existing
issue statistics).
parent 766da6c5
......@@ -54,9 +54,7 @@ export default {
</script>
<template>
<div
class="gl-display-flex gl-flex-direction-column gl-flex-shrink-0 gl-mr-6 gl-mb-5 js-issues-container"
>
<div class="gl-display-flex gl-flex-direction-column gl-flex-shrink-0 gl-mr-6 gl-mb-5">
<span class="gl-mb-2">
{{ label }}
<gl-badge variant="muted" size="sm">{{ total }}</gl-badge>
......
<script>
import { GlProgressBar, GlLink, GlButton, GlTooltipDirective } from '@gitlab/ui';
import { sum } from 'lodash';
import { __, n__, sprintf } from '~/locale';
import { MAX_MILESTONES_TO_DISPLAY } from '../constants';
import IssuableStats from './issuable_stats.vue';
......@@ -31,6 +30,21 @@ export default {
required: false,
default: '',
},
openMergeRequestsPath: {
type: String,
required: false,
default: '',
},
mergedMergeRequestsPath: {
type: String,
required: false,
default: '',
},
closedMergeRequestsPath: {
type: String,
required: false,
default: '',
},
},
data() {
return {
......@@ -45,17 +59,45 @@ export default {
});
},
percentComplete() {
const percent = Math.round((this.closedIssuesCount / this.totalIssuesCount) * 100);
const percent = Math.round((this.issueCounts.closed / this.issueCounts.total) * 100);
return Number.isNaN(percent) ? 0 : percent;
},
allIssueStats() {
return this.milestones.map(m => m.issueStats || {});
issueCounts() {
return this.milestones
.map(m => m.issueStats || {})
.reduce(
(acc, current) => {
acc.total += current.total || 0;
acc.closed += current.closed || 0;
return acc;
},
{
total: 0,
closed: 0,
},
);
},
totalIssuesCount() {
return sum(this.allIssueStats.map(stats => stats.total || 0));
showMergeRequestStats() {
return this.milestones.some(m => m.mrStats);
},
closedIssuesCount() {
return sum(this.allIssueStats.map(stats => stats.closed || 0));
mergeRequestCounts() {
return this.milestones
.map(m => m.mrStats || {})
.reduce(
(acc, current) => {
acc.total += current.total || 0;
acc.merged += current.merged || 0;
acc.closed += current.closed || 0;
return acc;
},
{
total: 0,
merged: 0,
closed: 0,
},
);
},
milestoneLabelText() {
return n__('Milestone', 'Milestones', this.milestones.length);
......@@ -98,7 +140,7 @@ export default {
>
<span class="gl-mb-3">{{ percentCompleteText }}</span>
<span class="gl-w-full">
<gl-progress-bar :value="closedIssuesCount" :max="totalIssuesCount" variant="success" />
<gl-progress-bar :value="issueCounts.closed" :max="issueCounts.total" variant="success" />
</span>
</div>
<div
......@@ -129,10 +171,22 @@ export default {
</div>
<issuable-stats
:label="__('Issues')"
:total="totalIssuesCount"
:closed="closedIssuesCount"
:total="issueCounts.total"
:closed="issueCounts.closed"
:open-path="openIssuesPath"
:closed-path="closedIssuesPath"
data-testid="issue-stats"
/>
<issuable-stats
v-if="showMergeRequestStats"
:label="__('Merge Requests')"
:total="mergeRequestCounts.total"
:merged="mergeRequestCounts.merged"
:closed="mergeRequestCounts.closed"
:open-path="openMergeRequestsPath"
:merged-path="mergedMergeRequestsPath"
:closed-path="closedMergeRequestsPath"
data-testid="merge-request-stats"
/>
</div>
</template>
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`~/releases/components/issuable_stats.vue matches snapshot 1`] = `
"<div class=\\"gl-display-flex gl-flex-direction-column gl-flex-shrink-0 gl-mr-6 gl-mb-5 js-issues-container\\"><span class=\\"gl-mb-2\\">
"<div class=\\"gl-display-flex gl-flex-direction-column gl-flex-shrink-0 gl-mr-6 gl-mb-5\\"><span class=\\"gl-mb-2\\">
Items
<span class=\\"badge badge-muted badge-pill gl-badge sm\\"><!----> 10</span></span>
<div class=\\"gl-display-flex\\"><span data-testid=\\"open-stat\\" class=\\"gl-white-space-pre-wrap\\">Open: <a href=\\"path/to/open/items\\" class=\\"gl-link\\">1</a></span> <span class=\\"gl-mx-2\\">•</span> <span data-testid=\\"merged-stat\\" class=\\"gl-white-space-pre-wrap\\">Merged: <a href=\\"path/to/merged/items\\" class=\\"gl-link\\">7</a></span> <span class=\\"gl-mx-2\\">•</span> <span data-testid=\\"closed-stat\\" class=\\"gl-white-space-pre-wrap\\">Closed: <a href=\\"path/to/closed/items\\" class=\\"gl-link\\">2</a></span></div>
......
......@@ -31,7 +31,8 @@ describe('Release block milestone info', () => {
const milestoneProgressBarContainer = () => wrapper.find('.js-milestone-progress-bar-container');
const milestoneListContainer = () => wrapper.find('.js-milestone-list-container');
const issuesContainer = () => wrapper.find('.js-issues-container');
const issuesContainer = () => wrapper.find('[data-testid="issue-stats"]');
const mergeRequestsContainer = () => wrapper.find('[data-testid="merge-request-stats"]');
describe('with default props', () => {
beforeEach(() => factory({ milestones }));
......@@ -187,4 +188,33 @@ describe('Release block milestone info', () => {
expectAllZeros();
});
describe('if the API response is missing the "mr_stats" property', () => {
beforeEach(() => factory({ milestones }));
it('does not render merge request stats', () => {
expect(mergeRequestsContainer().exists()).toBe(false);
});
});
describe('if the API response includes the "mr_stats" property', () => {
beforeEach(() => {
milestones = milestones.map(m => ({
...m,
mrStats: {
total: 15,
merged: 12,
closed: 1,
},
}));
return factory({ milestones });
});
it('renders merge request stats', () => {
expect(trimText(mergeRequestsContainer().text())).toBe(
'Merge Requests 30 Open: 4 • Merged: 24 • Closed: 2',
);
});
});
});
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