Commit a97dbf68 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '334041-display-progress-of-epic-weight-on-epic-boards' into 'master'

Show progress of an epic on epic board cards

See merge request gitlab-org/gitlab!64622
parents adc62ff2 788e8143
<script>
import { GlLabel, GlTooltip, GlTooltipDirective, GlIcon, GlLoadingIcon } from '@gitlab/ui';
import {
GlLabel,
GlTooltip,
GlTooltipDirective,
GlIcon,
GlLoadingIcon,
GlSprintf,
} from '@gitlab/ui';
import { sortBy } from 'lodash';
import { mapActions, mapGetters, mapState } from 'vuex';
import boardCardInner from 'ee_else_ce/boards/mixins/board_card_inner';
......@@ -26,6 +33,7 @@ export default {
IssueTimeEstimate,
IssueCardWeight: () => import('ee_component/boards/components/issue_card_weight.vue'),
BoardBlockedIcon,
GlSprintf,
},
directives: {
GlTooltip: GlTooltipDirective,
......@@ -103,6 +111,9 @@ export default {
shouldRenderEpicCountables() {
return this.isEpicBoard && this.item.hasIssues;
},
shouldRenderEpicProgress() {
return this.totalWeight > 0;
},
showLabelFooter() {
return this.isShowingLabels && this.item.labels.find(this.showLabel);
},
......@@ -130,6 +141,9 @@ export default {
this.item.descendantWeightSum.openedIssues + this.item.descendantWeightSum.closedIssues
);
},
totalProgress() {
return Math.round((this.item.descendantWeightSum.closedIssues / this.totalWeight) * 100);
},
},
methods: {
...mapActions(['performSearch', 'setError']),
......@@ -246,40 +260,51 @@ export default {
<gl-tooltip :target="() => $refs.countBadge" data-testid="epic-countables-tooltip">
<p v-if="allowSubEpics" class="gl-font-weight-bold gl-m-0">
{{ __('Epics') }} &#8226;
<span class="gl-font-weight-normal"
>{{
sprintf(__('%{openedEpics} open, %{closedEpics} closed'), {
openedEpics: item.descendantCounts.openedEpics,
closedEpics: item.descendantCounts.closedEpics,
})
}}
<span class="gl-font-weight-normal">
<gl-sprintf :message="__('%{openedEpics} open, %{closedEpics} closed')">
<template #openedEpics>{{ item.descendantCounts.openedEpics }}</template>
<template #closedEpics>{{ item.descendantCounts.closedEpics }}</template>
</gl-sprintf>
</span>
</p>
<p class="gl-font-weight-bold gl-m-0">
{{ __('Issues') }} &#8226;
<span class="gl-font-weight-normal"
>{{
sprintf(__('%{openedIssues} open, %{closedIssues} closed'), {
openedIssues: item.descendantCounts.openedIssues,
closedIssues: item.descendantCounts.closedIssues,
})
}}
<span class="gl-font-weight-normal">
<gl-sprintf :message="__('%{openedIssues} open, %{closedIssues} closed')">
<template #openedIssues>{{ item.descendantCounts.openedIssues }}</template>
<template #closedIssues>{{ item.descendantCounts.closedIssues }}</template>
</gl-sprintf>
</span>
</p>
<p class="gl-font-weight-bold gl-m-0">
{{ __('Weight') }} &#8226;
<span class="gl-font-weight-normal" data-testid="epic-countables-total-weight"
>{{
sprintf(__('%{closedWeight} complete, %{openWeight} incomplete'), {
openWeight: item.descendantWeightSum.openedIssues,
closedWeight: item.descendantWeightSum.closedIssues,
})
}}
{{ __('Total weight') }} &#8226;
<span class="gl-font-weight-normal" data-testid="epic-countables-total-weight">
{{ totalWeight }}
</span>
</p>
</gl-tooltip>
<span ref="countBadge" class="issue-count-badge board-card-info">
<gl-tooltip
v-if="shouldRenderEpicProgress"
:target="() => $refs.progressBadge"
data-testid="epic-progress-tooltip"
>
<p class="gl-font-weight-bold gl-m-0">
{{ __('Progress') }} &#8226;
<span class="gl-font-weight-normal" data-testid="epic-progress-tooltip-content">
<gl-sprintf
:message="__('%{completedWeight} of %{totalWeight} weight completed')"
>
<template #completedWeight>{{
item.descendantWeightSum.closedIssues
}}</template>
<template #totalWeight>{{ totalWeight }}</template>
</gl-sprintf>
</span>
</p>
</gl-tooltip>
<span ref="countBadge" class="issue-count-badge board-card-info gl-mr-0 gl-pr-0">
<span v-if="allowSubEpics" class="gl-mr-3">
<gl-icon name="epic" />
{{ totalEpicsCount }}
......@@ -293,6 +318,17 @@ export default {
{{ totalWeight }}
</span>
</span>
<span
v-if="shouldRenderEpicProgress"
ref="progressBadge"
class="issue-count-badge board-card-info gl-pl-0"
>
<span class="gl-mr-3" data-testid="epic-progress">
<gl-icon name="progress" />
{{ totalProgress }}%
</span>
</span>
</span>
<span v-if="!isEpicBoard">
<issue-due-date
......
......@@ -130,13 +130,14 @@ You can filter by the following:
- Author
- Label
### View count of issues and weight in an epic
### View count of issues, weight, and progress of an epic
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/331330) in GitLab 14.1.
Epics on an epic board show a summary of their issues and weight.
To see the number of open and closed issues and the completed and incomplete weight,
hover over the issues icon **{issues}** or weight icon **{weight}**.
Epics on an epic board show a summary of their issues, weight, and progress.
To see the number of open and closed issues and the completed and incomplete
weight, hover over the issues icon **{issues}**, weight icon **{weight}**, or
progress icon **{progress}**.
### Move epics and lists
......
......@@ -444,9 +444,6 @@ msgid_plural "%{bold_start}%{count}%{bold_end} opened merge requests"
msgstr[0] ""
msgstr[1] ""
msgid "%{closedWeight} complete, %{openWeight} incomplete"
msgstr ""
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr ""
......
......@@ -40,7 +40,9 @@ describe('Board card component', () => {
const findEpicCountables = () => wrapper.findByTestId('epic-countables');
const findEpicCountablesBadgeIssues = () => wrapper.findByTestId('epic-countables-counts-issues');
const findEpicCountablesBadgeWeight = () => wrapper.findByTestId('epic-countables-weight-issues');
const findEpicBadgeProgress = () => wrapper.findByTestId('epic-progress');
const findEpicCountablesTotalWeight = () => wrapper.findByTestId('epic-countables-total-weight');
const findEpicProgressTooltip = () => wrapper.findByTestId('epic-progress-tooltip-content');
const createStore = ({ isEpicBoard = false } = {}) => {
store = new Vuex.Store({
......@@ -466,7 +468,7 @@ describe('Board card component', () => {
expect(findEpicCountablesBadgeIssues().exists()).toBe(false);
});
it('shows render item countBadge and weights correctly', () => {
it('shows render item countBadge, weights, and progress correctly', () => {
createWrapper({
item: {
...issue,
......@@ -475,15 +477,32 @@ describe('Board card component', () => {
openedIssues: 1,
},
descendantWeightSum: {
...descendantWeightSum,
openedIssues: 2,
closedIssues: 10,
openedIssues: 5,
},
hasIssues: true,
},
});
expect(findEpicCountablesBadgeIssues().text()).toBe('1');
expect(findEpicCountablesBadgeWeight().text()).toBe('2');
expect(findEpicCountablesBadgeWeight().text()).toBe('15');
expect(findEpicBadgeProgress().text()).toBe('67%');
});
it('does not render progress when weight is zero', () => {
createWrapper({
item: {
...issue,
descendantCounts: {
...descendantCounts,
openedIssues: 1,
},
descendantWeightSum,
hasIssues: true,
},
});
expect(findEpicBadgeProgress().exists()).toBe(false);
});
it('renders the tooltip with the correct data', () => {
......@@ -502,7 +521,8 @@ describe('Board card component', () => {
const tooltip = findEpicCountablesTotalTooltip();
expect(tooltip).toBeDefined();
expect(findEpicCountablesTotalWeight().text()).toBe('10 complete, 5 incomplete');
expect(findEpicCountablesTotalWeight().text()).toBe('15');
expect(findEpicProgressTooltip().text()).toBe('10 of 15 weight completed');
});
});
});
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