diff --git a/ee/app/assets/javascripts/dependencies/components/app.vue b/ee/app/assets/javascripts/dependencies/components/app.vue index 55a044069b38257cd605f26a52991c850e5f1e9a..dfa4e5be2ea3e9f4dc52a8effdb8f9c42e675708 100644 --- a/ee/app/assets/javascripts/dependencies/components/app.vue +++ b/ee/app/assets/javascripts/dependencies/components/app.vue @@ -3,6 +3,7 @@ import { mapActions, mapGetters, mapState } from 'vuex'; import { GlBadge, GlEmptyState, + GlIcon, GlLoadingIcon, GlSprintf, GlTab, @@ -11,7 +12,7 @@ import { GlDeprecatedButton, } from '@gitlab/ui'; import { __ } from '~/locale'; -import Icon from '~/vue_shared/components/icon.vue'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import DependenciesActions from './dependencies_actions.vue'; import DependencyListIncompleteAlert from './dependency_list_incomplete_alert.vue'; import DependencyListJobFailedAlert from './dependency_list_job_failed_alert.vue'; @@ -24,6 +25,7 @@ export default { components: { DependenciesActions, GlBadge, + GlIcon, GlEmptyState, GlLoadingIcon, GlSprintf, @@ -33,9 +35,9 @@ export default { GlDeprecatedButton, DependencyListIncompleteAlert, DependencyListJobFailedAlert, - Icon, PaginatedDependenciesTable, }, + mixins: [glFeatureFlagsMixin()], props: { endpoint: { type: String, @@ -157,36 +159,47 @@ export default { @dismiss="dismissJobFailedAlert" /> -
-

- {{ __('Dependencies') }} - - - -

-

- - - - - - {{ generatedAtTimeAgo }} - -

+
+
+

+ {{ __('Dependencies') }} + + + +

+

+ + + + + + {{ generatedAtTimeAgo }} + +

+
+
- +
+ +
+ + +import { cloneDeep } from 'lodash'; +import { GlBadge, GlIcon, GlLink, GlNewButton, GlSkeletonLoading, GlTable } from '@gitlab/ui'; import { s__ } from '~/locale'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import DependenciesTableRow from './dependencies_table_row.vue'; +import DependencyLicenseLinks from './dependency_license_links.vue'; +import DependencyVulnerabilities from './dependency_vulnerabilities.vue'; + +const tdClass = (value, key, item) => { + const classes = []; + + // Don't draw a border between a row and its `row-details` slot + // eslint-disable-next-line no-underscore-dangle + if (item._showDetails) { + classes.push('border-bottom-0'); + } + + if (key === 'isVulnerable') { + classes.push('text-right'); + } + + return classes; +}; export default { name: 'DependenciesTable', components: { DependenciesTableRow, + DependencyLicenseLinks, + DependencyVulnerabilities, + GlBadge, + GlIcon, + GlLink, + GlNewButton, + GlSkeletonLoading, + GlTable, }, + mixins: [glFeatureFlagsMixin()], props: { dependencies: { type: Array, @@ -27,13 +57,112 @@ export default { { className: 'section-15', label: s__('Dependencies|License') }, ]; - return { tableSections }; + return { + localDependencies: this.transformDependenciesForUI(this.dependencies), + tableSections, + }; + }, + computed: { + anyDependencyHasVulnerabilities() { + return this.localDependencies.some(({ vulnerabilities }) => vulnerabilities.length > 0); + }, + }, + watch: { + dependencies(dependencies) { + this.localDependencies = this.transformDependenciesForUI(dependencies); + }, + }, + methods: { + // The GlTable component mutates the `_showDetails` property on items + // passed to it in order to track the visibilty of each row's `row-details` + // slot. So, create a deep clone of them here to avoid mutating the + // `dependencies` prop. + transformDependenciesForUI(dependencies) { + return cloneDeep(dependencies); + }, }, + fields: [ + { key: 'component', label: s__('Dependencies|Component'), tdClass }, + { key: 'packager', label: s__('Dependencies|Packager'), tdClass }, + { key: 'location', label: s__('Dependencies|Location'), tdClass }, + { key: 'license', label: s__('Dependencies|License'), tdClass }, + { key: 'isVulnerable', label: '', tdClass }, + ], + DEPENDENCIES_PER_PAGE: 20, };