Commit f74b1569 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '11873-use-gl-alert-in-dependency-list-ee' into 'master'

Use GlAlert component in Dependency List

See merge request gitlab-org/gitlab!21760
parents 53df0d9a a1f27839
......@@ -117,13 +117,13 @@ export default {
<section v-else>
<dependency-list-incomplete-alert
v-if="isIncomplete && !isIncompleteAlertDismissed"
@close="dismissIncompleteListAlert"
@dismiss="dismissIncompleteListAlert"
/>
<dependency-list-job-failed-alert
v-if="isJobFailed && !isJobFailedAlertDismissed"
:job-path="reportInfo.jobPath"
@close="dismissJobFailedAlert"
@dismiss="dismissJobFailedAlert"
/>
<header class="my-3">
......
export const WARNING = 'warning';
export const DANGER = 'danger';
export const WARNING_ALERT_CLASS = 'warning_message';
export const DANGER_ALERT_CLASS = 'danger_message';
export const WARNING_TEXT_CLASS = 'text-warning-900';
export const DANGER_TEXT_CLASS = 'text-danger-900';
// Limit the number of vulnerabilities to display so as to avoid jank.
// In practice, this limit will probably never be reached, since the
// largest number of vulnerabilities we've seen one dependency have is 20.
// eslint-disable-next-line import/prefer-default-export
export const MAX_DISPLAYED_VULNERABILITIES_PER_DEPENDENCY = 50;
<script>
import { GlButton } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
import {
DANGER,
DANGER_ALERT_CLASS,
DANGER_TEXT_CLASS,
WARNING,
WARNING_ALERT_CLASS,
WARNING_TEXT_CLASS,
} from './constants';
export default {
name: 'DependencyListAlert',
components: {
GlButton,
Icon,
},
props: {
type: {
type: String,
required: false,
default: DANGER,
validator: value => [WARNING, DANGER].includes(value),
},
headerText: {
type: String,
required: false,
default: '',
},
},
computed: {
textClass() {
return (
{
[WARNING]: WARNING_TEXT_CLASS,
[DANGER]: DANGER_TEXT_CLASS,
}[this.type] || ''
);
},
alertClass() {
return (
{
[WARNING]: WARNING_ALERT_CLASS,
[DANGER]: DANGER_ALERT_CLASS,
}[this.type] || ''
);
},
},
};
</script>
<template>
<div :class="[alertClass, textClass]">
<gl-button
:class="['btn-blank float-right mr-1 mt-1 js-close', textClass]"
:aria-label="__('Close')"
@click="$emit('close')"
>
<icon name="close" aria-hidden="true" />
</gl-button>
<h4 v-if="headerText" :class="textClass">{{ headerText }}</h4>
<slot></slot>
</div>
</template>
<script>
import DependencyListAlert from './dependency_list_alert.vue';
import { GlAlert } from '@gitlab/ui';
export default {
name: 'DependencyListIncompleteAlert',
components: {
DependencyListAlert,
GlAlert,
},
data() {
return {
......@@ -23,9 +23,9 @@ export default {
</script>
<template>
<dependency-list-alert
type="warning"
:header-text="s__('Dependencies|Unsupported file(s) detected')"
<gl-alert
variant="warning"
:title="s__('Dependencies|Unsupported file(s) detected')"
v-on="$listeners"
>
<p>
......@@ -35,8 +35,8 @@ export default {
)
}}
</p>
<ul>
<ul class="mb-0">
<li v-for="file in dependencyFiles" :key="file">{{ file }}</li>
</ul>
</dependency-list-alert>
</gl-alert>
</template>
<script>
import { GlButton } from '@gitlab/ui';
import { sprintf, s__ } from '~/locale';
import DependencyListAlert from './dependency_list_alert.vue';
import { GlAlert } from '@gitlab/ui';
import { __, sprintf, s__ } from '~/locale';
export default {
name: 'DependencyListJobFailedAlert',
components: {
DependencyListAlert,
GlButton,
GlAlert,
},
props: {
jobPath: {
......@@ -16,29 +14,34 @@ export default {
default: '',
},
},
data() {
return {
message: sprintf(
s__(
'Dependencies|The %{codeStartTag}dependency_scanning%{codeEndTag} job has failed and cannot generate the list. Please ensure the job is running properly and run the pipeline again.',
),
{ codeStartTag: '<code>', codeEndTag: '</code>' },
false,
),
};
computed: {
buttonProps() {
return this.jobPath
? {
secondaryButtonText: __('View job'),
secondaryButtonLink: this.jobPath,
}
: {};
},
},
message: sprintf(
s__(
'Dependencies|The %{codeStartTag}dependency_scanning%{codeEndTag} job has failed and cannot generate the list. Please ensure the job is running properly and run the pipeline again.',
),
{ codeStartTag: '<code>', codeEndTag: '</code>' },
false,
),
};
</script>
<template>
<dependency-list-alert
type="danger"
:header-text="s__('Dependencies|Job failed to generate the dependency list')"
<gl-alert
variant="danger"
:title="s__('Dependencies|Job failed to generate the dependency list')"
v-bind="buttonProps"
@dismiss="$emit('close')"
v-on="$listeners"
>
<p v-html="message"></p>
<gl-button v-if="jobPath" :href="jobPath" class="btn-inverted btn-danger mb-2">
{{ __('View job') }}
</gl-button>
</dependency-list-alert>
<span v-html="$options.message"></span>
</gl-alert>
</template>
---
title: Update the alerts used in the Dependency List to follow GitLab design guidelines
merge_request: 21760
author:
type: other
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DependencyListAlert component given no props matches the snapshot 1`] = `
<div
class="danger_message text-danger-900"
>
<glbutton-stub
aria-label="Close"
class="btn-blank float-right mr-1 mt-1 js-close text-danger-900"
>
<icon-stub
aria-hidden="true"
name="close"
size="16"
/>
</glbutton-stub>
<!---->
<p>
foo
<span>
bar
</span>
</p>
</div>
`;
exports[`DependencyListAlert component given the headerText prop matches the snapshot 1`] = `
<div
class="danger_message text-danger-900"
>
<glbutton-stub
aria-label="Close"
class="btn-blank float-right mr-1 mt-1 js-close text-danger-900"
>
<icon-stub
aria-hidden="true"
name="close"
size="16"
/>
</glbutton-stub>
<h4
class="text-danger-900"
>
A header
</h4>
<p>
foo
<span>
bar
</span>
</p>
</div>
`;
exports[`DependencyListAlert component given the warning type and headerText props matches the snapshot 1`] = `
<div
class="warning_message text-warning-900"
>
<glbutton-stub
aria-label="Close"
class="btn-blank float-right mr-1 mt-1 js-close text-warning-900"
>
<icon-stub
aria-hidden="true"
name="close"
size="16"
/>
</glbutton-stub>
<h4
class="text-warning-900"
>
Some header
</h4>
<p>
foo
<span>
bar
</span>
</p>
</div>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DependencyListIncompleteAlert component matches the snapshot 1`] = `
<dependencylistalert-stub
headertext="Unsupported file(s) detected"
type="warning"
<glalert-stub
dismissible="true"
dismisslabel="Dismiss"
primarybuttonlink=""
primarybuttontext=""
secondarybuttonlink=""
secondarybuttontext=""
title="Unsupported file(s) detected"
variant="warning"
>
<p>
......@@ -11,7 +17,9 @@ exports[`DependencyListIncompleteAlert component matches the snapshot 1`] = `
</p>
<ul>
<ul
class="mb-0"
>
<li>
package-lock.json
</li>
......@@ -34,5 +42,5 @@ exports[`DependencyListIncompleteAlert component matches the snapshot 1`] = `
pom.xml
</li>
</ul>
</dependencylistalert-stub>
</glalert-stub>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DependencyListJobFailedAlert component matches the snapshot 1`] = `
<dependencylistalert-stub
headertext="Job failed to generate the dependency list"
type="danger"
<glalert-stub
dismissible="true"
dismisslabel="Dismiss"
primarybuttonlink=""
primarybuttontext=""
secondarybuttonlink="/jobs/foo/3210"
secondarybuttontext="View job"
title="Job failed to generate the dependency list"
variant="danger"
>
<p>
<span>
The
<code>
dependency_scanning
</code>
job has failed and cannot generate the list. Please ensure the job is running properly and run the pipeline again.
</p>
<glbutton-stub
class="btn-inverted btn-danger mb-2"
href="/jobs/foo/3210"
>
View job
</glbutton-stub>
</dependencylistalert-stub>
</span>
</glalert-stub>
`;
......@@ -291,10 +291,10 @@ describe('DependenciesApp component', () => {
it('shows both dependencies tables with the correct props', expectDependenciesTables);
describe('when the job failure alert emits the close event', () => {
describe('when the job failure alert emits the dismiss event', () => {
beforeEach(() => {
const alertWrapper = findJobFailedAlert();
alertWrapper.vm.$emit('close');
alertWrapper.vm.$emit('dismiss');
return wrapper.vm.$nextTick();
});
......@@ -317,10 +317,10 @@ describe('DependenciesApp component', () => {
it('shows both dependencies tables with the correct props', expectDependenciesTables);
describe('when the incomplete-list alert emits the close event', () => {
describe('when the incomplete-list alert emits the dismiss event', () => {
beforeEach(() => {
const alertWrapper = findIncompleteListAlert();
alertWrapper.vm.$emit('close');
alertWrapper.vm.$emit('dismiss');
return wrapper.vm.$nextTick();
});
......
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { WARNING } from 'ee/dependencies/components/constants';
import DependencyListAlert from 'ee/dependencies/components/dependency_list_alert.vue';
describe('DependencyListAlert component', () => {
let wrapper;
const factory = (props = {}) => {
const localVue = createLocalVue();
wrapper = shallowMount(localVue.extend(DependencyListAlert), {
localVue,
sync: false,
propsData: { ...props },
slots: {
default: '<p>foo <span>bar</span></p>',
},
});
};
afterEach(() => {
wrapper.destroy();
});
describe('given no props', () => {
beforeEach(() => {
factory();
});
it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
});
});
describe('given the warning type and headerText props', () => {
beforeEach(() => {
factory({ type: WARNING, headerText: 'Some header' });
});
it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
});
});
describe('given the headerText prop', () => {
beforeEach(() => {
factory({ headerText: 'A header' });
});
it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
});
});
describe('clicking on the close button', () => {
beforeEach(() => {
factory();
wrapper.find('.js-close').vm.$emit('click');
return wrapper.vm.$nextTick();
});
it('emits the close event', () => {
expect(wrapper.emitted().close.length).toBe(1);
});
});
});
import { createLocalVue, shallowMount } from '@vue/test-utils';
import DependencyListAlert from 'ee/dependencies/components/dependency_list_alert.vue';
import { GlAlert } from '@gitlab/ui';
import DependencyListIncompleteAlert from 'ee/dependencies/components/dependency_list_incomplete_alert.vue';
describe('DependencyListIncompleteAlert component', () => {
......@@ -24,23 +24,23 @@ describe('DependencyListIncompleteAlert component', () => {
expect(wrapper.element).toMatchSnapshot();
});
describe('when the generic alert component emits a close event', () => {
let closeListenerSpy;
describe('when the GlAlert component emits a dismiss event', () => {
let dismissListenerSpy;
beforeEach(() => {
closeListenerSpy = jest.fn();
dismissListenerSpy = jest.fn();
factory({
listeners: {
close: closeListenerSpy,
dismiss: dismissListenerSpy,
},
});
wrapper.find(DependencyListAlert).vm.$emit('close');
wrapper.find(GlAlert).vm.$emit('dismiss');
});
it('calls the given listener', () => {
expect(closeListenerSpy).toHaveBeenCalledTimes(1);
expect(dismissListenerSpy).toHaveBeenCalledTimes(1);
});
});
});
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { GlButton } from '@gitlab/ui';
import DependencyListAlert from 'ee/dependencies/components/dependency_list_alert.vue';
import { GlAlert } from '@gitlab/ui';
import DependencyListJobFailedAlert from 'ee/dependencies/components/dependency_list_job_failed_alert.vue';
const NO_BUTTON_PROPS = {
secondaryButtonText: '',
secondaryButtonLink: '',
};
describe('DependencyListJobFailedAlert component', () => {
let wrapper;
......@@ -25,16 +29,20 @@ describe('DependencyListJobFailedAlert component', () => {
expect(wrapper.element).toMatchSnapshot();
});
it('inludes a button button if "jobPath" is given', () => {
factory({ propsData: { jobPath: '/jobs/foo/3210' } });
it('inludes a button if "jobPath" is given', () => {
const jobPath = '/jobs/foo/3210';
factory({ propsData: { jobPath } });
expect(wrapper.find(GlButton).exists()).toBe(true);
expect(wrapper.find(GlAlert).props()).toMatchObject({
secondaryButtonText: 'View job',
secondaryButtonLink: jobPath,
});
});
it('does not include a button if "jobPath" is not given', () => {
factory();
expect(wrapper.find(GlButton).exists()).toBe(false);
expect(wrapper.find(GlAlert).props()).toMatchObject(NO_BUTTON_PROPS);
});
it.each([undefined, null, ''])(
......@@ -42,28 +50,27 @@ describe('DependencyListJobFailedAlert component', () => {
jobPath => {
factory({ propsData: { jobPath } });
expect(wrapper.find(GlButton).exists()).toBe(false);
expect(wrapper.find(GlAlert).props()).toMatchObject(NO_BUTTON_PROPS);
},
);
describe('when the generic alert component emits a close event', () => {
let closeListenerSpy;
describe('when the GlAlert component emits a dismiss event', () => {
let dismissListenerSpy;
beforeEach(() => {
closeListenerSpy = jest.fn();
dismissListenerSpy = jest.fn();
factory({
propsData: { jobPath: '/jobs/foo/3210' },
listeners: {
close: closeListenerSpy,
dismiss: dismissListenerSpy,
},
});
wrapper.find(DependencyListAlert).vm.$emit('close');
wrapper.find(GlAlert).vm.$emit('dismiss');
});
it('calls the given listener', () => {
expect(closeListenerSpy).toHaveBeenCalledTimes(1);
expect(dismissListenerSpy).toHaveBeenCalledTimes(1);
});
});
});
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