Commit 932cad3a authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Merge branch '230381-activity-colum-display-issues' into 'master'

Move created issues in the activity column

See merge request gitlab-org/gitlab!43016
parents df7e484a 79607169
<script>
import { GlIcon, GlPopover, GlBadge } from '@gitlab/ui';
import IssueLink from 'ee/vulnerabilities/components/issue_link.vue';
import { n__ } from '~/locale';
export default {
components: {
GlIcon,
GlBadge,
GlPopover,
IssueLink,
},
props: {
issues: {
type: Array,
required: true,
},
},
computed: {
numberOfIssues() {
return this.issues.length;
},
popoverTitle() {
return n__('1 Issue', '%d Issues', this.numberOfIssues);
},
},
};
</script>
<template>
<div class="gl-display-inline-block">
<gl-badge ref="issueBadge" class="gl-px-3">
<gl-icon name="issues" class="gl-mr-2" />
{{ numberOfIssues }}
</gl-badge>
<gl-popover ref="popover" :target="() => $refs.issueBadge.$el" triggers="hover" placement="top">
<template #title>
{{ popoverTitle }}
</template>
<div v-for="{ issue } in issues" :key="issue.iid">
<issue-link :issue="issue" />
</div>
</gl-popover>
</div>
</template>
......@@ -13,7 +13,6 @@ import FiltersProducedNoResults from 'ee/security_dashboard/components/empty_sta
import DashboardHasNoVulnerabilities from 'ee/security_dashboard/components/empty_states/dashboard_has_no_vulnerabilities.vue';
import SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue';
import VulnerabilityCommentIcon from 'ee/security_dashboard/components/vulnerability_comment_icon.vue';
import IssueLink from 'ee/vulnerabilities/components/issue_link.vue';
import convertReportType from 'ee/vue_shared/security_reports/store/utils/convert_report_type';
import getPrimaryIdentifier from 'ee/vue_shared/security_reports/store/utils/get_primary_identifier';
import SecurityScannerAlert from './security_scanner_alert.vue';
......@@ -21,6 +20,7 @@ import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import { formatDate } from '~/lib/utils/datetime_utility';
import { s__, __, sprintf } from '~/locale';
import SelectionSummary from './selection_summary.vue';
import IssuesBadge from './issues_badge.vue';
import { VULNERABILITIES_PER_PAGE } from '../store/constants';
export const SCANNER_ALERT_DISMISSED_LOCAL_STORAGE_KEY =
......@@ -33,7 +33,7 @@ export default {
GlSkeletonLoading,
GlSprintf,
GlTable,
IssueLink,
IssuesBadge,
LocalStorageSync,
RemediatedBadge,
SecurityScannerAlert,
......@@ -168,7 +168,6 @@ export default {
key: 'activity',
label: s__('Vulnerability|Activity'),
thClass: 'gl-text-right',
tdClass: 'gl-text-right',
},
];
......@@ -259,8 +258,8 @@ export default {
this.$set(this.selectedVulnerabilities, `${vulnerability.id}`, vulnerability);
}
},
issue(item) {
return item.issueLinks?.nodes[0]?.issue;
issues(item) {
return item.issueLinks?.nodes || [];
},
formatDate(item) {
return formatDate(item.detectedAt, 'yyyy-mm-dd');
......@@ -366,7 +365,6 @@ export default {
>
{{ item.title }}
</gl-link>
<issue-link v-if="issue(item)" :issue="issue(item)" />
<vulnerability-comment-icon v-if="hasComments(item)" :vulnerability="item" />
</div>
<div
......@@ -414,7 +412,10 @@ export default {
</template>
<template #cell(activity)="{ item }">
<remediated-badge v-if="item.resolvedOnDefaultBranch" class="gl-ml-3" />
<div class="gl-display-flex gl-justify-content-end">
<issues-badge :issues="issues(item)" />
<remediated-badge v-if="item.resolvedOnDefaultBranch" class="gl-ml-3" />
</div>
</template>
<template #table-busy>
......
......@@ -7,11 +7,12 @@ fragment Vulnerability on Vulnerability {
vulnerabilityPath
resolvedOnDefaultBranch
userNotesCount
issueLinks(linkType: CREATED) {
issueLinks {
nodes {
issue {
iid
webUrl
webPath
title
state
}
......
......@@ -23,7 +23,7 @@ export default {
v-gl-tooltip="issue.title"
:href="issue.webUrl"
:data-testid="`issue-link-${issue.iid}`"
class="d-inline-flex align-items-center ml-2 gl-flex-shrink-0"
class="d-inline-flex align-items-center gl-flex-shrink-0"
>
<gl-icon
class="mr-1"
......
---
title: Move created issues in the activity column
merge_request: 43016
author:
type: changed
import { shallowMount } from '@vue/test-utils';
import { GlIcon, GlPopover, GlBadge } from '@gitlab/ui';
import IssuesBadge from 'ee/security_dashboard/components/issues_badge.vue';
import IssueLink from 'ee/vulnerabilities/components/issue_link.vue';
describe('Remediated badge component', () => {
const issues = [{ issue: { iid: 41 } }, { issue: { iid: 591 } }];
let wrapper;
const findIcon = () => wrapper.find(GlIcon);
const findBadge = () => wrapper.find(GlBadge);
const findIssueLink = () => wrapper.findAll(IssueLink);
const findPopover = () => wrapper.find(GlPopover);
const createWrapper = ({ propsData }) => {
return shallowMount(IssuesBadge, { propsData, stubs: { GlPopover, GlBadge } });
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('when there are multiple issues', () => {
beforeEach(() => {
wrapper = createWrapper({ propsData: { issues } });
});
it('displays the correct icon', () => {
expect(findIcon().props('name')).toBe('issues');
});
it('links the badge and the popover', () => {
const { popover } = wrapper.vm.$refs;
expect(popover.$attrs.target()).toEqual(findIcon().element.parentNode);
});
it('displays the issues', () => {
expect(findIssueLink()).toHaveLength(issues.length);
});
it('displays the correct number of issues in the badge', () => {
expect(findBadge().text()).toBe('2');
});
it('displays the correct number of issues in the popover title', () => {
expect(findPopover().text()).toBe('2 Issues');
});
});
describe('when there are no issues', () => {
beforeEach(() => {
wrapper = createWrapper({ propsData: { issues: [] } });
});
it('displays the correct number of issues in the badge', () => {
expect(findBadge().text()).toBe('0');
});
it('displays the correct number of issues in the popover title', () => {
expect(findPopover().text()).toBe('0 Issues');
});
});
});
......@@ -2,6 +2,7 @@ import { mount } from '@vue/test-utils';
import { GlDeprecatedSkeletonLoading as GlSkeletonLoading, GlTable } from '@gitlab/ui';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import RemediatedBadge from 'ee/vulnerabilities/components/remediated_badge.vue';
import IssuesBadge from 'ee/security_dashboard/components/issues_badge.vue';
import SecurityScannerAlert from 'ee/security_dashboard/components/security_scanner_alert.vue';
import SelectionSummary from 'ee/security_dashboard/components/selection_summary.vue';
import VulnerabilityCommentIcon from 'ee/security_dashboard/components/vulnerability_comment_icon.vue';
......@@ -42,6 +43,7 @@ describe('Vulnerability list component', () => {
const findSortableColumn = () => wrapper.find('[aria-sort="descending"]');
const findCell = label => wrapper.find(`.js-${label}`);
const findRow = (index = 0) => wrapper.findAll('tbody tr').at(index);
const findIssuesBadge = () => wrapper.find(IssuesBadge);
const findRemediatedBadge = () => wrapper.find(RemediatedBadge);
const findSecurityScannerAlert = () => wrapper.find(SecurityScannerAlert);
const findDismissalButton = () => findSecurityScannerAlert().find('button[aria-label="Dismiss"]');
......@@ -87,6 +89,10 @@ describe('Vulnerability list component', () => {
expect(cell.text()).toBe(newVulnerabilities[0].title);
});
it('should display the issues badge', () => {
expect(findIssuesBadge().exists()).toBe(true);
});
it('should display the remediated badge', () => {
expect(findRemediatedBadge().exists()).toBe(true);
});
......
......@@ -1014,6 +1014,11 @@ msgid_plural "%d Days"
msgstr[0] ""
msgstr[1] ""
msgid "1 Issue"
msgid_plural "%d Issues"
msgstr[0] ""
msgstr[1] ""
msgid "1 closed issue"
msgid_plural "%{issues} closed issues"
msgstr[0] ""
......
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