Commit 571fcbff authored by Jacques Erasmus's avatar Jacques Erasmus

Merge branch 'add_simple_multi_select' into 'master'

Add policy rule multi select for when the number of items is constant

See merge request gitlab-org/gitlab!79571
parents f9b5f81b fafb7d33
<script>
import { GlDropdown, GlDropdownItem, GlTruncate } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
const INDEX_NOT_FOUND = -1;
const NO_ITEM_SELECTED = 0;
const ONE_ITEM_SELECTED = 1;
export default {
components: {
GlDropdown,
GlDropdownItem,
GlTruncate,
},
props: {
itemTypeName: {
type: String,
required: true,
},
items: {
type: Object,
required: true,
},
value: {
type: Array,
required: false,
default: () => [],
},
},
data() {
return {
selected: [...this.value],
};
},
computed: {
text() {
switch (this.selected.length) {
case this.itemsKeys.length:
return sprintf(
this.$options.i18n.allSelectedLabel,
{ itemTypeName: this.itemTypeName },
false,
);
case NO_ITEM_SELECTED:
return sprintf(
this.$options.i18n.selectedItemsLabel,
{
itemTypeName: this.itemTypeName,
},
false,
);
case ONE_ITEM_SELECTED:
return this.items[this.selected[0]];
default:
return sprintf(this.$options.i18n.multipleSelectedLabel, {
firstLabel: this.items[this.selected[0]],
numberOfAdditionalLabels: this.selected.length - 1,
});
}
},
areAllSelected() {
return this.itemsKeys.length === this.selected.length;
},
itemsKeys() {
return Object.keys(this.items);
},
},
methods: {
setAllSelected() {
this.selected = this.areAllSelected ? [] : [...this.itemsKeys];
this.$emit('input', this.selected);
},
setSelected(item) {
const position = this.selected.indexOf(item);
if (position === INDEX_NOT_FOUND) {
this.selected.push(item);
} else {
this.selected.splice(position, 1);
}
this.$emit('input', this.selected);
},
isSelected(item) {
return this.selected.includes(item);
},
},
i18n: {
multipleSelectedLabel: s__(
'PolicyRuleMultiSelect|%{firstLabel} +%{numberOfAdditionalLabels} more',
),
selectAllLabel: s__('PolicyRuleMultiSelect|Select all'),
selectedItemsLabel: s__('PolicyRuleMultiSelect|Select %{itemTypeName}'),
allSelectedLabel: s__('PolicyRuleMultiSelect|All %{itemTypeName}'),
},
ALL_KEY: 'all',
};
</script>
<template>
<gl-dropdown :text="text">
<gl-dropdown-item
:key="$options.ALL_KEY"
is-check-item
:is-checked="areAllSelected"
data-testid="all-items-selected"
@click.native.capture.stop="setAllSelected"
>
{{ $options.i18n.selectAllLabel }}
</gl-dropdown-item>
<gl-dropdown-item
v-for="(label, key) in items"
:key="key"
is-check-item
:is-checked="isSelected(key)"
@click.native.capture.stop="setSelected(key)"
>
<gl-truncate :text="label" />
</gl-dropdown-item>
</gl-dropdown>
</template>
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import PolicyRuleMultiSelect from 'ee/threat_monitoring/components/policy_rule_multi_select.vue';
const items = { start: 'Start now', middle: 'Almost there', end: 'Done' };
const itemsKeys = Object.keys(items);
const itemsLength = itemsKeys.length;
const itemsLengthIncludingSelectAll = itemsLength + 1;
// +1 due to the item Select all
describe('Policy Rule Multi Select', () => {
let wrapper;
const createComponent = (props = {}) => {
wrapper = shallowMount(PolicyRuleMultiSelect, {
propsData: {
itemTypeName: 'demo items',
items,
value: [],
...props,
},
});
};
afterEach(() => {
wrapper.destroy();
});
beforeEach(() => {
createComponent();
});
const findDropdown = () => wrapper.findComponent(GlDropdown);
const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
const findAllSelectedItem = () => wrapper.find('[data-testid="all-items-selected"]');
describe('Initialization', () => {
it('renders dropdown', () => {
expect(findDropdown().exists()).toBe(true);
});
it('renders text based on itemTypeName property', () => {
expect(findDropdown().props('text')).toBe('Select demo items');
});
describe('Without any selected item', () => {
it('does not have any selected item', () => {
expect(findDropdownItems().filter((element) => element.props('isChecked'))).toHaveLength(0);
});
it('displays text related to the performing of a selection', () => {
expect(findDropdown().props('text')).toBe('Select demo items');
});
it('does not have select all item selected', () => {
expect(findAllSelectedItem().props('isChecked')).toBe(false);
});
});
describe('With one selected item', () => {
const expectedKey = 'start';
const expectedValue = items[expectedKey];
beforeEach(() => {
createComponent({ value: [expectedKey] });
});
it('has one item selected', () => {
expect(findDropdownItems().filter((element) => element.props('isChecked'))).toHaveLength(1);
});
it('displays text related to the selected item', () => {
expect(findDropdown().props('text')).toBe(expectedValue);
});
it('does not have select all item selected', () => {
expect(findAllSelectedItem().props('isChecked')).toBe(false);
});
});
describe('With multiple selected items', () => {
const expectedKeys = ['start', 'middle'];
const expectedLength = expectedKeys.length;
const expectedValue = 'Start now +1 more';
beforeEach(() => {
createComponent({ value: expectedKeys });
});
it('has multiple items selected', () => {
expect(findDropdownItems().filter((element) => element.props('isChecked'))).toHaveLength(
expectedLength,
);
});
it('displays text related to the first selected item followed by the number of additional items', () => {
expect(findDropdown().props('text')).toBe(expectedValue);
});
it('does not have select all item selected', () => {
expect(findAllSelectedItem().props('isChecked')).toBe(false);
});
});
describe('With all selected items', () => {
const expectedValue = 'All demo items';
beforeEach(() => {
createComponent({ value: itemsKeys });
});
it('has all items selected', () => {
expect(findDropdownItems().filter((element) => element.props('isChecked'))).toHaveLength(
itemsLengthIncludingSelectAll,
);
});
it('displays text related to all items being selected', () => {
expect(findDropdown().props('text')).toBe(expectedValue);
});
it('has select all item selected', () => {
expect(findAllSelectedItem().props('isChecked')).toBe(true);
});
});
});
it('has all items selected after select all is checked', async () => {
const allSelectItem = findAllSelectedItem();
const allDropdownItems = findDropdownItems();
expect(allDropdownItems.filter((element) => element.props('isChecked'))).toHaveLength(0);
await allSelectItem.trigger('click');
expect(allDropdownItems.filter((element) => element.props('isChecked'))).toHaveLength(
itemsLengthIncludingSelectAll,
);
expect(wrapper.emitted().input).toEqual([[itemsKeys]]);
});
it('has all items unselected after select all is unchecked', async () => {
createComponent({ value: itemsKeys });
const allSelectItem = findAllSelectedItem();
const allDropdownItems = findDropdownItems();
expect(allDropdownItems.filter((element) => element.props('isChecked'))).toHaveLength(
itemsLengthIncludingSelectAll,
);
await allSelectItem.trigger('click');
expect(allDropdownItems.filter((element) => element.props('isChecked'))).toHaveLength(0);
expect(wrapper.emitted().input).toEqual([[[]]]);
});
it('has an item selected after it is checked', async () => {
const expectedKey = 'end';
const expectedValue = items[expectedKey];
const dropdownItemsWithDone = findDropdownItems().filter((element) =>
element.html().includes(expectedValue),
);
expect(dropdownItemsWithDone.filter((element) => element.props('isChecked'))).toHaveLength(0);
await dropdownItemsWithDone.at(0).trigger('click');
expect(dropdownItemsWithDone.filter((element) => element.props('isChecked'))).toHaveLength(1);
expect(wrapper.emitted().input).toEqual([[[expectedKey]]]);
});
it('has an item unselected after it is unchecked', async () => {
const expectedKey = 'end';
const expectedValue = items[expectedKey];
createComponent({ value: [expectedKey] });
const dropdownItemsWithDone = findDropdownItems().filter((element) =>
element.html().includes(expectedValue),
);
expect(dropdownItemsWithDone.filter((element) => element.props('isChecked'))).toHaveLength(1);
await dropdownItemsWithDone.at(0).trigger('click');
expect(dropdownItemsWithDone.filter((element) => element.props('isChecked'))).toHaveLength(0);
expect(wrapper.emitted().input).toEqual([[[]]]);
});
});
...@@ -27082,6 +27082,18 @@ msgstr "" ...@@ -27082,6 +27082,18 @@ msgstr ""
msgid "Policy project doesn't exist" msgid "Policy project doesn't exist"
msgstr "" msgstr ""
msgid "PolicyRuleMultiSelect|%{firstLabel} +%{numberOfAdditionalLabels} more"
msgstr ""
msgid "PolicyRuleMultiSelect|All %{itemTypeName}"
msgstr ""
msgid "PolicyRuleMultiSelect|Select %{itemTypeName}"
msgstr ""
msgid "PolicyRuleMultiSelect|Select all"
msgstr ""
msgid "Polling interval multiplier" msgid "Polling interval multiplier"
msgstr "" msgstr ""
......
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