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 { ...@@ -117,13 +117,13 @@ export default {
<section v-else> <section v-else>
<dependency-list-incomplete-alert <dependency-list-incomplete-alert
v-if="isIncomplete && !isIncompleteAlertDismissed" v-if="isIncomplete && !isIncompleteAlertDismissed"
@close="dismissIncompleteListAlert" @dismiss="dismissIncompleteListAlert"
/> />
<dependency-list-job-failed-alert <dependency-list-job-failed-alert
v-if="isJobFailed && !isJobFailedAlertDismissed" v-if="isJobFailed && !isJobFailedAlertDismissed"
:job-path="reportInfo.jobPath" :job-path="reportInfo.jobPath"
@close="dismissJobFailedAlert" @dismiss="dismissJobFailedAlert"
/> />
<header class="my-3"> <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. // Limit the number of vulnerabilities to display so as to avoid jank.
// In practice, this limit will probably never be reached, since the // In practice, this limit will probably never be reached, since the
// largest number of vulnerabilities we've seen one dependency have is 20. // 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; 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> <script>
import DependencyListAlert from './dependency_list_alert.vue'; import { GlAlert } from '@gitlab/ui';
export default { export default {
name: 'DependencyListIncompleteAlert', name: 'DependencyListIncompleteAlert',
components: { components: {
DependencyListAlert, GlAlert,
}, },
data() { data() {
return { return {
...@@ -23,9 +23,9 @@ export default { ...@@ -23,9 +23,9 @@ export default {
</script> </script>
<template> <template>
<dependency-list-alert <gl-alert
type="warning" variant="warning"
:header-text="s__('Dependencies|Unsupported file(s) detected')" :title="s__('Dependencies|Unsupported file(s) detected')"
v-on="$listeners" v-on="$listeners"
> >
<p> <p>
...@@ -35,8 +35,8 @@ export default { ...@@ -35,8 +35,8 @@ export default {
) )
}} }}
</p> </p>
<ul> <ul class="mb-0">
<li v-for="file in dependencyFiles" :key="file">{{ file }}</li> <li v-for="file in dependencyFiles" :key="file">{{ file }}</li>
</ul> </ul>
</dependency-list-alert> </gl-alert>
</template> </template>
<script> <script>
import { GlButton } from '@gitlab/ui'; import { GlAlert } from '@gitlab/ui';
import { sprintf, s__ } from '~/locale'; import { __, sprintf, s__ } from '~/locale';
import DependencyListAlert from './dependency_list_alert.vue';
export default { export default {
name: 'DependencyListJobFailedAlert', name: 'DependencyListJobFailedAlert',
components: { components: {
DependencyListAlert, GlAlert,
GlButton,
}, },
props: { props: {
jobPath: { jobPath: {
...@@ -16,29 +14,34 @@ export default { ...@@ -16,29 +14,34 @@ export default {
default: '', default: '',
}, },
}, },
data() { computed: {
return { buttonProps() {
message: sprintf( return this.jobPath
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.', secondaryButtonText: __('View job'),
), secondaryButtonLink: this.jobPath,
{ codeStartTag: '<code>', codeEndTag: '</code>' }, }
false, : {};
), },
};
}, },
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> </script>
<template> <template>
<dependency-list-alert <gl-alert
type="danger" variant="danger"
:header-text="s__('Dependencies|Job failed to generate the dependency list')" :title="s__('Dependencies|Job failed to generate the dependency list')"
v-bind="buttonProps"
@dismiss="$emit('close')"
v-on="$listeners" v-on="$listeners"
> >
<p v-html="message"></p> <span v-html="$options.message"></span>
<gl-button v-if="jobPath" :href="jobPath" class="btn-inverted btn-danger mb-2"> </gl-alert>
{{ __('View job') }}
</gl-button>
</dependency-list-alert>
</template> </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 // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DependencyListIncompleteAlert component matches the snapshot 1`] = ` exports[`DependencyListIncompleteAlert component matches the snapshot 1`] = `
<dependencylistalert-stub <glalert-stub
headertext="Unsupported file(s) detected" dismissible="true"
type="warning" dismisslabel="Dismiss"
primarybuttonlink=""
primarybuttontext=""
secondarybuttonlink=""
secondarybuttontext=""
title="Unsupported file(s) detected"
variant="warning"
> >
<p> <p>
...@@ -11,7 +17,9 @@ exports[`DependencyListIncompleteAlert component matches the snapshot 1`] = ` ...@@ -11,7 +17,9 @@ exports[`DependencyListIncompleteAlert component matches the snapshot 1`] = `
</p> </p>
<ul> <ul
class="mb-0"
>
<li> <li>
package-lock.json package-lock.json
</li> </li>
...@@ -34,5 +42,5 @@ exports[`DependencyListIncompleteAlert component matches the snapshot 1`] = ` ...@@ -34,5 +42,5 @@ exports[`DependencyListIncompleteAlert component matches the snapshot 1`] = `
pom.xml pom.xml
</li> </li>
</ul> </ul>
</dependencylistalert-stub> </glalert-stub>
`; `;
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DependencyListJobFailedAlert component matches the snapshot 1`] = ` exports[`DependencyListJobFailedAlert component matches the snapshot 1`] = `
<dependencylistalert-stub <glalert-stub
headertext="Job failed to generate the dependency list" dismissible="true"
type="danger" dismisslabel="Dismiss"
primarybuttonlink=""
primarybuttontext=""
secondarybuttonlink="/jobs/foo/3210"
secondarybuttontext="View job"
title="Job failed to generate the dependency list"
variant="danger"
> >
<p> <span>
The The
<code> <code>
dependency_scanning dependency_scanning
</code> </code>
job has failed and cannot generate the list. Please ensure the job is running properly and run the pipeline again. job has failed and cannot generate the list. Please ensure the job is running properly and run the pipeline again.
</p> </span>
</glalert-stub>
<glbutton-stub
class="btn-inverted btn-danger mb-2"
href="/jobs/foo/3210"
>
View job
</glbutton-stub>
</dependencylistalert-stub>
`; `;
...@@ -291,10 +291,10 @@ describe('DependenciesApp component', () => { ...@@ -291,10 +291,10 @@ describe('DependenciesApp component', () => {
it('shows both dependencies tables with the correct props', expectDependenciesTables); 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(() => { beforeEach(() => {
const alertWrapper = findJobFailedAlert(); const alertWrapper = findJobFailedAlert();
alertWrapper.vm.$emit('close'); alertWrapper.vm.$emit('dismiss');
return wrapper.vm.$nextTick(); return wrapper.vm.$nextTick();
}); });
...@@ -317,10 +317,10 @@ describe('DependenciesApp component', () => { ...@@ -317,10 +317,10 @@ describe('DependenciesApp component', () => {
it('shows both dependencies tables with the correct props', expectDependenciesTables); 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(() => { beforeEach(() => {
const alertWrapper = findIncompleteListAlert(); const alertWrapper = findIncompleteListAlert();
alertWrapper.vm.$emit('close'); alertWrapper.vm.$emit('dismiss');
return wrapper.vm.$nextTick(); 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 { 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'; import DependencyListIncompleteAlert from 'ee/dependencies/components/dependency_list_incomplete_alert.vue';
describe('DependencyListIncompleteAlert component', () => { describe('DependencyListIncompleteAlert component', () => {
...@@ -24,23 +24,23 @@ describe('DependencyListIncompleteAlert component', () => { ...@@ -24,23 +24,23 @@ describe('DependencyListIncompleteAlert component', () => {
expect(wrapper.element).toMatchSnapshot(); expect(wrapper.element).toMatchSnapshot();
}); });
describe('when the generic alert component emits a close event', () => { describe('when the GlAlert component emits a dismiss event', () => {
let closeListenerSpy; let dismissListenerSpy;
beforeEach(() => { beforeEach(() => {
closeListenerSpy = jest.fn(); dismissListenerSpy = jest.fn();
factory({ factory({
listeners: { listeners: {
close: closeListenerSpy, dismiss: dismissListenerSpy,
}, },
}); });
wrapper.find(DependencyListAlert).vm.$emit('close'); wrapper.find(GlAlert).vm.$emit('dismiss');
}); });
it('calls the given listener', () => { it('calls the given listener', () => {
expect(closeListenerSpy).toHaveBeenCalledTimes(1); expect(dismissListenerSpy).toHaveBeenCalledTimes(1);
}); });
}); });
}); });
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { createLocalVue, shallowMount } from '@vue/test-utils';
import { GlButton } from '@gitlab/ui'; import { GlAlert } from '@gitlab/ui';
import DependencyListAlert from 'ee/dependencies/components/dependency_list_alert.vue';
import DependencyListJobFailedAlert from 'ee/dependencies/components/dependency_list_job_failed_alert.vue'; import DependencyListJobFailedAlert from 'ee/dependencies/components/dependency_list_job_failed_alert.vue';
const NO_BUTTON_PROPS = {
secondaryButtonText: '',
secondaryButtonLink: '',
};
describe('DependencyListJobFailedAlert component', () => { describe('DependencyListJobFailedAlert component', () => {
let wrapper; let wrapper;
...@@ -25,16 +29,20 @@ describe('DependencyListJobFailedAlert component', () => { ...@@ -25,16 +29,20 @@ describe('DependencyListJobFailedAlert component', () => {
expect(wrapper.element).toMatchSnapshot(); expect(wrapper.element).toMatchSnapshot();
}); });
it('inludes a button button if "jobPath" is given', () => { it('inludes a button if "jobPath" is given', () => {
factory({ propsData: { jobPath: '/jobs/foo/3210' } }); 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', () => { it('does not include a button if "jobPath" is not given', () => {
factory(); factory();
expect(wrapper.find(GlButton).exists()).toBe(false); expect(wrapper.find(GlAlert).props()).toMatchObject(NO_BUTTON_PROPS);
}); });
it.each([undefined, null, ''])( it.each([undefined, null, ''])(
...@@ -42,28 +50,27 @@ describe('DependencyListJobFailedAlert component', () => { ...@@ -42,28 +50,27 @@ describe('DependencyListJobFailedAlert component', () => {
jobPath => { jobPath => {
factory({ propsData: { 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', () => { describe('when the GlAlert component emits a dismiss event', () => {
let closeListenerSpy; let dismissListenerSpy;
beforeEach(() => { beforeEach(() => {
closeListenerSpy = jest.fn(); dismissListenerSpy = jest.fn();
factory({ factory({
propsData: { jobPath: '/jobs/foo/3210' },
listeners: { listeners: {
close: closeListenerSpy, dismiss: dismissListenerSpy,
}, },
}); });
wrapper.find(DependencyListAlert).vm.$emit('close'); wrapper.find(GlAlert).vm.$emit('dismiss');
}); });
it('calls the given listener', () => { 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