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 ...@@ -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 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 SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue';
import VulnerabilityCommentIcon from 'ee/security_dashboard/components/vulnerability_comment_icon.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 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 getPrimaryIdentifier from 'ee/vue_shared/security_reports/store/utils/get_primary_identifier';
import SecurityScannerAlert from './security_scanner_alert.vue'; import SecurityScannerAlert from './security_scanner_alert.vue';
...@@ -21,6 +20,7 @@ import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; ...@@ -21,6 +20,7 @@ import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import { formatDate } from '~/lib/utils/datetime_utility'; import { formatDate } from '~/lib/utils/datetime_utility';
import { s__, __, sprintf } from '~/locale'; import { s__, __, sprintf } from '~/locale';
import SelectionSummary from './selection_summary.vue'; import SelectionSummary from './selection_summary.vue';
import IssuesBadge from './issues_badge.vue';
import { VULNERABILITIES_PER_PAGE } from '../store/constants'; import { VULNERABILITIES_PER_PAGE } from '../store/constants';
export const SCANNER_ALERT_DISMISSED_LOCAL_STORAGE_KEY = export const SCANNER_ALERT_DISMISSED_LOCAL_STORAGE_KEY =
...@@ -33,7 +33,7 @@ export default { ...@@ -33,7 +33,7 @@ export default {
GlSkeletonLoading, GlSkeletonLoading,
GlSprintf, GlSprintf,
GlTable, GlTable,
IssueLink, IssuesBadge,
LocalStorageSync, LocalStorageSync,
RemediatedBadge, RemediatedBadge,
SecurityScannerAlert, SecurityScannerAlert,
...@@ -168,7 +168,6 @@ export default { ...@@ -168,7 +168,6 @@ export default {
key: 'activity', key: 'activity',
label: s__('Vulnerability|Activity'), label: s__('Vulnerability|Activity'),
thClass: 'gl-text-right', thClass: 'gl-text-right',
tdClass: 'gl-text-right',
}, },
]; ];
...@@ -259,8 +258,8 @@ export default { ...@@ -259,8 +258,8 @@ export default {
this.$set(this.selectedVulnerabilities, `${vulnerability.id}`, vulnerability); this.$set(this.selectedVulnerabilities, `${vulnerability.id}`, vulnerability);
} }
}, },
issue(item) { issues(item) {
return item.issueLinks?.nodes[0]?.issue; return item.issueLinks?.nodes || [];
}, },
formatDate(item) { formatDate(item) {
return formatDate(item.detectedAt, 'yyyy-mm-dd'); return formatDate(item.detectedAt, 'yyyy-mm-dd');
...@@ -366,7 +365,6 @@ export default { ...@@ -366,7 +365,6 @@ export default {
> >
{{ item.title }} {{ item.title }}
</gl-link> </gl-link>
<issue-link v-if="issue(item)" :issue="issue(item)" />
<vulnerability-comment-icon v-if="hasComments(item)" :vulnerability="item" /> <vulnerability-comment-icon v-if="hasComments(item)" :vulnerability="item" />
</div> </div>
<div <div
...@@ -414,7 +412,10 @@ export default { ...@@ -414,7 +412,10 @@ export default {
</template> </template>
<template #cell(activity)="{ item }"> <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>
<template #table-busy> <template #table-busy>
......
...@@ -7,11 +7,12 @@ fragment Vulnerability on Vulnerability { ...@@ -7,11 +7,12 @@ fragment Vulnerability on Vulnerability {
vulnerabilityPath vulnerabilityPath
resolvedOnDefaultBranch resolvedOnDefaultBranch
userNotesCount userNotesCount
issueLinks(linkType: CREATED) { issueLinks {
nodes { nodes {
issue { issue {
iid iid
webUrl webUrl
webPath
title title
state state
} }
......
...@@ -23,7 +23,7 @@ export default { ...@@ -23,7 +23,7 @@ export default {
v-gl-tooltip="issue.title" v-gl-tooltip="issue.title"
:href="issue.webUrl" :href="issue.webUrl"
:data-testid="`issue-link-${issue.iid}`" :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 <gl-icon
class="mr-1" 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'; ...@@ -2,6 +2,7 @@ import { mount } from '@vue/test-utils';
import { GlDeprecatedSkeletonLoading as GlSkeletonLoading, GlTable } from '@gitlab/ui'; import { GlDeprecatedSkeletonLoading as GlSkeletonLoading, GlTable } from '@gitlab/ui';
import { useLocalStorageSpy } from 'helpers/local_storage_helper'; import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import RemediatedBadge from 'ee/vulnerabilities/components/remediated_badge.vue'; 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 SecurityScannerAlert from 'ee/security_dashboard/components/security_scanner_alert.vue';
import SelectionSummary from 'ee/security_dashboard/components/selection_summary.vue'; import SelectionSummary from 'ee/security_dashboard/components/selection_summary.vue';
import VulnerabilityCommentIcon from 'ee/security_dashboard/components/vulnerability_comment_icon.vue'; import VulnerabilityCommentIcon from 'ee/security_dashboard/components/vulnerability_comment_icon.vue';
...@@ -42,6 +43,7 @@ describe('Vulnerability list component', () => { ...@@ -42,6 +43,7 @@ describe('Vulnerability list component', () => {
const findSortableColumn = () => wrapper.find('[aria-sort="descending"]'); const findSortableColumn = () => wrapper.find('[aria-sort="descending"]');
const findCell = label => wrapper.find(`.js-${label}`); const findCell = label => wrapper.find(`.js-${label}`);
const findRow = (index = 0) => wrapper.findAll('tbody tr').at(index); const findRow = (index = 0) => wrapper.findAll('tbody tr').at(index);
const findIssuesBadge = () => wrapper.find(IssuesBadge);
const findRemediatedBadge = () => wrapper.find(RemediatedBadge); const findRemediatedBadge = () => wrapper.find(RemediatedBadge);
const findSecurityScannerAlert = () => wrapper.find(SecurityScannerAlert); const findSecurityScannerAlert = () => wrapper.find(SecurityScannerAlert);
const findDismissalButton = () => findSecurityScannerAlert().find('button[aria-label="Dismiss"]'); const findDismissalButton = () => findSecurityScannerAlert().find('button[aria-label="Dismiss"]');
...@@ -87,6 +89,10 @@ describe('Vulnerability list component', () => { ...@@ -87,6 +89,10 @@ describe('Vulnerability list component', () => {
expect(cell.text()).toBe(newVulnerabilities[0].title); expect(cell.text()).toBe(newVulnerabilities[0].title);
}); });
it('should display the issues badge', () => {
expect(findIssuesBadge().exists()).toBe(true);
});
it('should display the remediated badge', () => { it('should display the remediated badge', () => {
expect(findRemediatedBadge().exists()).toBe(true); expect(findRemediatedBadge().exists()).toBe(true);
}); });
......
...@@ -1014,6 +1014,11 @@ msgid_plural "%d Days" ...@@ -1014,6 +1014,11 @@ msgid_plural "%d Days"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
msgid "1 Issue"
msgid_plural "%d Issues"
msgstr[0] ""
msgstr[1] ""
msgid "1 closed issue" msgid "1 closed issue"
msgid_plural "%{issues} closed issues" msgid_plural "%{issues} closed issues"
msgstr[0] "" 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