diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-01-20 00:07:28 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-01-20 00:07:28 +0300 |
commit | 9bc3ee9ad4c857570b7a029345cc6fff3ed46b5f (patch) | |
tree | 4de11c170947a73056c72b47f1036c048e7c082e /app/assets/javascripts/vue_shared | |
parent | dc539af30068062bd6fc2f9c6b478d4a1feb8c23 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/vue_shared')
-rw-r--r-- | app/assets/javascripts/vue_shared/components/entity_select/constants.js (renamed from app/assets/javascripts/vue_shared/components/group_select/constants.js) | 1 | ||||
-rw-r--r-- | app/assets/javascripts/vue_shared/components/entity_select/entity_select.vue (renamed from app/assets/javascripts/vue_shared/components/group_select/group_select.vue) | 137 | ||||
-rw-r--r-- | app/assets/javascripts/vue_shared/components/entity_select/group_select.vue | 132 | ||||
-rw-r--r-- | app/assets/javascripts/vue_shared/components/entity_select/init_group_selects.js (renamed from app/assets/javascripts/vue_shared/components/group_select/init_group_selects.js) | 0 | ||||
-rw-r--r-- | app/assets/javascripts/vue_shared/components/entity_select/utils.js (renamed from app/assets/javascripts/vue_shared/components/group_select/utils.js) | 0 |
5 files changed, 183 insertions, 87 deletions
diff --git a/app/assets/javascripts/vue_shared/components/group_select/constants.js b/app/assets/javascripts/vue_shared/components/entity_select/constants.js index 06537d682fe..a48bba3be8c 100644 --- a/app/assets/javascripts/vue_shared/components/group_select/constants.js +++ b/app/assets/javascripts/vue_shared/components/entity_select/constants.js @@ -1,6 +1,7 @@ import { __ } from '~/locale'; export const TOGGLE_TEXT = __('Search for a group'); +export const HEADER_TEXT = __('Select a group'); export const RESET_LABEL = __('Reset'); export const FETCH_GROUPS_ERROR = __('Unable to fetch groups. Reload the page to try again.'); export const FETCH_GROUP_ERROR = __('Unable to fetch group. Reload the page to try again.'); diff --git a/app/assets/javascripts/vue_shared/components/group_select/group_select.vue b/app/assets/javascripts/vue_shared/components/entity_select/entity_select.vue index d295052e2ce..42a37eef1eb 100644 --- a/app/assets/javascripts/vue_shared/components/group_select/group_select.vue +++ b/app/assets/javascripts/vue_shared/components/entity_select/entity_select.vue @@ -1,28 +1,15 @@ <script> import { debounce } from 'lodash'; -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 { GlFormGroup, GlCollapsibleListbox } from '@gitlab/ui'; import { __ } from '~/locale'; import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants'; -import { groupsPath } from './utils'; -import { - TOGGLE_TEXT, - RESET_LABEL, - FETCH_GROUPS_ERROR, - FETCH_GROUP_ERROR, - QUERY_TOO_SHORT_MESSAGE, -} from './constants'; +import { RESET_LABEL, QUERY_TOO_SHORT_MESSAGE } from './constants'; const MINIMUM_QUERY_LENGTH = 3; -const GROUPS_PER_PAGE = 20; export default { components: { GlFormGroup, - GlAlert, GlCollapsibleListbox, }, props: { @@ -48,13 +35,20 @@ export default { required: false, default: false, }, - parentGroupID: { + headerText: { type: String, - required: false, - default: null, + required: true, }, - groupsFilter: { + defaultToggleText: { type: String, + required: true, + }, + fetchItems: { + type: Function, + required: true, + }, + fetchInitialSelectionText: { + type: Function, required: false, default: null, }, @@ -63,10 +57,10 @@ export default { return { pristine: true, searching: false, - hasMoreGroups: true, + hasMoreItems: true, infiniteScrollLoading: false, searchString: '', - groups: [], + items: [], page: 1, selectedValue: null, selectedText: null, @@ -78,14 +72,14 @@ export default { set(value) { this.selectedValue = value; this.selectedText = - value === null ? null : this.groups.find((group) => group.value === value).full_name; + value === null ? null : this.items.find((item) => item.value === value).text; }, get() { return this.selectedValue; }, }, toggleText() { - return this.selectedText ?? this.$options.i18n.toggleText; + return this.selectedText ?? this.defaultToggleText; }, resetButtonLabel() { return this.clearable ? RESET_LABEL : ''; @@ -109,90 +103,64 @@ export default { search: debounce(function debouncedSearch(searchString) { this.searchString = searchString; if (this.isSearchQueryTooShort) { - this.groups = []; + this.items = []; } else { - this.fetchGroups(); + this.fetchEntities(); } }, DEFAULT_DEBOUNCE_AND_THROTTLE_MS), - async fetchGroups(page = 1) { + async fetchEntities(page = 1) { if (page === 1) { this.searching = true; - this.groups = []; - this.hasMoreGroups = true; + this.items = []; + this.hasMoreItems = true; } else { this.infiniteScrollLoading = true; } - try { - const { data, headers } = await axios.get( - Api.buildUrl(groupsPath(this.groupsFilter, this.parentGroupID)), - { - params: { - search: this.searchString, - per_page: GROUPS_PER_PAGE, - page, - }, - }, - ); - const groups = data.length ? data : data.results || []; - - this.groups.push( - ...groups.map((group) => ({ - ...group, - value: String(group.id), - })), - ); + const { items, totalPages } = await this.fetchItems(this.searchString, page); - const { totalPages } = parseIntPagination(normalizeHeaders(headers)); - if (page === totalPages) { - this.hasMoreGroups = false; - } + this.items.push(...items); - this.page = page; - this.searching = false; - this.infiniteScrollLoading = false; - } catch (error) { - this.handleError({ message: FETCH_GROUPS_ERROR, error }); + if (page === totalPages) { + this.hasMoreItems = false; } + + this.page = page; + this.searching = false; + this.infiniteScrollLoading = false; }, async fetchInitialSelection() { if (!this.initialSelection) { this.pristine = false; return; } - this.searching = true; - try { - const group = await Api.group(this.initialSelection); - this.selectedValue = this.initialSelection; - this.selectedText = group.full_name; - this.pristine = false; - this.searching = false; - } catch (error) { - this.handleError({ message: FETCH_GROUP_ERROR, error }); + + if (!this.fetchInitialSelectionText) { + throw new Error( + '`initialSelection` is provided but lacks `fetchInitialSelectionText` to retrieve the corresponding text', + ); } + + this.searching = true; + const name = await this.fetchInitialSelectionText(this.initialSelection); + this.selectedValue = this.initialSelection; + this.selectedText = name; + this.pristine = false; + this.searching = false; }, onShown() { - if (!this.searchString && !this.groups.length) { - this.fetchGroups(); + if (!this.searchString && !this.items.length) { + this.fetchEntities(); } }, onReset() { this.selected = null; }, onBottomReached() { - this.fetchGroups(this.page + 1); - }, - handleError({ message, error }) { - Sentry.captureException(error); - this.errorMessage = message; - }, - dismissError() { - this.errorMessage = ''; + this.fetchEntities(this.page + 1); }, }, i18n: { - toggleText: TOGGLE_TEXT, - selectGroup: __('Select a group'), noResultsText: __('No results found.'), searchQueryTooShort: QUERY_TOO_SHORT_MESSAGE, }, @@ -201,20 +169,18 @@ export default { <template> <gl-form-group :label="label"> - <gl-alert v-if="errorMessage" class="gl-mb-3" variant="danger" @dismiss="dismissError">{{ - errorMessage - }}</gl-alert> + <slot name="error"></slot> <gl-collapsible-listbox ref="listbox" v-model="selected" - :header-text="$options.i18n.selectGroup" + :header-text="headerText" :reset-button-label="resetButtonLabel" :toggle-text="toggleText" :loading="searching && pristine" :searching="searching" - :items="groups" + :items="items" :no-results-text="noResultsText" - :infinite-scroll="hasMoreGroups" + :infinite-scroll="hasMoreItems" :infinite-scroll-loading="infiniteScrollLoading" searchable @shown="onShown" @@ -223,10 +189,7 @@ export default { @bottom-reached="onBottomReached" > <template #list-item="{ item }"> - <div class="gl-font-weight-bold"> - {{ item.full_name }} - </div> - <div class="gl-text-gray-300">{{ item.full_path }}</div> + <slot name="list-item" :item="item"></slot> </template> </gl-collapsible-listbox> <input :id="inputId" data-testid="input" type="hidden" :name="inputName" :value="inputValue" /> diff --git a/app/assets/javascripts/vue_shared/components/entity_select/group_select.vue b/app/assets/javascripts/vue_shared/components/entity_select/group_select.vue new file mode 100644 index 00000000000..a5fc438e932 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/entity_select/group_select.vue @@ -0,0 +1,132 @@ +<script> +import { GlAlert } 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, { DEFAULT_PER_PAGE } from '~/api'; +import { groupsPath } from './utils'; +import { TOGGLE_TEXT, HEADER_TEXT, FETCH_GROUPS_ERROR, FETCH_GROUP_ERROR } from './constants'; +import EntitySelect from './entity_select.vue'; + +export default { + components: { + GlAlert, + EntitySelect, + }, + props: { + label: { + type: String, + required: false, + default: '', + }, + inputName: { + type: String, + required: true, + }, + inputId: { + type: String, + required: true, + }, + initialSelection: { + type: String, + required: false, + default: null, + }, + clearable: { + type: Boolean, + required: false, + default: false, + }, + parentGroupID: { + type: String, + required: false, + default: null, + }, + groupsFilter: { + type: String, + required: false, + default: null, + }, + }, + data() { + return { + errorMessage: '', + }; + }, + methods: { + async fetchGroups(searchString = '', page = 1) { + let groups = []; + let totalPages = 0; + try { + const { data, headers } = await axios.get( + Api.buildUrl(groupsPath(this.groupsFilter, this.parentGroupID)), + { + params: { + search: searchString, + per_page: DEFAULT_PER_PAGE, + page, + }, + }, + ); + groups = (data.length ? data : data.results || []).map((group) => ({ + ...group, + text: group.full_name, + value: String(group.id), + })); + + totalPages = parseIntPagination(normalizeHeaders(headers)).totalPages; + } catch (error) { + this.handleError({ message: FETCH_GROUPS_ERROR, error }); + } + return { items: groups, totalPages }; + }, + async fetchGroupName(groupId) { + let groupName = ''; + try { + const group = await Api.group(groupId); + groupName = group.full_name; + } catch (error) { + this.handleError({ message: FETCH_GROUP_ERROR, error }); + } + return groupName; + }, + handleError({ message, error }) { + Sentry.captureException(error); + this.errorMessage = message; + }, + dismissError() { + this.errorMessage = ''; + }, + }, + i18n: { + toggleText: TOGGLE_TEXT, + selectGroup: HEADER_TEXT, + }, +}; +</script> + +<template> + <entity-select + :label="label" + :input-name="inputName" + :input-id="inputId" + :initial-selection="initialSelection" + :clearable="clearable" + :header-text="$options.i18n.selectGroup" + :default-toggle-text="$options.i18n.toggleText" + :fetch-items="fetchGroups" + :fetch-initial-selection-text="fetchGroupName" + > + <template #error> + <gl-alert v-if="errorMessage" class="gl-mb-3" variant="danger" @dismiss="dismissError">{{ + errorMessage + }}</gl-alert> + </template> + <template #list-item="{ item }"> + <div class="gl-font-weight-bold"> + {{ item.full_name }} + </div> + <div class="gl-text-gray-300">{{ item.full_path }}</div> + </template> + </entity-select> +</template> diff --git a/app/assets/javascripts/vue_shared/components/group_select/init_group_selects.js b/app/assets/javascripts/vue_shared/components/entity_select/init_group_selects.js index dbfac8a0339..dbfac8a0339 100644 --- a/app/assets/javascripts/vue_shared/components/group_select/init_group_selects.js +++ b/app/assets/javascripts/vue_shared/components/entity_select/init_group_selects.js diff --git a/app/assets/javascripts/vue_shared/components/group_select/utils.js b/app/assets/javascripts/vue_shared/components/entity_select/utils.js index 0a4622269f4..0a4622269f4 100644 --- a/app/assets/javascripts/vue_shared/components/group_select/utils.js +++ b/app/assets/javascripts/vue_shared/components/entity_select/utils.js |