Commit 29917364 authored by Paul Gascou-Vaillancourt's avatar Paul Gascou-Vaillancourt Committed by Enrique Alcántara

Animate the bulk actions section

parent 2702d91f
<script>
import { GlEmptyState, GlFormCheckbox } from '@gitlab/ui';
import { GlCollapse, GlEmptyState, GlFormCheckbox } from '@gitlab/ui';
import { mapActions, mapState, mapGetters } from 'vuex';
import Pagination from '~/vue_shared/components/pagination_links.vue';
import SecurityDashboardTableRow from './security_dashboard_table_row.vue';
......@@ -8,6 +8,7 @@ import SelectionSummary from './selection_summary_vuex.vue';
export default {
name: 'SecurityDashboardTable',
components: {
GlCollapse,
GlEmptyState,
GlFormCheckbox,
Pagination,
......@@ -61,7 +62,9 @@ export default {
<template>
<div class="ci-table js-security-dashboard-table" data-qa-selector="security_report_content">
<selection-summary v-if="isSelectingVulnerabilities" />
<gl-collapse :visible="isSelectingVulnerabilities" data-testid="selection-summary-collapse">
<selection-summary />
</gl-collapse>
<div class="gl-responsive-table-row table-row-header gl-bg-gray-50 text-2 px-2" role="row">
<div class="table-section section-5">
<gl-form-checkbox
......
<script>
import { GlButton, GlAlert } from '@gitlab/ui';
import { GlCollapse, GlButton, GlAlert } from '@gitlab/ui';
import vulnerabilityStateMutations from 'ee/security_dashboard/graphql/mutate_vulnerability_state';
import { __, s__, n__ } from '~/locale';
import toast from '~/vue_shared/plugins/global_toast';
......@@ -9,6 +9,7 @@ import StatusDropdown from './status_dropdown.vue';
export default {
name: 'SelectionSummary',
components: {
GlCollapse,
GlButton,
GlAlert,
StatusDropdown,
......@@ -18,6 +19,11 @@ export default {
type: Array,
required: true,
},
visible: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
......@@ -97,30 +103,36 @@ export default {
</script>
<template>
<div class="card gl-z-index-2!" :class="{ 'with-error': Boolean(updateErrorText) }">
<gl-alert v-if="updateErrorText" variant="danger" :dismissible="false">
{{ updateErrorText }}
</gl-alert>
<gl-collapse
:visible="visible"
class="selection-summary gl-z-index-2!"
data-testid="selection-summary-collapse"
>
<div class="card" :class="{ 'with-error': Boolean(updateErrorText) }">
<gl-alert v-if="updateErrorText" variant="danger" :dismissible="false">
{{ updateErrorText }}
</gl-alert>
<form class="card-body gl-display-flex gl-align-items-center" @submit.prevent="handleSubmit">
<div
class="gl-line-height-0 gl-border-r-solid gl-border-gray-100 gl-pr-6 gl-border-1 gl-h-7 gl-display-flex gl-align-items-center"
>
<span
><b>{{ selectedVulnerabilitiesCount }}</b> {{ $options.i18n.selected }}</span
<form class="card-body gl-display-flex gl-align-items-center" @submit.prevent="handleSubmit">
<div
class="gl-line-height-0 gl-border-r-solid gl-border-gray-100 gl-pr-6 gl-border-1 gl-h-7 gl-display-flex gl-align-items-center"
>
</div>
<div class="gl-flex-fill-1 gl-ml-6 gl-mr-4">
<status-dropdown @change="handleStatusDropdownChange" />
</div>
<template v-if="shouldShowActionButtons">
<gl-button type="button" class="gl-mr-4" @click="resetSelected">
{{ $options.i18n.cancel }}
</gl-button>
<gl-button type="submit" category="primary" variant="confirm">
{{ $options.i18n.changeStatus }}
</gl-button>
</template>
</form>
</div>
<span
><b>{{ selectedVulnerabilitiesCount }}</b> {{ $options.i18n.selected }}</span
>
</div>
<div class="gl-flex-fill-1 gl-ml-6 gl-mr-4">
<status-dropdown @change="handleStatusDropdownChange" />
</div>
<template v-if="shouldShowActionButtons">
<gl-button type="button" class="gl-mr-4" @click="resetSelected">
{{ $options.i18n.cancel }}
</gl-button>
<gl-button type="submit" category="primary" variant="confirm">
{{ $options.i18n.changeStatus }}
</gl-button>
</template>
</form>
</div>
</gl-collapse>
</template>
......@@ -305,8 +305,8 @@ export default {
<template>
<div class="vulnerability-list">
<selection-summary
v-if="shouldShowSelectionSummary"
:selected-vulnerabilities="Object.values(selectedVulnerabilities)"
:visible="shouldShowSelectionSummary"
@cancel-selection="deselectAllVulnerabilities"
@vulnerability-updated="deselectVulnerability"
/>
......
......@@ -95,7 +95,7 @@ $selection-summary-with-error-height: 118px;
// Due to position: sticky not being supported on Chrome (https://caniuse.com/#feat=css-sticky),
// the property is assigned to the th element as a workaround
.card,
.selection-summary,
thead th {
position: -webkit-sticky;
position: sticky;
......
---
title: Adds an expand/collapse transition when showing/hiding the bulk action section in the vulnerability lists.
merge_request: 60944
author:
type: changed
......@@ -4,7 +4,6 @@ import Vuex from 'vuex';
import SecurityDashboardTable from 'ee/security_dashboard/components/security_dashboard_table.vue';
import SecurityDashboardTableRow from 'ee/security_dashboard/components/security_dashboard_table_row.vue';
import SelectionSummary from 'ee/security_dashboard/components/selection_summary.vue';
import createStore from 'ee/security_dashboard/store';
import {
......@@ -12,6 +11,7 @@ import {
RECEIVE_VULNERABILITIES_SUCCESS,
REQUEST_VULNERABILITIES,
} from 'ee/security_dashboard/store/modules/vulnerabilities/mutation_types';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import mockDataVulnerabilities from '../store/modules/vulnerabilities/data/mock_data_vulnerabilities';
......@@ -25,7 +25,7 @@ describe('Security Dashboard Table', () => {
beforeEach(() => {
store = createStore();
wrapper = shallowMount(SecurityDashboardTable, {
wrapper = shallowMountExtended(SecurityDashboardTable, {
localVue,
store,
});
......@@ -37,6 +37,7 @@ describe('Security Dashboard Table', () => {
});
const findCheckbox = () => wrapper.find(GlFormCheckbox);
const findSelectionSummaryCollapse = () => wrapper.findByTestId('selection-summary-collapse');
describe('while loading', () => {
beforeEach(() => {
......@@ -63,7 +64,7 @@ describe('Security Dashboard Table', () => {
});
it('should not show the multi select box', () => {
expect(wrapper.find(SelectionSummary).exists()).toBe(false);
expect(findSelectionSummaryCollapse().attributes('visible')).toBeFalsy();
});
it('should show the select all as unchecked', () => {
......@@ -76,7 +77,7 @@ describe('Security Dashboard Table', () => {
});
it('should show the multi select box', () => {
expect(wrapper.find(SelectionSummary).exists()).toBe(true);
expect(findSelectionSummaryCollapse().attributes('visible')).toBe('true');
});
it('should show the select all as checked', () => {
......
import { GlDeprecatedSkeletonLoading as GlSkeletonLoading, GlTable, GlTruncate } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { capitalize } from 'lodash';
import DashboardHasNoVulnerabilities from 'ee/security_dashboard/components/empty_states/dashboard_has_no_vulnerabilities.vue';
import FiltersProducedNoResults from 'ee/security_dashboard/components/empty_states/filters_produced_no_results.vue';
......@@ -9,35 +8,33 @@ import VulnerabilityCommentIcon from 'ee/security_dashboard/components/vulnerabi
import VulnerabilityList from 'ee/security_dashboard/components/vulnerability_list.vue';
import RemediatedBadge from 'ee/vulnerabilities/components/remediated_badge.vue';
import { trimText } from 'helpers/text_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { generateVulnerabilities, vulnerabilities } from './mock_data';
describe('Vulnerability list component', () => {
let wrapper;
const createWrapper = ({ props = {}, listeners, provide = {} } = {}) => {
return extendedWrapper(
mount(VulnerabilityList, {
propsData: {
vulnerabilities: [],
...props,
},
stubs: {
GlPopover: true,
},
listeners,
provide: () => ({
noVulnerabilitiesSvgPath: '#',
dashboardDocumentation: '#',
emptyStateSvgPath: '#',
notEnabledScannersHelpPath: '#',
noPipelineRunScannersHelpPath: '#',
hasVulnerabilities: true,
hasJiraVulnerabilitiesIntegrationEnabled: false,
...provide,
}),
return mountExtended(VulnerabilityList, {
propsData: {
vulnerabilities: [],
...props,
},
stubs: {
GlPopover: true,
},
listeners,
provide: () => ({
noVulnerabilitiesSvgPath: '#',
dashboardDocumentation: '#',
emptyStateSvgPath: '#',
notEnabledScannersHelpPath: '#',
noPipelineRunScannersHelpPath: '#',
hasVulnerabilities: true,
hasJiraVulnerabilitiesIntegrationEnabled: false,
...provide,
}),
);
});
};
const locationText = ({ file, startLine }) => `${file}:${startLine}`;
......@@ -140,14 +137,14 @@ describe('Vulnerability list component', () => {
});
it('should not show the selection summary if no vulnerabilities are selected', () => {
expect(findSelectionSummary().exists()).toBe(false);
expect(findSelectionSummary().props('visible')).toBe(false);
});
it('should show the selection summary when a checkbox is selected', async () => {
findDataCell('vulnerability-checkbox').setChecked(true);
await wrapper.vm.$nextTick();
expect(findSelectionSummary().exists()).toBe(true);
expect(findSelectionSummary().props('visible')).toBe(true);
});
it('should sync selected vulnerabilities when the vulnerability list is updated', async () => {
......@@ -161,7 +158,7 @@ describe('Vulnerability list component', () => {
await wrapper.vm.$nextTick();
expect(findSelectionSummary().exists()).toBe(false);
expect(findSelectionSummary().props('visible')).toBe(false);
});
it('should uncheck a selected vulnerability after the vulnerability is updated', async () => {
......
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