Не подтверждена Коммит 3ba820fd создал по автору Artur Fedorov's avatar Artur Fedorov
Просмотр файлов

Migrate Protected Branches selector to Listbox

Protected Branches selector
basis component GlDropdown
is migrated to GlListbox

Changelog: changed
EE: true
владелец 2b10cbb4
<script>
import { GlDropdown, GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui';
import { GlCollapsibleListbox, GlTruncate } from '@gitlab/ui';
import { debounce } from 'lodash';
import Api from 'ee/api';
import { __ } from '~/locale';
......@@ -7,9 +7,8 @@ import { BRANCH_FETCH_DELAY, ALL_BRANCHES, ALL_PROTECTED_BRANCHES, PLACEHOLDER }
export default {
components: {
GlDropdown,
GlDropdownItem,
GlSearchBoxByType,
GlCollapsibleListbox,
GlTruncate,
},
props: {
projectId: {
......@@ -45,6 +44,7 @@ export default {
data() {
return {
branches: [],
listboxItems: [],
initialLoading: false,
searching: false,
searchTerm: '',
......@@ -81,6 +81,9 @@ export default {
return PLACEHOLDER;
},
},
created() {
this.debouncedSearchKeyUpdate = debounce(this.setSearchTerm, BRANCH_FETCH_DELAY);
},
mounted() {
this.initialLoading = true;
this.fetchBranches()
......@@ -91,6 +94,9 @@ export default {
});
},
methods: {
convertBranchesToListboxItems(branches = []) {
return branches.map(({ id, name }) => ({ text: name, value: id }));
},
fetchBranches(term) {
this.searching = true;
const includeAllBranches = !term || term.toLowerCase().includes('all');
......@@ -110,25 +116,32 @@ export default {
return Api.projectProtectedBranches(this.projectId, term)
.then((branches) => {
this.$emit('apiError', { hasErrored: false });
this.listboxItems = this.convertBranchesToListboxItems([...baseBranches, ...branches]);
this.branches = [...baseBranches, ...branches];
})
.catch((error) => {
this.$emit('apiError', { hasErrored: true, error });
this.listboxItems = this.convertBranchesToListboxItems(baseBranches);
this.branches = baseBranches;
})
.finally(() => {
this.searching = false;
});
},
search: debounce(function debouncedSearch() {
setSearchTerm(query) {
this.searchTerm = query;
this.fetchBranches(this.searchTerm);
}, BRANCH_FETCH_DELAY),
isSelectedBranch(id) {
return this.selectedBranch.id === id;
},
onSelect(branch) {
this.selected = branch;
this.$emit('input', branch);
onSelect(branchId) {
if (branchId === ALL_BRANCHES.id) {
this.selected = ALL_BRANCHES;
} else if (branchId === ALL_PROTECTED_BRANCHES.id) {
this.selected = ALL_PROTECTED_BRANCHES;
} else {
this.selected = this.branches.find(({ id }) => id === branchId);
}
this.$emit('input', this.selected);
},
branchNameClass(id) {
return {
......@@ -143,24 +156,21 @@ export default {
</script>
<template>
<gl-dropdown
:class="{ 'is-invalid': isInvalid }"
class="gl-w-full gl-dropdown-menu-full-width"
:text="selectedBranch.name"
:loading="initialLoading"
<gl-collapsible-listbox
block
searchable
:items="listboxItems"
:header-text="$options.i18n.header"
:loading="initialLoading"
:toggle-class="['gl-w-full', { 'is-invalid': isInvalid }]"
:toggle-text="selectedBranch.name"
:selected="selectedBranch.id"
:searching="searching"
@search="debouncedSearchKeyUpdate"
@select="onSelect"
>
<template #header>
<gl-search-box-by-type v-model="searchTerm" :is-loading="searching" @input="search" />
<template #list-item="{ item }">
<gl-truncate :class="branchNameClass(item.value)" :text="item.text" />
</template>
<gl-dropdown-item
v-for="branch in branches"
:key="branch.id"
is-check-item
:is-checked="isSelectedBranch(branch.id)"
@click="onSelect(branch)"
>
<span :class="branchNameClass(branch.id)">{{ branch.name }}</span>
</gl-dropdown-item>
</gl-dropdown>
</gl-collapsible-listbox>
</template>
import { GlDropdown, GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui';
import { shallowMount, mount } from '@vue/test-utils';
import { GlCollapsibleListbox, GlListboxItem, GlTruncate } from '@gitlab/ui';
import { nextTick } from 'vue';
import Api from 'ee/api';
import ProtectedBranchesSelector from 'ee/vue_shared/components/branches_selector/protected_branches_selector.vue';
......@@ -8,6 +7,7 @@ import {
ALL_PROTECTED_BRANCHES,
PLACEHOLDER,
} from 'ee/vue_shared/components/branches_selector/constants';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { TEST_BRANCHES_SELECTIONS, TEST_PROJECT_ID, TEST_PROTECTED_BRANCHES } from './mock_data';
......@@ -19,17 +19,22 @@ const error = new Error('Something went wrong');
describe('Protected Branches Selector', () => {
let wrapper;
const findDropdown = () => wrapper.findComponent(GlDropdown);
const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
const findDropdown = () => wrapper.findComponent(GlCollapsibleListbox);
const findDropdownItems = () => wrapper.findAllComponents(GlListboxItem);
const findSelectableBranches = () => findDropdownItems().wrappers.map((item) => item.text());
const findSearch = () => wrapper.findComponent(GlSearchBoxByType);
const findSearch = () => wrapper.findByTestId('listbox-search-input');
const createComponent = (props = {}, mountFn = shallowMount) => {
const createComponent = (props = {}, mountFn = shallowMountExtended) => {
wrapper = mountFn(ProtectedBranchesSelector, {
propsData: {
projectId: '1',
...props,
},
stubs: {
GlListboxItem,
GlCollapsibleListbox,
GlTruncate,
},
});
};
......@@ -51,7 +56,7 @@ describe('Protected Branches Selector', () => {
createComponent({ isInvalid: true });
await waitForPromises();
expect(findDropdown().classes('is-invalid')).toBe(true);
expect(findDropdown().props('toggleClass')).toEqual(['gl-w-full', { 'is-invalid': true }]);
});
it.each`
......@@ -81,13 +86,13 @@ describe('Protected Branches Selector', () => {
});
await waitForPromises();
expect(findDropdown().props('text')).toBe(branchName);
expect(findDropdown().props('toggleText')).toBe(branchName);
if (selectedBranches.length > 0 || selectedBranchesNames.length > 0)
expect(
findDropdownItems()
.filter((item) => item.text() === branchName)
.at(0)
.props('isChecked'),
.props('isSelected'),
).toBe(true);
},
);
......@@ -136,12 +141,12 @@ describe('Protected Branches Selector', () => {
});
await waitForPromises();
expect(findDropdown().props('text')).toBe(ALL_PROTECTED_BRANCHES.name);
expect(findDropdown().props('toggleText')).toBe(ALL_PROTECTED_BRANCHES.name);
expect(
findDropdownItems()
.filter((item) => item.text() === ALL_PROTECTED_BRANCHES.name)
.at(0)
.props('isChecked'),
.props('isSelected'),
).toBe(true);
});
......@@ -178,12 +183,11 @@ describe('Protected Branches Selector', () => {
it('fetches protected branches with search term', async () => {
const term = 'lorem';
createComponent({}, mount);
createComponent();
findSearch().vm.$emit('input', term);
await nextTick();
expect(findSearch().props('isLoading')).toBe(true);
expect(findDropdown().props('searching')).toBe(true);
await waitForPromises();
......@@ -192,11 +196,11 @@ describe('Protected Branches Selector', () => {
[{ hasErrored: false }],
[{ hasErrored: false }],
]);
expect(findSearch().props('isLoading')).toBe(false);
expect(findDropdown().props('searching')).toBe(false);
});
it('fetches protected branches with no all branches if there is a search', async () => {
createComponent({}, mount);
createComponent();
findSearch().vm.$emit('input', 'main');
await waitForPromises();
......@@ -205,7 +209,7 @@ describe('Protected Branches Selector', () => {
});
it('fetches protected branches with all branches if search contains term "all"', async () => {
createComponent({}, mount);
createComponent();
findSearch().vm.$emit('input', 'all');
await waitForPromises();
......@@ -215,7 +219,7 @@ describe('Protected Branches Selector', () => {
describe('with allow all protected branches option', () => {
it('fetches protected branches with all branches if search contains term "all"', async () => {
createComponent({ allowAllProtectedBranchesOption: true }, mount);
createComponent({ allowAllProtectedBranchesOption: true });
findSearch().vm.$emit('input', 'all');
await waitForPromises();
......@@ -225,7 +229,7 @@ describe('Protected Branches Selector', () => {
describe('when fetching the branch list fails while searching', () => {
beforeEach(() => {
createComponent({ allowAllProtectedBranchesOption: true }, mount);
createComponent({ allowAllProtectedBranchesOption: true });
return waitForPromises();
});
......@@ -250,7 +254,7 @@ describe('Protected Branches Selector', () => {
describe('when fetching the branch list fails while searching', () => {
beforeEach(() => {
createComponent({}, mount);
createComponent();
return waitForPromises();
});
......@@ -287,9 +291,8 @@ describe('Protected Branches Selector', () => {
createComponent();
await waitForPromises();
await findDropdownItems().at(1).vm.$emit('click');
await findDropdown().vm.$emit('select', TEST_PROTECTED_BRANCHES[0].id);
expect(findDropdownItems().at(1).props('isChecked')).toBe(true);
expect(wrapper.emitted('input')).toStrictEqual([[TEST_PROTECTED_BRANCHES[0]]]);
});
});
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать