Add on-demands scans index page header

This leverages the ConfigurationPageLayout component to add a header to
the on-demand scans index page. The header contains a help text and a
link to the on-demands scans form.
To make room for the link, ConfigurationPageLayout has been extended
with a new actions slot.
parent dc21858e
<script> <script>
import { GlTabs } from '@gitlab/ui'; import { GlButton, GlLink, GlSprintf, GlTabs } from '@gitlab/ui';
import { s__ } from '~/locale';
import ConfigurationPageLayout from 'ee/security_configuration/components/configuration_page_layout.vue';
import AllTab from './tabs/all.vue'; import AllTab from './tabs/all.vue';
import RunningTab from './tabs/running.vue'; import RunningTab from './tabs/running.vue';
import FinishedTab from './tabs/finished.vue'; import FinishedTab from './tabs/finished.vue';
...@@ -24,13 +26,18 @@ const TABS = { ...@@ -24,13 +26,18 @@ const TABS = {
export default { export default {
TABS, TABS,
components: { components: {
GlButton,
GlLink,
GlSprintf,
GlTabs, GlTabs,
ConfigurationPageLayout,
AllTab, AllTab,
RunningTab, RunningTab,
FinishedTab, FinishedTab,
ScheduledTab, ScheduledTab,
EmptyState, EmptyState,
}, },
inject: ['newDastScanPath', 'helpPagePath'],
data() { data() {
return { return {
activeTabIndex: 0, activeTabIndex: 0,
...@@ -57,12 +64,41 @@ export default { ...@@ -57,12 +64,41 @@ export default {
this.activeTabIndex = tabIndex; this.activeTabIndex = tabIndex;
} }
}, },
i18n: {
title: s__('OnDemandScans|On-demand scans'),
newScanButtonLabel: s__('OnDemandScans|New DAST scan'),
description: s__(
'OnDemandScans|On-demand scans run outside of DevOps cycle and find vulnerabilities in your projects. %{learnMoreLinkStart}Lean more%{learnMoreLinkEnd}.',
),
},
}; };
</script> </script>
<template> <template>
<gl-tabs v-if="hasData" v-model="activeTab"> <configuration-page-layout v-if="hasData">
<component :is="tab.component" v-for="(tab, key) in $options.TABS" :key="key" :item-count="0" /> <template #heading>
</gl-tabs> {{ $options.i18n.title }}
</template>
<template #actions>
<gl-button variant="confirm" :href="newDastScanPath" data-testid="new-scan-link">
{{ $options.i18n.newScanButtonLabel }}
</gl-button>
</template>
<template #description>
<gl-sprintf :message="$options.i18n.description">
<template #learnMoreLink="{ content }">
<gl-link :href="helpPagePath" data-testid="help-page-link">{{ content }}</gl-link>
</template>
</gl-sprintf>
</template>
<gl-tabs v-model="activeTab">
<component
:is="tab.component"
v-for="(tab, key) in $options.TABS"
:key="key"
:item-count="0"
/>
</gl-tabs>
</configuration-page-layout>
<empty-state v-else /> <empty-state v-else />
</template> </template>
<template> <template>
<article> <article>
<slot name="alert"></slot> <slot name="alert"></slot>
<header class="gl-my-5 gl-border-b-1 gl-border-b-gray-100 gl-border-b-solid"> <header
<h4> class="gl-my-5 gl-border-b-1 gl-border-b-gray-100 gl-border-b-solid gl-display-flex gl-justify-content-space-between"
<slot name="heading"></slot> >
</h4> <div>
<p> <h4 class="gl-mt-0">
<slot name="description"></slot> <slot name="heading"></slot>
</p> </h4>
<p>
<slot name="description"></slot>
</p>
</div>
<div>
<slot name="actions"></slot>
</div>
</header> </header>
<slot></slot> <slot></slot>
</article> </article>
......
import { shallowMount } from '@vue/test-utils'; import { GlTabs, GlSprintf } from '@gitlab/ui';
import { GlTabs } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import OnDemandScans from 'ee/on_demand_scans/components/on_demand_scans.vue'; import OnDemandScans from 'ee/on_demand_scans/components/on_demand_scans.vue';
import ConfigurationPageLayout from 'ee/security_configuration/components/configuration_page_layout.vue';
import { createRouter } from 'ee/on_demand_scans/router'; import { createRouter } from 'ee/on_demand_scans/router';
import AllTab from 'ee/on_demand_scans/components/tabs/all.vue'; import AllTab from 'ee/on_demand_scans/components/tabs/all.vue';
import RunningTab from 'ee/on_demand_scans/components/tabs/running.vue'; import RunningTab from 'ee/on_demand_scans/components/tabs/running.vue';
...@@ -12,7 +13,13 @@ describe('OnDemandScans', () => { ...@@ -12,7 +13,13 @@ describe('OnDemandScans', () => {
let wrapper; let wrapper;
let router; let router;
// Props
const newDastScanPath = '/on_demand_scans/new';
const helpPagePath = '/help/page/path';
// Finders // Finders
const findNewScanLink = () => wrapper.findByTestId('new-scan-link');
const findHelpPageLink = () => wrapper.findByTestId('help-page-link');
const findTabs = () => wrapper.findComponent(GlTabs); const findTabs = () => wrapper.findComponent(GlTabs);
const findAllTab = () => wrapper.findComponent(AllTab); const findAllTab = () => wrapper.findComponent(AllTab);
const findRunningTab = () => wrapper.findComponent(RunningTab); const findRunningTab = () => wrapper.findComponent(RunningTab);
...@@ -21,8 +28,16 @@ describe('OnDemandScans', () => { ...@@ -21,8 +28,16 @@ describe('OnDemandScans', () => {
const findEmptyState = () => wrapper.findComponent(EmptyState); const findEmptyState = () => wrapper.findComponent(EmptyState);
const createComponent = () => { const createComponent = () => {
wrapper = shallowMount(OnDemandScans, { wrapper = shallowMountExtended(OnDemandScans, {
router, router,
provide: {
newDastScanPath,
helpPagePath,
},
stubs: {
ConfigurationPageLayout,
GlSprintf,
},
}); });
}; };
...@@ -46,6 +61,20 @@ describe('OnDemandScans', () => { ...@@ -46,6 +61,20 @@ describe('OnDemandScans', () => {
wrapper.setData({ hasData: true }); wrapper.setData({ hasData: true });
}); });
it('renders a link to the docs', () => {
const link = findHelpPageLink();
expect(link.exists()).toBe(true);
expect(link.attributes('href')).toBe(helpPagePath);
});
it('renders a link to create a new scan', () => {
const link = findNewScanLink();
expect(link.exists()).toBe(true);
expect(link.attributes('href')).toBe(newDastScanPath);
});
it('renders the tabs if there is data', async () => { it('renders the tabs if there is data', async () => {
expect(findAllTab().exists()).toBe(true); expect(findAllTab().exists()).toBe(true);
expect(findRunningTab().exists()).toBe(true); expect(findRunningTab().exists()).toBe(true);
......
...@@ -4,15 +4,23 @@ exports[`Security Configuration Page Layout component matches the snapshot 1`] = ...@@ -4,15 +4,23 @@ exports[`Security Configuration Page Layout component matches the snapshot 1`] =
<article> <article>
Page alert Page alert
<header <header
class="gl-my-5 gl-border-b-1 gl-border-b-gray-100 gl-border-b-solid" class="gl-my-5 gl-border-b-1 gl-border-b-gray-100 gl-border-b-solid gl-display-flex gl-justify-content-space-between"
> >
<h4> <div>
Page title <h4
</h4> class="gl-mt-0"
>
Page title
</h4>
<p>
Scanner description
</p>
</div>
<p> <div>
Scanner description Action
</p> </div>
</header> </header>
<div> <div>
......
...@@ -9,6 +9,7 @@ describe('Security Configuration Page Layout component', () => { ...@@ -9,6 +9,7 @@ describe('Security Configuration Page Layout component', () => {
slots: { slots: {
alert: 'Page alert', alert: 'Page alert',
heading: 'Page title', heading: 'Page title',
actions: 'Action',
description: 'Scanner description', description: 'Scanner description',
default: '<div>form</div>', default: '<div>form</div>',
}, },
......
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