Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/work_items/components/shared/work_item_token_input.vue')
-rw-r--r--app/assets/javascripts/work_items/components/shared/work_item_token_input.vue126
1 files changed, 92 insertions, 34 deletions
diff --git a/app/assets/javascripts/work_items/components/shared/work_item_token_input.vue b/app/assets/javascripts/work_items/components/shared/work_item_token_input.vue
index 3595ab631df..c122db6c902 100644
--- a/app/assets/javascripts/work_items/components/shared/work_item_token_input.vue
+++ b/app/assets/javascripts/work_items/components/shared/work_item_token_input.vue
@@ -1,20 +1,29 @@
<script>
-import { GlTokenSelector } from '@gitlab/ui';
+import { GlTokenSelector, GlAlert } from '@gitlab/ui';
import { debounce } from 'lodash';
+
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { isNumeric } from '~/lib/utils/number_utils';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
+import SafeHtml from '~/vue_shared/directives/safe_html';
+import { highlighter } from 'ee_else_ce/gfm_auto_complete';
+import groupWorkItemsQuery from '../../graphql/group_work_items.query.graphql';
import projectWorkItemsQuery from '../../graphql/project_work_items.query.graphql';
import {
WORK_ITEMS_TYPE_MAP,
I18N_WORK_ITEM_SEARCH_INPUT_PLACEHOLDER,
+ I18N_WORK_ITEM_SEARCH_ERROR,
sprintfWorkItem,
} from '../../constants';
export default {
components: {
GlTokenSelector,
+ GlAlert,
},
+ directives: { SafeHtml },
+ inject: ['isGroup'],
props: {
value: {
type: Array,
@@ -47,30 +56,37 @@ export default {
},
apollo: {
availableWorkItems: {
- query: projectWorkItemsQuery,
+ query() {
+ return this.isGroup ? groupWorkItemsQuery : projectWorkItemsQuery;
+ },
variables() {
return {
fullPath: this.fullPath,
- searchTerm: this.search?.title || this.search,
+ searchTerm: '',
types: this.childrenType ? [this.childrenType] : [],
- in: this.search ? 'TITLE' : undefined,
+ isNumber: false,
};
},
skip() {
return !this.searchStarted;
},
update(data) {
- return data.workspace.workItems.nodes.filter(
- (wi) => !this.childrenIds.includes(wi.id) && this.parentWorkItemId !== wi.id,
- );
+ return [
+ ...this.filterItems(data.workspace.workItemsByIid?.nodes),
+ ...this.filterItems(data.workspace.workItems.nodes),
+ ];
+ },
+ error() {
+ this.error = sprintfWorkItem(I18N_WORK_ITEM_SEARCH_ERROR, this.childrenTypeName);
},
},
},
data() {
return {
availableWorkItems: [],
- search: '',
+ query: '',
searchStarted: false,
+ error: '',
};
},
computed: {
@@ -101,7 +117,24 @@ export default {
methods: {
getIdFromGraphQLId,
setSearchKey(value) {
- this.search = value;
+ this.query = value;
+
+ // Query parameters for searching by text
+ const variables = {
+ searchTerm: value,
+ in: value ? 'TITLE' : undefined,
+ iid: null,
+ isNumber: false,
+ };
+
+ // Check if it is a number, add iid as query parameter
+ if (isNumeric(value) && value) {
+ variables.iid = value;
+ variables.isNumber = true;
+ }
+
+ // Fetch combined results of search by iid and search by title.
+ this.$apollo.queries.availableWorkItems.refetch(variables);
},
handleFocus() {
this.searchStarted = true;
@@ -125,33 +158,58 @@ export default {
}
});
},
+ formatResults(input) {
+ if (!this.query) {
+ return input;
+ }
+
+ return highlighter(`<span class="gl-text-black-normal">${input}</span>`, this.query);
+ },
+ unsetError() {
+ this.error = '';
+ },
+ filterItems(items) {
+ return (
+ items?.filter(
+ (wi) => !this.childrenIds.includes(wi.id) && this.parentWorkItemId !== wi.id,
+ ) || []
+ );
+ },
},
};
</script>
<template>
- <gl-token-selector
- ref="tokenSelector"
- v-model="workItemsToAdd"
- :dropdown-items="availableWorkItems"
- :loading="isLoading"
- :placeholder="addInputPlaceholder"
- menu-class="gl-dropdown-menu-wide dropdown-reduced-height gl-min-h-7!"
- :container-class="tokenSelectorContainerClass"
- data-testid="work-item-token-select-input"
- @text-input="debouncedSearchKeyUpdate"
- @focus="handleFocus"
- @mouseover.native="handleMouseOver"
- @mouseout.native="handleMouseOut"
- @token-add="focusInputText"
- @token-remove="focusInputText"
- @blur="handleBlur"
- >
- <template #token-content="{ token }"> {{ token.iid }} {{ token.title }} </template>
- <template #dropdown-item-content="{ dropdownItem }">
- <div class="gl-display-flex">
- <div class="gl-text-secondary gl-font-sm gl-mr-4">{{ dropdownItem.iid }}</div>
- <div class="gl-text-truncate">{{ dropdownItem.title }}</div>
- </div>
- </template>
- </gl-token-selector>
+ <div>
+ <gl-alert v-if="error" variant="danger" class="gl-mb-3" @dismiss="unsetError">
+ {{ error }}
+ </gl-alert>
+ <gl-token-selector
+ ref="tokenSelector"
+ v-model="workItemsToAdd"
+ :dropdown-items="availableWorkItems"
+ :loading="isLoading"
+ :placeholder="addInputPlaceholder"
+ menu-class="gl-dropdown-menu-wide dropdown-reduced-height gl-min-h-7!"
+ :container-class="tokenSelectorContainerClass"
+ data-testid="work-item-token-select-input"
+ @text-input="debouncedSearchKeyUpdate"
+ @focus="handleFocus"
+ @mouseover.native="handleMouseOver"
+ @mouseout.native="handleMouseOut"
+ @token-add="focusInputText"
+ @token-remove="focusInputText"
+ @blur="handleBlur"
+ >
+ <template #token-content="{ token }"> {{ token.iid }} {{ token.title }} </template>
+ <template #dropdown-item-content="{ dropdownItem }">
+ <div class="gl-display-flex">
+ <div
+ v-safe-html="formatResults(dropdownItem.iid)"
+ class="gl-text-secondary gl-font-sm gl-mr-4"
+ ></div>
+ <div v-safe-html="formatResults(dropdownItem.title)" class="gl-text-truncate"></div>
+ </div>
+ </template>
+ </gl-token-selector>
+ </div>
</template>