diff options
Diffstat (limited to 'app/assets/javascripts/vue_shared/components/group_select/group_select.vue')
-rw-r--r-- | app/assets/javascripts/vue_shared/components/group_select/group_select.vue | 91 |
1 files changed, 65 insertions, 26 deletions
diff --git a/app/assets/javascripts/vue_shared/components/group_select/group_select.vue b/app/assets/javascripts/vue_shared/components/group_select/group_select.vue index 5db723e1e5a..d295052e2ce 100644 --- a/app/assets/javascripts/vue_shared/components/group_select/group_select.vue +++ b/app/assets/javascripts/vue_shared/components/group_select/group_select.vue @@ -1,26 +1,35 @@ <script> import { debounce } from 'lodash'; -import { GlCollapsibleListbox } from '@gitlab/ui'; +import { GlFormGroup, GlAlert, GlCollapsibleListbox } from '@gitlab/ui'; +import * as Sentry from '@sentry/browser'; import axios from '~/lib/utils/axios_utils'; +import { normalizeHeaders, parseIntPagination } from '~/lib/utils/common_utils'; import Api from '~/api'; import { __ } from '~/locale'; import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants'; -import { createAlert } from '~/flash'; import { groupsPath } from './utils'; import { TOGGLE_TEXT, + RESET_LABEL, FETCH_GROUPS_ERROR, FETCH_GROUP_ERROR, QUERY_TOO_SHORT_MESSAGE, } from './constants'; const MINIMUM_QUERY_LENGTH = 3; +const GROUPS_PER_PAGE = 20; export default { components: { + GlFormGroup, + GlAlert, GlCollapsibleListbox, }, props: { + label: { + type: String, + required: true, + }, inputName: { type: String, required: true, @@ -54,10 +63,14 @@ export default { return { pristine: true, searching: false, + hasMoreGroups: true, + infiniteScrollLoading: false, searchString: '', groups: [], + page: 1, selectedValue: null, selectedText: null, + errorMessage: '', }; }, computed: { @@ -74,6 +87,9 @@ export default { toggleText() { return this.selectedText ?? this.$options.i18n.toggleText; }, + resetButtonLabel() { + return this.clearable ? RESET_LABEL : ''; + }, inputValue() { return this.selectedValue ? this.selectedValue : ''; }, @@ -95,35 +111,48 @@ export default { if (this.isSearchQueryTooShort) { this.groups = []; } else { - this.fetchGroups(searchString); + this.fetchGroups(); } }, DEFAULT_DEBOUNCE_AND_THROTTLE_MS), - async fetchGroups(searchString = '') { - this.searching = true; + async fetchGroups(page = 1) { + if (page === 1) { + this.searching = true; + this.groups = []; + this.hasMoreGroups = true; + } else { + this.infiniteScrollLoading = true; + } try { - const { data } = await axios.get( + const { data, headers } = await axios.get( Api.buildUrl(groupsPath(this.groupsFilter, this.parentGroupID)), { params: { - search: searchString, + search: this.searchString, + per_page: GROUPS_PER_PAGE, + page, }, }, ); const groups = data.length ? data : data.results || []; - this.groups = groups.map((group) => ({ - ...group, - value: String(group.id), - })); + this.groups.push( + ...groups.map((group) => ({ + ...group, + value: String(group.id), + })), + ); + + const { totalPages } = parseIntPagination(normalizeHeaders(headers)); + if (page === totalPages) { + this.hasMoreGroups = false; + } + this.page = page; this.searching = false; + this.infiniteScrollLoading = false; } catch (error) { - createAlert({ - message: FETCH_GROUPS_ERROR, - error, - parent: this.$el, - }); + this.handleError({ message: FETCH_GROUPS_ERROR, error }); } }, async fetchInitialSelection() { @@ -139,11 +168,7 @@ export default { this.pristine = false; this.searching = false; } catch (error) { - createAlert({ - message: FETCH_GROUP_ERROR, - error, - parent: this.$el, - }); + this.handleError({ message: FETCH_GROUP_ERROR, error }); } }, onShown() { @@ -154,11 +179,20 @@ export default { onReset() { this.selected = null; }, + onBottomReached() { + this.fetchGroups(this.page + 1); + }, + handleError({ message, error }) { + Sentry.captureException(error); + this.errorMessage = message; + }, + dismissError() { + this.errorMessage = ''; + }, }, i18n: { toggleText: TOGGLE_TEXT, selectGroup: __('Select a group'), - reset: __('Reset'), noResultsText: __('No results found.'), searchQueryTooShort: QUERY_TOO_SHORT_MESSAGE, }, @@ -166,21 +200,27 @@ export default { </script> <template> - <div> + <gl-form-group :label="label"> + <gl-alert v-if="errorMessage" class="gl-mb-3" variant="danger" @dismiss="dismissError">{{ + errorMessage + }}</gl-alert> <gl-collapsible-listbox ref="listbox" v-model="selected" :header-text="$options.i18n.selectGroup" - :reset-button-label="$options.i18n.reset" + :reset-button-label="resetButtonLabel" :toggle-text="toggleText" :loading="searching && pristine" :searching="searching" :items="groups" :no-results-text="noResultsText" + :infinite-scroll="hasMoreGroups" + :infinite-scroll-loading="infiniteScrollLoading" searchable @shown="onShown" @search="search" @reset="onReset" + @bottom-reached="onBottomReached" > <template #list-item="{ item }"> <div class="gl-font-weight-bold"> @@ -189,7 +229,6 @@ export default { <div class="gl-text-gray-300">{{ item.full_path }}</div> </template> </gl-collapsible-listbox> - <div class="flash-container"></div> <input :id="inputId" data-testid="input" type="hidden" :name="inputName" :value="inputValue" /> - </div> + </gl-form-group> </template> |