Commit dde9f6e4 authored by Daniel Tian's avatar Daniel Tian Committed by Savas Vedova

Add indeterminate state to select all checkbox on vulnerability report

Changelog: changed
MR: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79398
EE: true
parent ba10dd4e
......@@ -98,6 +98,10 @@ export default {
(v) => v.scanner?.vendor !== 'GitLab' && v.scanner?.vendor !== '',
);
},
hasSelectedSomeVulnerabilities() {
// Whether the user has selected at least 1, but not all vulnerabilities.
return this.numOfSelectedVulnerabilities > 0 && !this.hasSelectedAllVulnerabilities;
},
hasSelectedAllVulnerabilities() {
if (!this.vulnerabilities.length) {
return false;
......@@ -330,6 +334,7 @@ export default {
class="gl-m-0"
data-testid="vulnerability-checkbox-all"
:checked="hasSelectedAllVulnerabilities"
:indeterminate="hasSelectedSomeVulnerabilities"
@change="toggleAllVulnerabilities"
/>
</template>
......
......@@ -100,6 +100,10 @@ export default {
(v) => v.scanner?.vendor !== 'GitLab' && v.scanner?.vendor !== '',
);
},
hasSelectedSomeVulnerabilities() {
// Whether the user has selected at least 1, but not all vulnerabilities.
return this.numOfSelectedVulnerabilities > 0 && !this.hasSelectedAllVulnerabilities;
},
hasSelectedAllVulnerabilities() {
if (!this.vulnerabilities.length) {
return false;
......@@ -282,6 +286,7 @@ export default {
class="gl-display-table-cell"
data-testid="vulnerability-checkbox-all"
:checked="hasSelectedAllVulnerabilities"
:indeterminate="hasSelectedSomeVulnerabilities"
@change="toggleAllVulnerabilities"
/>
</template>
......
import { GlDeprecatedSkeletonLoading as GlSkeletonLoading, GlTable, GlTruncate } from '@gitlab/ui';
import {
GlDeprecatedSkeletonLoading as GlSkeletonLoading,
GlFormCheckbox,
GlTable,
GlTruncate,
} from '@gitlab/ui';
import { capitalize } from 'lodash';
import { nextTick } from 'vue';
import DashboardHasNoVulnerabilities from 'ee/security_dashboard/components/shared/empty_states/dashboard_has_no_vulnerabilities.vue';
......@@ -12,6 +17,7 @@ import FalsePositiveBadge from 'ee/vulnerabilities/components/false_positive_bad
import RemediatedBadge from 'ee/vulnerabilities/components/remediated_badge.vue';
import { trimText } from 'helpers/text_helper';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { stubComponent } from 'helpers/stub_component';
import { generateVulnerabilities, vulnerabilities } from '../mock_data';
describe('Vulnerability list component', () => {
......@@ -66,6 +72,8 @@ describe('Vulnerability list component', () => {
const findDashboardHasNoVulnerabilities = () =>
wrapper.findComponent(DashboardHasNoVulnerabilities);
const findVendorNames = () => wrapper.findByTestId('vulnerability-vendor');
const findCheckAllCheckbox = () => wrapper.findByTestId('vulnerability-checkbox-all');
const findAllRowCheckboxes = () => wrapper.findAllByTestId('vulnerability-checkbox');
afterEach(() => {
wrapper.destroy();
......@@ -636,6 +644,54 @@ describe('Vulnerability list component', () => {
});
});
describe('select all checkbox', () => {
it('will toggle between selecting all and deselecting all vulnerabilities', async () => {
const getChecked = () => findAllRowCheckboxes().filter((x) => x.element.checked === true);
wrapper = createWrapper({ props: { vulnerabilities } });
// Sanity check to ensure that everything starts off unchecked.
expect(getChecked()).toHaveLength(0);
await findCheckAllCheckbox().trigger('click');
// First click should select all rows.
expect(getChecked()).toHaveLength(vulnerabilities.length);
await findCheckAllCheckbox().trigger('click');
// Second click should un-select all rows.
expect(getChecked()).toHaveLength(0);
});
it('will toggle the indeterminate state when some but not all vulnerabilities are selected', async () => {
const expectIndeterminateState = (state) =>
expect(findCheckAllCheckbox().props('indeterminate')).toBe(state);
wrapper = createWrapper({
props: { vulnerabilities },
stubs: { GlFormCheckbox: stubComponent(GlFormCheckbox, { props: ['indeterminate'] }) },
});
// We start off with no items selected, so no indeterminate state.
expectIndeterminateState(false);
await findRow(1).trigger('click');
// When we go from 0 to 1 item selected, indeterminate state should be true.
expectIndeterminateState(true);
await findRow(1).trigger('click');
// When we go from 1 to 0 items selected, indeterminate state should be false.
expectIndeterminateState(false);
// Check all items.
findCheckAllCheckbox().trigger('click');
// When all the items are selected, indeterminate state should be false.
expectIndeterminateState(false);
await findRow(1).trigger('click');
// When we uncheck an item when all items are selected, indeterminate state should be true.
expectIndeterminateState(true);
});
});
describe('when it is the pipeline dashboard', () => {
beforeEach(() => {
wrapper = createWrapper({
......
import { GlSkeletonLoading, GlTable, GlTruncate } from '@gitlab/ui';
import { GlSkeletonLoading, GlTable, GlTruncate, GlFormCheckbox } from '@gitlab/ui';
import { capitalize } from 'lodash';
import { Portal } from 'portal-vue';
import { nextTick } from 'vue';
......@@ -14,6 +14,7 @@ import RemediatedBadge from 'ee/vulnerabilities/components/remediated_badge.vue'
import { trimText } from 'helpers/text_helper';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { FIELDS } from 'ee/security_dashboard/components/shared/vulnerability_report/constants';
import { stubComponent } from 'helpers/stub_component';
import { generateVulnerabilities, vulnerabilities } from '../../mock_data';
const { DETECTED, STATUS, SEVERITY, DESCRIPTION, IDENTIFIER, TOOL, ACTIVITY } = FIELDS;
......@@ -73,6 +74,8 @@ describe('Vulnerability list component', () => {
const findDashboardHasNoVulnerabilities = () =>
wrapper.findComponent(DashboardHasNoVulnerabilities);
const findVendorNames = () => wrapper.findByTestId('vulnerability-vendor');
const findCheckAllCheckbox = () => wrapper.findByTestId('vulnerability-checkbox-all');
const findAllRowCheckboxes = () => wrapper.findAllByTestId('vulnerability-checkbox');
afterEach(() => {
wrapper.destroy();
......@@ -612,6 +615,54 @@ describe('Vulnerability list component', () => {
});
});
describe('select all checkbox', () => {
it('will toggle between selecting all and deselecting all vulnerabilities', async () => {
const getChecked = () => findAllRowCheckboxes().filter((x) => x.element.checked === true);
createWrapper({ props: { vulnerabilities } });
// Sanity check to ensure that everything starts off unchecked.
expect(getChecked()).toHaveLength(0);
await findCheckAllCheckbox().trigger('click');
// First click should select all rows.
expect(getChecked()).toHaveLength(vulnerabilities.length);
await findCheckAllCheckbox().trigger('click');
// Second click should un-select all rows.
expect(getChecked()).toHaveLength(0);
});
it('will toggle the indeterminate state when some but not all vulnerabilities are selected', async () => {
const expectIndeterminateState = (state) =>
expect(findCheckAllCheckbox().props('indeterminate')).toBe(state);
createWrapper({
props: { vulnerabilities },
stubs: { GlFormCheckbox: stubComponent(GlFormCheckbox, { props: ['indeterminate'] }) },
});
// We start off with no items selected, so no indeterminate state.
expectIndeterminateState(false);
await findRow(1).trigger('click');
// When we go from 0 to 1 item selected, indeterminate state should be true.
expectIndeterminateState(true);
await findRow(1).trigger('click');
// When we go from 1 to 0 items selected, indeterminate state should be false.
expectIndeterminateState(false);
// Check all items.
findCheckAllCheckbox().trigger('click');
// When all the items are selected, indeterminate state should be false.
expectIndeterminateState(false);
await findRow(1).trigger('click');
// When we uncheck an item when all items are selected, indeterminate state should be true.
expectIndeterminateState(true);
});
});
describe('fields prop', () => {
it('shows the expected columns in the table', () => {
const fields = [STATUS, SEVERITY];
......
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