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:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-10-20 11:43:02 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-10-20 11:43:02 +0300
commitd9ab72d6080f594d0b3cae15f14b3ef2c6c638cb (patch)
tree2341ef426af70ad1e289c38036737e04b0aa5007 /app/assets/javascripts/vue_shared/components/filtered_search_bar
parentd6e514dd13db8947884cd58fe2a9c2a063400a9b (diff)
Add latest changes from gitlab-org/gitlab@14-4-stable-eev14.4.0-rc42
Diffstat (limited to 'app/assets/javascripts/vue_shared/components/filtered_search_bar')
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/queries/epic.fragment.graphql15
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/queries/search_epics.query.graphql16
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue29
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/epic_token.vue138
4 files changed, 110 insertions, 88 deletions
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/queries/epic.fragment.graphql b/app/assets/javascripts/vue_shared/components/filtered_search_bar/queries/epic.fragment.graphql
new file mode 100644
index 00000000000..9e9bda8ad3e
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/queries/epic.fragment.graphql
@@ -0,0 +1,15 @@
+fragment EpicNode on Epic {
+ id
+ iid
+ group {
+ fullPath
+ }
+ title
+ state
+ reference
+ referencePath: reference(full: true)
+ webPath
+ webUrl
+ createdAt
+ closedAt
+}
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/queries/search_epics.query.graphql b/app/assets/javascripts/vue_shared/components/filtered_search_bar/queries/search_epics.query.graphql
new file mode 100644
index 00000000000..4bb4b586fc9
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/queries/search_epics.query.graphql
@@ -0,0 +1,16 @@
+#import "./epic.fragment.graphql"
+
+query searchEpics($fullPath: ID!, $search: String, $state: EpicState) {
+ group(fullPath: $fullPath) {
+ epics(
+ search: $search
+ state: $state
+ includeAncestorGroups: true
+ includeDescendantGroups: false
+ ) {
+ nodes {
+ ...EpicNode
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue
index d1326e96794..cee7c40aa83 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue
@@ -67,6 +67,11 @@ export default {
required: false,
default: 'id',
},
+ searchBy: {
+ type: String,
+ required: false,
+ default: undefined,
+ },
},
data() {
return {
@@ -112,16 +117,18 @@ export default {
);
},
showDefaultSuggestions() {
- return this.availableDefaultSuggestions.length;
+ return this.availableDefaultSuggestions.length > 0;
},
showRecentSuggestions() {
- return this.isRecentSuggestionsEnabled && this.recentSuggestions.length && !this.searchKey;
+ return (
+ this.isRecentSuggestionsEnabled && this.recentSuggestions.length > 0 && !this.searchKey
+ );
},
showPreloadedSuggestions() {
- return this.preloadedSuggestions.length && !this.searchKey;
+ return this.preloadedSuggestions.length > 0 && !this.searchKey;
},
showAvailableSuggestions() {
- return this.availableSuggestions.length;
+ return this.availableSuggestions.length > 0;
},
showSuggestions() {
// These conditions must match the template under `#suggestions` slot
@@ -134,13 +141,19 @@ export default {
this.showAvailableSuggestions
);
},
+ searchTerm() {
+ return this.searchBy && this.activeTokenValue
+ ? this.activeTokenValue[this.searchBy]
+ : undefined;
+ },
},
watch: {
active: {
immediate: true,
handler(newValue) {
if (!newValue && !this.suggestions.length) {
- this.$emit('fetch-suggestions', this.value.data);
+ const search = this.searchTerm ? this.searchTerm : this.value.data;
+ this.$emit('fetch-suggestions', search);
}
},
},
@@ -148,8 +161,10 @@ export default {
methods: {
handleInput: debounce(function debouncedSearch({ data }) {
this.searchKey = data;
- if (!this.suggestionsLoading) {
- this.$emit('fetch-suggestions', data);
+
+ if (!this.suggestionsLoading && !this.activeTokenValue) {
+ const search = this.searchTerm ? this.searchTerm : data;
+ this.$emit('fetch-suggestions', search);
}
}, DEBOUNCE_DELAY),
handleTokenValueSelected(activeTokenValue) {
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/epic_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/epic_token.vue
index 9f68308808e..9c2f5306654 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/epic_token.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/epic_token.vue
@@ -1,22 +1,19 @@
<script>
-import {
- GlDropdownDivider,
- GlFilteredSearchSuggestion,
- GlFilteredSearchToken,
- GlLoadingIcon,
-} from '@gitlab/ui';
-import { debounce } from 'lodash';
+import { GlFilteredSearchSuggestion } from '@gitlab/ui';
import createFlash from '~/flash';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { __ } from '~/locale';
-import { DEBOUNCE_DELAY, DEFAULT_NONE_ANY, FILTER_NONE_ANY, OPERATOR_IS_NOT } from '../constants';
+import { DEFAULT_NONE_ANY, FILTER_NONE_ANY, OPERATOR_IS_NOT } from '../constants';
+import searchEpicsQuery from '../queries/search_epics.query.graphql';
+
+import BaseToken from './base_token.vue';
export default {
- separator: '::&',
+ prefix: '&',
+ separator: '::',
components: {
- GlDropdownDivider,
- GlFilteredSearchToken,
+ BaseToken,
GlFilteredSearchSuggestion,
- GlLoadingIcon,
},
props: {
config: {
@@ -27,11 +24,15 @@ export default {
type: Object,
required: true,
},
+ active: {
+ type: Boolean,
+ required: true,
+ },
},
data() {
return {
epics: this.config.initialEpics || [],
- loading: true,
+ loading: false,
};
},
computed: {
@@ -56,98 +57,73 @@ export default {
}
return this.defaultEpics;
},
- activeEpic() {
- if (this.currentValue && this.epics.length) {
- // Check if current value is an epic ID.
- if (typeof this.currentValue === 'number') {
- return this.epics.find((epic) => epic[this.idProperty] === this.currentValue);
- }
-
- // Current value is a string.
- const [groupPath, idProperty] = this.currentValue?.split(this.$options.separator);
- return this.epics.find(
- (epic) =>
- epic.group_full_path === groupPath &&
- epic[this.idProperty] === parseInt(idProperty, 10),
- );
- }
- return null;
- },
- displayText() {
- return `${this.activeEpic?.title}${this.$options.separator}${this.activeEpic?.iid}`;
- },
- },
- watch: {
- active: {
- immediate: true,
- handler(newValue) {
- if (!newValue && !this.epics.length) {
- this.searchEpics({ data: this.currentValue });
- }
- },
- },
},
methods: {
- fetchEpicsBySearchTerm({ epicPath = '', search = '' }) {
+ fetchEpics(search = '') {
+ return this.$apollo
+ .query({
+ query: searchEpicsQuery,
+ variables: { fullPath: this.config.fullPath, search },
+ })
+ .then(({ data }) => data.group?.epics.nodes);
+ },
+ fetchEpicsBySearchTerm(search) {
this.loading = true;
- this.config
- .fetchEpics({ epicPath, search })
+ this.fetchEpics(search)
.then((response) => {
- this.epics = Array.isArray(response) ? response : response.data;
+ this.epics = Array.isArray(response) ? response : response?.data;
})
.catch(() => createFlash({ message: __('There was a problem fetching epics.') }))
.finally(() => {
this.loading = false;
});
},
- searchEpics: debounce(function debouncedSearch({ data }) {
- let epicPath = this.activeEpic?.web_url;
-
- // When user visits the page with token value already included in filters
- // We don't have any information about selected token except for its
- // group path and iid joined by separator, so we need to manually
- // compose epic path from it.
- if (data.includes?.(this.$options.separator)) {
- const [groupPath, epicIid] = data.split(this.$options.separator);
- epicPath = `/groups/${groupPath}/-/epics/${epicIid}`;
+ getActiveEpic(epics, data) {
+ if (data && epics.length) {
+ return epics.find((epic) => this.getValue(epic) === data);
}
- this.fetchEpicsBySearchTerm({ epicPath, search: data });
- }, DEBOUNCE_DELAY),
-
+ return undefined;
+ },
getValue(epic) {
- return this.config.useIdValue
- ? String(epic[this.idProperty])
- : `${epic.group_full_path}${this.$options.separator}${epic[this.idProperty]}`;
+ return this.getEpicIdProperty(epic).toString();
+ },
+ displayValue(epic) {
+ return `${this.$options.prefix}${this.getEpicIdProperty(epic)}${this.$options.separator}${
+ epic?.title
+ }`;
+ },
+ getEpicIdProperty(epic) {
+ return getIdFromGraphQLId(epic[this.idProperty]);
},
},
};
</script>
<template>
- <gl-filtered-search-token
+ <base-token
:config="config"
- v-bind="{ ...$props, ...$attrs }"
+ :value="value"
+ :active="active"
+ :suggestions-loading="loading"
+ :suggestions="epics"
+ :get-active-token-value="getActiveEpic"
+ :default-suggestions="availableDefaultEpics"
+ :recent-suggestions-storage-key="config.recentSuggestionsStorageKey"
+ search-by="title"
+ @fetch-suggestions="fetchEpicsBySearchTerm"
v-on="$listeners"
- @input="searchEpics"
>
- <template #view="{ inputValue }">
- {{ activeEpic ? displayText : inputValue }}
+ <template #view="{ viewTokenProps: { inputValue, activeTokenValue } }">
+ {{ activeTokenValue ? displayValue(activeTokenValue) : inputValue }}
</template>
- <template #suggestions>
+ <template #suggestions-list="{ suggestions }">
<gl-filtered-search-suggestion
- v-for="epic in availableDefaultEpics"
- :key="epic.value"
- :value="epic.value"
+ v-for="epic in suggestions"
+ :key="epic.id"
+ :value="getValue(epic)"
>
- {{ epic.text }}
+ {{ epic.title }}
</gl-filtered-search-suggestion>
- <gl-dropdown-divider v-if="availableDefaultEpics.length" />
- <gl-loading-icon v-if="loading" size="sm" />
- <template v-else>
- <gl-filtered-search-suggestion v-for="epic in epics" :key="epic.id" :value="getValue(epic)">
- {{ epic.title }}
- </gl-filtered-search-suggestion>
- </template>
</template>
- </gl-filtered-search-token>
+ </base-token>
</template>