Skip to content
Snippets Groups Projects
Verified Commit c5b500c8 authored by Phil Hughes's avatar Phil Hughes
Browse files

Added approvals count badge to merge request row in dashboard

parent f192d3dd
No related branches found
No related tags found
2 merge requests!162233Draft: Script to update Topology Service Gem,!151667Added approvals count badge to merge request row in dashboard
Showing
with 356 additions and 93 deletions
<script>
import { GlBadge, GlTooltipDirective } from '@gitlab/ui';
import { n__ } from '~/locale';
export default {
components: {
GlBadge,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
mergeRequest: {
type: Object,
required: true,
},
},
computed: {
hasApprovers() {
return this.mergeRequest.approvedBy.nodes.length;
},
tooltipTitle() {
return n__('%d approver', '%d approvers', this.mergeRequest.approvedBy.nodes.length);
},
},
};
</script>
<template>
<gl-badge v-if="hasApprovers" v-gl-tooltip="tooltipTitle" icon="approval" variant="success">
{{ __('Approved') }}
</gl-badge>
</template>
import MergeRequest from './merge_request.vue';
const mockMergeRequest = {
reference: '!123456',
titleHtml: 'Merge request title',
webUrl: '#',
author: {
name: 'John Smith',
webUrl: 'https://gitlab.com/root',
},
milestone: {
title: '17.0',
},
labels: {
nodes: [
{
id: 'gid://gitlab/GroupLabel/992791',
color: '#428BCA',
title: 'Deliverable',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/3103452',
color: '#E44D2A',
title: 'devops::create',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/2975007',
color: '#F0AD4E',
title: 'feature::enhancement',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/3412464',
color: '#3cb371',
title: 'frontend',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/16934793',
color: '#A8D695',
title: 'group::code review',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/14918378',
color: '#F0AD4E',
title: 'section::dev',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/10230929',
color: '#009966',
title: 'type::feature',
description: 'Label description',
},
],
},
assignees: {
nodes: [
{
id: 'gid://gitlab/User/1',
avatarUrl: '',
name: 'John Smith',
username: 'jsmith',
webUrl: 'https://gitlab.com/root',
webPath: '/root',
},
],
},
reviewers: {
nodes: [
{
id: 'gid://gitlab/User/1',
avatarUrl: '',
name: 'John Smith',
username: 'jsmith',
webUrl: 'https://gitlab.com/root',
webPath: '/root',
},
{
id: 'gid://gitlab/User/2',
avatarUrl: '',
name: 'John Smith',
username: 'jsmith',
webUrl: 'https://gitlab.com/root',
webPath: '/root',
},
],
},
userDiscussionsCount: 5,
createdAt: '2024-04-22T10:13:09Z',
updatedAt: '2024-04-19T14:34:42Z',
};
const Template = (_, { argTypes }) => {
return {
components: { MergeRequest },
......@@ -15,106 +109,25 @@ export default {
export const Default = Template.bind({});
Default.args = {
mergeRequest: mockMergeRequest,
};
export const WithApprovalNeeded = Template.bind({});
WithApprovalNeeded.args = {
mergeRequest: {
reference: '!123456',
titleHtml: 'Merge request title',
webUrl: '#',
author: {
name: 'John Smith',
webUrl: 'https://gitlab.com/root',
},
milestone: {
title: '17.0',
},
labels: {
...mockMergeRequest,
approved: false,
approvalsRequired: 4,
approvalsLeft: 2,
approvedBy: {
nodes: [
{
id: 'gid://gitlab/GroupLabel/992791',
color: '#428BCA',
title: 'Deliverable',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/3103452',
color: '#E44D2A',
title: 'devops::create',
description: 'Label description',
id: 1,
},
{
id: 'gid://gitlab/GroupLabel/2975007',
color: '#F0AD4E',
title: 'feature::enhancement',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/3412464',
color: '#3cb371',
title: 'frontend',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/16934793',
color: '#A8D695',
title: 'group::code review',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/14918378',
color: '#F0AD4E',
title: 'section::dev',
description: 'Label description',
},
{
id: 'gid://gitlab/GroupLabel/10230929',
color: '#009966',
title: 'type::feature',
description: 'Label description',
},
],
},
assignees: {
nodes: [
{
id: 'gid://gitlab/User/1',
avatarUrl: '',
name: 'John Smith',
username: 'jsmith',
webUrl: 'https://gitlab.com/root',
webPath: '/root',
id: 2,
},
],
},
reviewers: {
nodes: [
{
id: 'gid://gitlab/User/1',
avatarUrl: '',
name: 'John Smith',
username: 'jsmith',
webUrl: 'https://gitlab.com/root',
webPath: '/root',
},
{
id: 'gid://gitlab/User/2',
avatarUrl: '',
name: 'John Smith',
username: 'jsmith',
webUrl: 'https://gitlab.com/root',
webPath: '/root',
},
],
},
headPipeline: {
id: 'gid://gitlab/Ci::Pipeline/1',
detailedStatus: {
id: 'success-1',
icon: 'status_success',
text: 'Passed',
detailsPath: '/',
},
},
userDiscussionsCount: 5,
createdAt: '2024-04-22T10:13:09Z',
updatedAt: '2024-04-19T14:34:42Z',
},
};
<script>
import { GlLink, GlSprintf, GlIcon, GlLabel, GlTooltipDirective } from '@gitlab/ui';
import ApprovalCount from 'ee_component/merge_request_dashboard/components/approval_count.vue';
import { __, sprintf } from '~/locale';
import isShowingLabelsQuery from '~/graphql_shared/client/is_showing_labels.query.graphql';
import SafeHtml from '~/vue_shared/directives/safe_html';
......@@ -23,6 +24,7 @@ export default {
CiIcon,
TimeAgoTooltip,
UserAvatarLink,
ApprovalCount,
},
directives: {
SafeHtml,
......@@ -115,6 +117,12 @@ export default {
:class="{ 'gl-mr-2': index !== mergeRequest.reviewers.nodes.length - 1 }"
/>
</li>
<li
v-if="mergeRequest.approvalsRequired || mergeRequest.approved"
class="gl-ml-4 gl-display-flex gl-align-self-center"
>
<approval-count :merge-request="mergeRequest" />
</li>
<li
v-if="mergeRequest.userDiscussionsCount"
v-gl-tooltip="__('Comments')"
......
......@@ -2,6 +2,7 @@
#import "~/graphql_shared/fragments/label.fragment.graphql"
#import "~/graphql_shared/fragments/milestone.fragment.graphql"
#import "~/graphql_shared/fragments/ci_icon.fragment.graphql"
#import "ee_else_ce/merge_request_dashboard/queries/merge_request_approval.fragment.graphql"
fragment MergeRequestDashboardFragment on MergeRequest {
id
......@@ -38,4 +39,5 @@ fragment MergeRequestDashboardFragment on MergeRequest {
userDiscussionsCount
createdAt
updatedAt
...MergeRequestApprovalFragment
}
fragment MergeRequestApprovalFragment on MergeRequest {
approved
approvedBy {
nodes {
id
}
}
}
<script>
import { GlBadge, GlTooltipDirective } from '@gitlab/ui';
import { __, sprintf } from '~/locale';
import ApprovalsCountCe from '~/merge_request_dashboard/components/approval_count.vue';
export default {
components: {
GlBadge,
ApprovalsCountCe,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
mergeRequest: {
type: Object,
required: true,
},
},
computed: {
approvalText() {
return this.mergeRequest.approved
? __('Approved')
: sprintf(__('%{approvals_given} of %{required} Approvals'), {
approvals_given: this.mergeRequest.approvalsRequired - this.mergeRequest.approvalsLeft,
required: this.mergeRequest.approvalsRequired,
});
},
tooltipTitle() {
return sprintf(__('Required approvals (%{approvals_given} of %{required} given)'), {
approvals_given: this.mergeRequest.approvalsRequired - this.mergeRequest.approvalsLeft,
required: this.mergeRequest.approvalsRequired,
});
},
badgeVariant() {
return this.mergeRequest.approved ? 'success' : 'neutral';
},
badgeIcon() {
return this.mergeRequest.approved ? 'check' : 'approval';
},
},
};
</script>
<template>
<gl-badge
v-if="mergeRequest.approvalsRequired"
v-gl-tooltip="tooltipTitle"
icon="approval"
:variant="badgeVariant"
>
{{ approvalText }}
</gl-badge>
<approvals-count-ce v-else :merge-request="mergeRequest" />
</template>
#import "~/merge_request_dashboard/queries/merge_request_approval.fragment.graphql"
fragment MergeRequestApprovalFragment on MergeRequest {
approved
approvalsRequired
approvalsLeft
approvedBy {
nodes {
id
}
}
}
import { shallowMount } from '@vue/test-utils';
import { GlBadge } from '@gitlab/ui';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import ApprovalCount from 'ee/merge_request_dashboard/components/approval_count.vue';
import ApprovalCountFOSS from '~/merge_request_dashboard/components/approval_count.vue';
let wrapper;
function createComponent(propsData = {}) {
wrapper = shallowMount(ApprovalCount, {
propsData,
directives: {
GlTooltip: createMockDirective('gl-tooltip'),
},
});
}
const findFossApprovalCount = () => wrapper.findComponent(ApprovalCountFOSS);
const findBadge = () => wrapper.findComponent(GlBadge);
const findTooltip = () => getBinding(findBadge().element, 'gl-tooltip');
describe('Merge request dashboard approval count FOSS component', () => {
describe('when approvals are not required', () => {
it('renders approval count FOSS component', () => {
createComponent({
mergeRequest: { approvalsRequired: 0 },
});
expect(findFossApprovalCount().exists()).toBe(true);
expect(findFossApprovalCount().props('mergeRequest')).toEqual(
expect.objectContaining({
approvalsRequired: 0,
}),
);
});
});
describe('when approvals are required', () => {
it('renders badge when merge request is approved', () => {
createComponent({
mergeRequest: { approvalsRequired: 1, approvalsLeft: 1 },
});
expect(findBadge().exists()).toBe(true);
});
it.each`
approved | approvalsRequired | approvalsLeft | tooltipTitle
${false} | ${1} | ${1} | ${'Required approvals (0 of 1 given)'}
${false} | ${1} | ${0} | ${'Required approvals (1 of 1 given)'}
`(
'renders badge with correct tooltip title',
({ approved, approvalsRequired, approvalsLeft, tooltipTitle }) => {
createComponent({
mergeRequest: { approved, approvalsRequired, approvalsLeft },
});
expect(findTooltip().value).toBe(tooltipTitle);
},
);
it.each`
approved | approvalsRequired | approvalsLeft | tooltipTitle
${false} | ${1} | ${1} | ${'0 of 1 Approvals'}
${false} | ${1} | ${0} | ${'1 of 1 Approvals'}
${true} | ${1} | ${0} | ${'Approved'}
`(
'renders badge with correct tooltip title',
({ approved, approvalsRequired, approvalsLeft, tooltipTitle }) => {
createComponent({
mergeRequest: { approved, approvalsRequired, approvalsLeft },
});
expect(findBadge().text()).toBe(tooltipTitle);
},
);
});
});
import { shallowMount } from '@vue/test-utils';
import { GlBadge } from '@gitlab/ui';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import ApprovalCount from '~/merge_request_dashboard/components/approval_count.vue';
let wrapper;
function createComponent(propsData = {}) {
wrapper = shallowMount(ApprovalCount, {
propsData,
directives: {
GlTooltip: createMockDirective('gl-tooltip'),
},
});
}
const findBadge = () => wrapper.findComponent(GlBadge);
const findTooltip = () => getBinding(findBadge().element, 'gl-tooltip');
describe('Merge request dashboard approval count FOSS component', () => {
it('does not render badge when merge request is not approved', () => {
createComponent({
mergeRequest: { approvedBy: { nodes: [] } },
});
expect(findBadge().exists()).toBe(false);
});
it('renders badge when merge request is approved', () => {
createComponent({
mergeRequest: { approvedBy: { nodes: ['approved'] } },
});
expect(findBadge().exists()).toBe(true);
});
it.each`
approvers | tooltipTitle
${[1]} | ${'1 approver'}
${[1, 2]} | ${'2 approvers'}
`('renders badge with correct tooltip title', ({ approvers, tooltipTitle }) => {
createComponent({
mergeRequest: { approvedBy: { nodes: approvers } },
});
expect(findTooltip().value).toBe(tooltipTitle);
});
});
......@@ -26,6 +26,12 @@ export function createMockMergeRequest(mergeRequest = {}) {
userDiscussionsCount: 0,
createdAt: '',
updatedAt: '',
approved: false,
approvalsRequired: 0,
approvalsLeft: null,
approvedBy: {
nodes: [],
},
__typename: 'MergeRequest',
...mergeRequest,
};
......
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