Commit df6b6ccd authored by Phil Hughes's avatar Phil Hughes

Merge branch '7941-security-dashboard-empty-state' into 'master'

Resolve "Security dashboard empty state"

Closes #7941

See merge request gitlab-org/gitlab-ee!8443
parents 0d8941b4 4ffa69ef
...@@ -28,6 +28,10 @@ export default { ...@@ -28,6 +28,10 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
emptyStateSvgPath: {
type: String,
required: true,
},
vulnerabilitiesEndpoint: { vulnerabilitiesEndpoint: {
type: String, type: String,
required: true, required: true,
...@@ -105,7 +109,10 @@ export default { ...@@ -105,7 +109,10 @@ export default {
</span> </span>
</template> </template>
<security-dashboard-table /> <security-dashboard-table
:dashboard-documentation="dashboardDocumentation"
:empty-state-svg-path="emptyStateSvgPath"
/>
</tab> </tab>
</tabs> </tabs>
<issue-modal <issue-modal
......
<script>
import { GlButton } from '@gitlab/ui';
export default {
name: 'EmptyState',
components: {
GlButton,
},
props: {
svgPath: {
type: String,
required: true,
},
link: {
type: String,
required: true,
},
},
};
</script>
<template>
<div class="empty-state row">
<div class="col-12">
<div class="svg-content svg-250">
<img :src="svgPath" :alt="s__('Security Reports|No Vulnerabilities')" />
</div>
<div class="text-content">
<h4>{{ s__("Security Reports|We've found no vulnerabilities for your group") }}</h4>
<p>
{{
s__(
"Security Reports|While it's rare to have no vulnerabilities for your group, it can happen. In any event, we ask that you please double check your settings to make sure you've set up your dashboard correctly.",
)
}}
</p>
<div class="text-center">
<gl-button variant="success" :href="link">{{
s__('Security Reports|Learn more about setting up your dashboard')
}}</gl-button>
</div>
</div>
</div>
</div>
</template>
...@@ -2,13 +2,25 @@ ...@@ -2,13 +2,25 @@
import { mapActions, mapState, mapGetters } from 'vuex'; import { mapActions, mapState, mapGetters } from 'vuex';
import Pagination from '~/vue_shared/components/pagination_links.vue'; import Pagination from '~/vue_shared/components/pagination_links.vue';
import SecurityDashboardTableRow from './security_dashboard_table_row.vue'; import SecurityDashboardTableRow from './security_dashboard_table_row.vue';
import EmptyState from './empty_state.vue';
export default { export default {
name: 'SecurityDashboardTable', name: 'SecurityDashboardTable',
components: { components: {
EmptyState,
Pagination, Pagination,
SecurityDashboardTableRow, SecurityDashboardTableRow,
}, },
props: {
dashboardDocumentation: {
type: String,
required: true,
},
emptyStateSvgPath: {
type: String,
required: true,
},
},
computed: { computed: {
...mapState('vulnerabilities', ['vulnerabilities', 'pageInfo', 'isLoadingVulnerabilities']), ...mapState('vulnerabilities', ['vulnerabilities', 'pageInfo', 'isLoadingVulnerabilities']),
...mapGetters('vulnerabilities', ['dashboardListError']), ...mapGetters('vulnerabilities', ['dashboardListError']),
...@@ -59,6 +71,12 @@ export default { ...@@ -59,6 +71,12 @@ export default {
@openModal="openModal({ vulnerability });" @openModal="openModal({ vulnerability });"
/> />
<empty-state
v-if="!vulnerabilities.length"
:svg-path="emptyStateSvgPath"
:link="dashboardDocumentation"
/>
<pagination <pagination
v-if="showPagination" v-if="showPagination"
:change="fetchVulnerabilities" :change="fetchVulnerabilities"
......
...@@ -15,6 +15,7 @@ export default () => { ...@@ -15,6 +15,7 @@ export default () => {
return createElement('group-security-dashboard-app', { return createElement('group-security-dashboard-app', {
props: { props: {
dashboardDocumentation: el.dataset.dashboardDocumentation, dashboardDocumentation: el.dataset.dashboardDocumentation,
emptyStateSvgPath: el.dataset.emptyStateSvgPath,
vulnerabilityFeedbackHelpPath: el.dataset.vulnerabilityFeedbackHelpPath, vulnerabilityFeedbackHelpPath: el.dataset.vulnerabilityFeedbackHelpPath,
vulnerabilitiesEndpoint: el.dataset.vulnerabilitiesEndpoint, vulnerabilitiesEndpoint: el.dataset.vulnerabilitiesEndpoint,
vulnerabilitiesCountEndpoint: el.dataset.vulnerabilitiesSummaryEndpoint, vulnerabilitiesCountEndpoint: el.dataset.vulnerabilitiesSummaryEndpoint,
......
...@@ -4,4 +4,5 @@ ...@@ -4,4 +4,5 @@
#js-group-security-dashboard{ data: { vulnerabilities_endpoint: group_security_vulnerabilities_path(@group), #js-group-security-dashboard{ data: { vulnerabilities_endpoint: group_security_vulnerabilities_path(@group),
vulnerabilities_summary_endpoint: summary_group_security_vulnerabilities_path(@group), vulnerabilities_summary_endpoint: summary_group_security_vulnerabilities_path(@group),
vulnerability_feedback_help_path: help_page_path("user/project/merge_requests/index", anchor: "interacting-with-security-reports-ultimate"), vulnerability_feedback_help_path: help_page_path("user/project/merge_requests/index", anchor: "interacting-with-security-reports-ultimate"),
empty_state_svg_path: image_path('illustrations/security-dashboard-empty-state.svg'),
dashboard_documentation: help_page_path('user/group/security_dashboard/index') } } dashboard_documentation: help_page_path('user/group/security_dashboard/index') } }
---
title: Adds Security dashboard empty state
merge_request: 8443
author:
type: added
...@@ -4,6 +4,7 @@ import axios from '~/lib/utils/axios_utils'; ...@@ -4,6 +4,7 @@ import axios from '~/lib/utils/axios_utils';
import component from 'ee/security_dashboard/components/security_dashboard_table.vue'; import component from 'ee/security_dashboard/components/security_dashboard_table.vue';
import createStore from 'ee/security_dashboard/store'; import createStore from 'ee/security_dashboard/store';
import { TEST_HOST } from 'spec/test_constants';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import waitForPromises from 'spec/helpers/wait_for_promises'; import waitForPromises from 'spec/helpers/wait_for_promises';
...@@ -13,6 +14,10 @@ import mockDataVulnerabilities from '../store/vulnerabilities/data/mock_data_vul ...@@ -13,6 +14,10 @@ import mockDataVulnerabilities from '../store/vulnerabilities/data/mock_data_vul
describe('Security Dashboard Table', () => { describe('Security Dashboard Table', () => {
const Component = Vue.extend(component); const Component = Vue.extend(component);
const vulnerabilitiesEndpoint = '/vulnerabilitiesEndpoint.json'; const vulnerabilitiesEndpoint = '/vulnerabilitiesEndpoint.json';
const props = {
dashboardDocumentation: TEST_HOST,
emptyStateSvgPath: TEST_HOST,
};
let store; let store;
let mock; let mock;
let vm; let vm;
...@@ -32,7 +37,7 @@ describe('Security Dashboard Table', () => { ...@@ -32,7 +37,7 @@ describe('Security Dashboard Table', () => {
describe('while loading', () => { describe('while loading', () => {
beforeEach(() => { beforeEach(() => {
store.dispatch('vulnerabilities/requestVulnerabilities'); store.dispatch('vulnerabilities/requestVulnerabilities');
vm = mountComponentWithStore(Component, { store }); vm = mountComponentWithStore(Component, { store, props });
}); });
it('should render 10 skeleton rows in the table', () => { it('should render 10 skeleton rows in the table', () => {
...@@ -40,10 +45,10 @@ describe('Security Dashboard Table', () => { ...@@ -40,10 +45,10 @@ describe('Security Dashboard Table', () => {
}); });
}); });
describe('with success result', () => { describe('with a list of vulnerabilities', () => {
beforeEach(() => { beforeEach(() => {
mock.onGet(vulnerabilitiesEndpoint).replyOnce(200, mockDataVulnerabilities); mock.onGet(vulnerabilitiesEndpoint).replyOnce(200, mockDataVulnerabilities);
vm = mountComponentWithStore(Component, { store }); vm = mountComponentWithStore(Component, { store, props });
}); });
it('should render a row for each vulnerability', done => { it('should render a row for each vulnerability', done => {
...@@ -57,4 +62,20 @@ describe('Security Dashboard Table', () => { ...@@ -57,4 +62,20 @@ describe('Security Dashboard Table', () => {
.catch(done.fail); .catch(done.fail);
}); });
}); });
describe('with no vulnerabilties', () => {
beforeEach(() => {
mock.onGet(vulnerabilitiesEndpoint).replyOnce(200, []);
vm = mountComponentWithStore(Component, { store, props });
});
it('should render the empty state', done => {
waitForPromises()
.then(() => {
expect(vm.$el.querySelector('.empty-state')).not.toBeNull();
done();
})
.catch(done.fail);
});
});
}); });
...@@ -7367,9 +7367,15 @@ msgstr "" ...@@ -7367,9 +7367,15 @@ msgstr ""
msgid "Security Reports|Dismiss vulnerability" msgid "Security Reports|Dismiss vulnerability"
msgstr "" msgstr ""
msgid "Security Reports|Learn more about setting up your dashboard"
msgstr ""
msgid "Security Reports|More info" msgid "Security Reports|More info"
msgstr "" msgstr ""
msgid "Security Reports|No Vulnerabilities"
msgstr ""
msgid "Security Reports|Revert dismissal" msgid "Security Reports|Revert dismissal"
msgstr "" msgstr ""
...@@ -7388,6 +7394,12 @@ msgstr "" ...@@ -7388,6 +7394,12 @@ msgstr ""
msgid "Security Reports|There was an error reverting this dismissal." msgid "Security Reports|There was an error reverting this dismissal."
msgstr "" msgstr ""
msgid "Security Reports|We've found no vulnerabilities for your group"
msgstr ""
msgid "Security Reports|While it's rare to have no vulnerabilities for your group, it can happen. In any event, we ask that you please double check your settings to make sure you've set up your dashboard correctly."
msgstr ""
msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities." msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
msgstr "" msgstr ""
......
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