summaryrefslogtreecommitdiff
path: root/app/assets/javascripts
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts')
-rw-r--r--app/assets/javascripts/issuables_list/components/issuable.vue2
-rw-r--r--app/assets/javascripts/issuables_list/components/issuables_list_app.vue149
-rw-r--r--app/assets/javascripts/issuables_list/constants.js21
-rw-r--r--app/assets/javascripts/main.js4
-rw-r--r--app/assets/javascripts/notes/stores/actions.js11
-rw-r--r--app/assets/javascripts/registry/explorer/components/details_page/tags_list_row.vue31
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue2
-rw-r--r--app/assets/javascripts/registry/explorer/constants/details.js8
-rw-r--r--app/assets/javascripts/search_autocomplete.js (renamed from app/assets/javascripts/global_search_input.js)207
9 files changed, 312 insertions, 123 deletions
diff --git a/app/assets/javascripts/issuables_list/components/issuable.vue b/app/assets/javascripts/issuables_list/components/issuable.vue
index 9af1887ef12..04991a8d374 100644
--- a/app/assets/javascripts/issuables_list/components/issuable.vue
+++ b/app/assets/javascripts/issuables_list/components/issuable.vue
@@ -365,7 +365,7 @@ export default {
:title="__('Comments')"
:class="{ 'no-comments': hasNoComments }"
>
- <i class="fa fa-comments"></i>
+ <gl-icon name="comments" class="gl-vertical-align-text-bottom" />
{{ userNotesCount }}
</gl-link>
</div>
diff --git a/app/assets/javascripts/issuables_list/components/issuables_list_app.vue b/app/assets/javascripts/issuables_list/components/issuables_list_app.vue
index db18bcbce09..e1a40323f5d 100644
--- a/app/assets/javascripts/issuables_list/components/issuables_list_app.vue
+++ b/app/assets/javascripts/issuables_list/components/issuables_list_app.vue
@@ -12,8 +12,10 @@ import {
import { __ } from '~/locale';
import initManualOrdering from '~/manual_ordering';
import Issuable from './issuable.vue';
+import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
import {
sortOrderMap,
+ availableSortOptionsJira,
RELATIVE_POSITION,
PAGE_SIZE,
PAGE_SIZE_MANUAL,
@@ -29,6 +31,7 @@ export default {
GlPagination,
GlSkeletonLoading,
Issuable,
+ FilteredSearchBar,
},
props: {
canBulkEdit: {
@@ -50,14 +53,25 @@ export default {
type: String,
required: true,
},
+ projectPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
sortKey: {
type: String,
required: false,
default: '',
},
+ type: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
data() {
return {
+ availableSortOptionsJira,
filters: {},
isBulkEditing: false,
issuables: [],
@@ -141,6 +155,22 @@ export default {
nextPage: this.paginationNext,
};
},
+ isJira() {
+ return this.type === 'jira';
+ },
+ initialFilterValue() {
+ const value = [];
+ const { search } = this.getQueryObject();
+
+ if (search) {
+ value.push(search);
+ }
+ return value;
+ },
+ initialSortBy() {
+ const { sort } = this.getQueryObject();
+ return sort || 'created_desc';
+ },
},
watch: {
selection() {
@@ -262,51 +292,92 @@ export default {
this.filters = filters;
},
+ refetchIssuables() {
+ const ignored = ['utf8', 'state'];
+ const params = omit(this.filters, ignored);
+
+ historyPushState(setUrlParams(params, window.location.href, true));
+ this.fetchIssuables();
+ },
+ handleFilter(filters) {
+ let search = null;
+
+ filters.forEach(filter => {
+ if (typeof filter === 'string') {
+ search = filter;
+ }
+ });
+
+ this.filters.search = search;
+ this.page = 1;
+
+ this.refetchIssuables();
+ },
+ handleSort(sort) {
+ this.filters.sort = sort;
+ this.page = 1;
+
+ this.refetchIssuables();
+ },
},
};
</script>
<template>
- <ul v-if="loading" class="content-list">
- <li v-for="n in $options.LOADING_LIST_ITEMS_LENGTH" :key="n" class="issue gl-px-5! gl-py-5!">
- <gl-skeleton-loading />
- </li>
- </ul>
- <div v-else-if="issuables.length">
- <div v-if="isBulkEditing" class="issue px-3 py-3 border-bottom border-light">
- <input type="checkbox" :checked="allIssuablesSelected" class="mr-2" @click="onSelectAll" />
- <strong>{{ __('Select all') }}</strong>
- </div>
- <ul
- class="content-list issuable-list issues-list"
- :class="{ 'manual-ordering': isManualOrdering }"
- >
- <issuable
- v-for="issuable in issuables"
- :key="issuable.id"
- class="pr-3"
- :class="{ 'user-can-drag': isManualOrdering }"
- :issuable="issuable"
- :is-bulk-editing="isBulkEditing"
- :selected="isSelected(issuable.id)"
- :base-url="baseUrl"
- @select="onSelectIssuable"
- />
+ <div>
+ <filtered-search-bar
+ v-if="isJira"
+ :namespace="projectPath"
+ :search-input-placeholder="__('Search Jira issues')"
+ :tokens="[]"
+ :sort-options="availableSortOptionsJira"
+ :initial-filter-value="initialFilterValue"
+ :initial-sort-by="initialSortBy"
+ class="row-content-block"
+ @onFilter="handleFilter"
+ @onSort="handleSort"
+ />
+ <ul v-if="loading" class="content-list">
+ <li v-for="n in $options.LOADING_LIST_ITEMS_LENGTH" :key="n" class="issue gl-px-5! gl-py-5!">
+ <gl-skeleton-loading />
+ </li>
</ul>
- <div class="mt-3">
- <gl-pagination
- v-bind="paginationProps"
- class="gl-justify-content-center"
- @input="onPaginate"
- />
+ <div v-else-if="issuables.length">
+ <div v-if="isBulkEditing" class="issue px-3 py-3 border-bottom border-light">
+ <input type="checkbox" :checked="allIssuablesSelected" class="mr-2" @click="onSelectAll" />
+ <strong>{{ __('Select all') }}</strong>
+ </div>
+ <ul
+ class="content-list issuable-list issues-list"
+ :class="{ 'manual-ordering': isManualOrdering }"
+ >
+ <issuable
+ v-for="issuable in issuables"
+ :key="issuable.id"
+ class="pr-3"
+ :class="{ 'user-can-drag': isManualOrdering }"
+ :issuable="issuable"
+ :is-bulk-editing="isBulkEditing"
+ :selected="isSelected(issuable.id)"
+ :base-url="baseUrl"
+ @select="onSelectIssuable"
+ />
+ </ul>
+ <div class="mt-3">
+ <gl-pagination
+ v-bind="paginationProps"
+ class="gl-justify-content-center"
+ @input="onPaginate"
+ />
+ </div>
</div>
+ <gl-empty-state
+ v-else
+ :title="emptyState.title"
+ :description="emptyState.description"
+ :svg-path="emptySvgPath"
+ :primary-button-link="emptyState.primaryLink"
+ :primary-button-text="emptyState.primaryText"
+ />
</div>
- <gl-empty-state
- v-else
- :title="emptyState.title"
- :description="emptyState.description"
- :svg-path="emptySvgPath"
- :primary-button-link="emptyState.primaryLink"
- :primary-button-text="emptyState.primaryText"
- />
</template>
diff --git a/app/assets/javascripts/issuables_list/constants.js b/app/assets/javascripts/issuables_list/constants.js
index 71b9c52c703..e240efd2804 100644
--- a/app/assets/javascripts/issuables_list/constants.js
+++ b/app/assets/javascripts/issuables_list/constants.js
@@ -1,3 +1,5 @@
+import { __ } from '~/locale';
+
// Maps sort order as it appears in the URL query to API `order_by` and `sort` params.
const PRIORITY = 'priority';
const ASC = 'asc';
@@ -31,3 +33,22 @@ export const sortOrderMap = {
weight_desc: { order_by: WEIGHT, sort: DESC },
weight: { order_by: WEIGHT, sort: ASC },
};
+
+export const availableSortOptionsJira = [
+ {
+ id: 1,
+ title: __('Created date'),
+ sortDirection: {
+ descending: 'created_desc',
+ ascending: 'created_asc',
+ },
+ },
+ {
+ id: 2,
+ title: __('Last updated'),
+ sortDirection: {
+ descending: 'updated_desc',
+ ascending: 'updated_asc',
+ },
+ },
+];
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index b742fe42024..3f85295a5ed 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -33,7 +33,7 @@ import initFrequentItemDropdowns from './frequent_items';
import initBreadcrumbs from './breadcrumb';
import initUsagePingConsent from './usage_ping_consent';
import initPerformanceBar from './performance_bar';
-import initGlobalSearchInput from './global_search_input';
+import initSearchAutocomplete from './search_autocomplete';
import GlFieldErrors from './gl_field_errors';
import initUserPopovers from './user_popovers';
import initBroadcastNotifications from './broadcast_notification';
@@ -113,7 +113,7 @@ function deferredInitialisation() {
initFrequentItemDropdowns();
initPersistentUserCallouts();
- if (document.querySelector('.search')) initGlobalSearchInput();
+ if (document.querySelector('.search')) initSearchAutocomplete();
addSelectOnFocusBehaviour('.js-select-on-focus');
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index 32af62fe6f1..8a7734f4d31 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -402,9 +402,8 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
};
const pollSuccessCallBack = (resp, commit, state, getters, dispatch) => {
- if (resp.notes && resp.notes.length) {
- updateOrCreateNotes({ commit, state, getters, dispatch }, resp.notes);
-
+ if (resp.notes?.length) {
+ dispatch('updateOrCreateNotes', resp.notes);
dispatch('startTaskList');
}
@@ -424,12 +423,12 @@ const getFetchDataParams = state => {
return { endpoint, options };
};
-export const fetchData = ({ commit, state, getters }) => {
+export const fetchData = ({ commit, state, getters, dispatch }) => {
const { endpoint, options } = getFetchDataParams(state);
axios
.get(endpoint, options)
- .then(({ data }) => pollSuccessCallBack(data, commit, state, getters))
+ .then(({ data }) => pollSuccessCallBack(data, commit, state, getters, dispatch))
.catch(() => Flash(__('Something went wrong while fetching latest comments.')));
};
@@ -449,7 +448,7 @@ export const poll = ({ commit, state, getters, dispatch }) => {
if (!Visibility.hidden()) {
eTagPoll.makeRequest();
} else {
- fetchData({ commit, state, getters });
+ dispatch('fetchData');
}
Visibility.change(() => {
diff --git a/app/assets/javascripts/registry/explorer/components/details_page/tags_list_row.vue b/app/assets/javascripts/registry/explorer/components/details_page/tags_list_row.vue
index d9816dc5102..51ba2337db6 100644
--- a/app/assets/javascripts/registry/explorer/components/details_page/tags_list_row.vue
+++ b/app/assets/javascripts/registry/explorer/components/details_page/tags_list_row.vue
@@ -1,5 +1,5 @@
<script>
-import { GlFormCheckbox, GlTooltipDirective, GlSprintf } from '@gitlab/ui';
+import { GlFormCheckbox, GlTooltipDirective, GlSprintf, GlIcon } from '@gitlab/ui';
import { n__ } from '~/locale';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import { numberToHumanSize } from '~/lib/utils/number_utils';
@@ -16,12 +16,16 @@ import {
PUBLISHED_DETAILS_ROW_TEXT,
MANIFEST_DETAILS_ROW_TEST,
CONFIGURATION_DETAILS_ROW_TEST,
+ MISSING_MANIFEST_WARNING_TOOLTIP,
+ NOT_AVAILABLE_TEXT,
+ NOT_AVAILABLE_SIZE,
} from '../../constants/index';
export default {
components: {
GlSprintf,
GlFormCheckbox,
+ GlIcon,
DeleteButton,
ListItem,
ClipboardButton,
@@ -55,10 +59,11 @@ export default {
PUBLISHED_DETAILS_ROW_TEXT,
MANIFEST_DETAILS_ROW_TEST,
CONFIGURATION_DETAILS_ROW_TEST,
+ MISSING_MANIFEST_WARNING_TOOLTIP,
},
computed: {
formattedSize() {
- return this.tag.total_size ? numberToHumanSize(this.tag.total_size) : '';
+ return this.tag.total_size ? numberToHumanSize(this.tag.total_size) : NOT_AVAILABLE_SIZE;
},
layers() {
return this.tag.layers ? n__('%d layer', '%d layers', this.tag.layers) : '';
@@ -68,7 +73,7 @@ export default {
},
shortDigest() {
// remove sha256: from the string, and show only the first 7 char
- return this.tag.digest?.substring(7, 14);
+ return this.tag.digest?.substring(7, 14) ?? NOT_AVAILABLE_TEXT;
},
publishedDate() {
return formatDate(this.tag.created_at, 'isoDate');
@@ -85,6 +90,9 @@ export default {
tagLocation() {
return this.tag.path?.replace(`:${this.tag.name}`, '');
},
+ invalidTag() {
+ return !this.tag.digest;
+ },
},
};
</script>
@@ -94,6 +102,7 @@ export default {
<template #left-action>
<gl-form-checkbox
v-if="Boolean(tag.destroy_path)"
+ :disabled="invalidTag"
class="gl-m-0"
:checked="selected"
@change="$emit('select')"
@@ -116,6 +125,13 @@ export default {
:text="tag.location"
css-class="btn-default btn-transparent btn-clipboard"
/>
+
+ <gl-icon
+ v-if="invalidTag"
+ v-gl-tooltip="{ title: $options.i18n.MISSING_MANIFEST_WARNING_TOOLTIP }"
+ name="warning"
+ class="gl-text-orange-500 gl-mb-2 gl-ml-2"
+ />
</div>
</template>
@@ -146,7 +162,7 @@ export default {
</template>
<template #right-action>
<delete-button
- :disabled="!tag.destroy_path"
+ :disabled="!tag.destroy_path || invalidTag"
:title="$options.i18n.REMOVE_TAG_BUTTON_TITLE"
:tooltip-title="$options.i18n.REMOVE_TAG_BUTTON_DISABLE_TOOLTIP"
:tooltip-disabled="Boolean(tag.destroy_path)"
@@ -154,7 +170,8 @@ export default {
@delete="$emit('delete')"
/>
</template>
- <template #details_published>
+
+ <template v-if="!invalidTag" #details_published>
<details-row icon="clock" data-testid="published-date-detail">
<gl-sprintf :message="$options.i18n.PUBLISHED_DETAILS_ROW_TEXT">
<template #repositoryPath>
@@ -169,7 +186,7 @@ export default {
</gl-sprintf>
</details-row>
</template>
- <template #details_manifest_digest>
+ <template v-if="!invalidTag" #details_manifest_digest>
<details-row icon="log" data-testid="manifest-detail">
<gl-sprintf :message="$options.i18n.MANIFEST_DETAILS_ROW_TEST">
<template #digest>
@@ -184,7 +201,7 @@ export default {
/>
</details-row>
</template>
- <template #details_configuration_digest>
+ <template v-if="!invalidTag" #details_configuration_digest>
<details-row icon="cloud-gear" data-testid="configuration-detail">
<gl-sprintf :message="$options.i18n.CONFIGURATION_DETAILS_ROW_TEST">
<template #digest>
diff --git a/app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue b/app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue
index e62c0b78efb..2874d89d913 100644
--- a/app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue
+++ b/app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue
@@ -88,7 +88,7 @@ export default {
v-if="item.failedDelete"
v-gl-tooltip="{ title: $options.i18n.ASYNC_DELETE_IMAGE_ERROR_MESSAGE }"
name="warning"
- class="text-warning"
+ class="gl-text-orange-500"
/>
</template>
<template #left-secondary>
diff --git a/app/assets/javascripts/registry/explorer/constants/details.js b/app/assets/javascripts/registry/explorer/constants/details.js
index c6b611114e6..1dc5882d415 100644
--- a/app/assets/javascripts/registry/explorer/constants/details.js
+++ b/app/assets/javascripts/registry/explorer/constants/details.js
@@ -1,4 +1,4 @@
-import { s__ } from '~/locale';
+import { s__, __ } from '~/locale';
// Translations strings
export const DETAILS_PAGE_TITLE = s__('ContainerRegistry|%{imageName} tags');
@@ -48,6 +48,12 @@ export const REMOVE_TAG_BUTTON_DISABLE_TOOLTIP = s__(
'ContainerRegistry|Deletion disabled due to missing or insufficient permissions.',
);
+export const MISSING_MANIFEST_WARNING_TOOLTIP = s__(
+ 'ContainerRegistry|Invalid tag: missing manifest digest',
+);
+
+export const NOT_AVAILABLE_TEXT = __('N/A');
+export const NOT_AVAILABLE_SIZE = __('0 bytes');
// Parameters
export const DEFAULT_PAGE = 1;
diff --git a/app/assets/javascripts/global_search_input.js b/app/assets/javascripts/search_autocomplete.js
index a7c121259d4..05e0b9e7089 100644
--- a/app/assets/javascripts/global_search_input.js
+++ b/app/assets/javascripts/search_autocomplete.js
@@ -1,8 +1,10 @@
/* eslint-disable no-return-assign, consistent-return, class-methods-use-this */
import $ from 'jquery';
-import { throttle } from 'lodash';
+import { escape, throttle } from 'lodash';
import { s__, __, sprintf } from '~/locale';
+import { getIdenticonBackgroundClass, getIdenticonTitle } from '~/helpers/avatar_helper';
+import axios from './lib/utils/axios_utils';
import {
isInGroupsPage,
isInProjectPage,
@@ -65,11 +67,15 @@ function setSearchOptions() {
}
}
-export class GlobalSearchInput {
- constructor({ wrap } = {}) {
+export class SearchAutocomplete {
+ constructor({ wrap, optsEl, autocompletePath, projectId, projectRef } = {}) {
setSearchOptions();
this.bindEventContext();
this.wrap = wrap || $('.search');
+ this.optsEl = optsEl || this.wrap.find('.search-autocomplete-opts');
+ this.autocompletePath = autocompletePath || this.optsEl.data('autocompletePath');
+ this.projectId = projectId || (this.optsEl.data('autocompleteProjectId') || '');
+ this.projectRef = projectRef || (this.optsEl.data('autocompleteProjectRef') || '');
this.dropdown = this.wrap.find('.dropdown');
this.dropdownToggle = this.wrap.find('.js-dropdown-search-toggle');
this.dropdownMenu = this.dropdown.find('.dropdown-menu');
@@ -86,7 +92,7 @@ export class GlobalSearchInput {
// Only when user is logged in
if (gon.current_user_id) {
- this.createGlobalSearchInput();
+ this.createAutocomplete();
}
this.bindEvents();
@@ -111,7 +117,7 @@ export class GlobalSearchInput {
return (this.originalState = this.serializeState());
}
- createGlobalSearchInput() {
+ createAutocomplete() {
return this.searchInput.glDropdown({
filterInputBlur: false,
filterable: true,
@@ -143,17 +149,116 @@ export class GlobalSearchInput {
if (glDropdownInstance) {
glDropdownInstance.filter.options.callback(contents);
}
- this.enableDropdown();
+ this.enableAutocomplete();
}
return;
}
- const options = this.scopedSearchOptions(term);
+ // Prevent multiple ajax calls
+ if (this.loadingSuggestions) {
+ return;
+ }
- callback(options);
+ this.loadingSuggestions = true;
+
+ return axios
+ .get(this.autocompletePath, {
+ params: {
+ project_id: this.projectId,
+ project_ref: this.projectRef,
+ term,
+ },
+ })
+ .then(response => {
+ const options = this.scopedSearchOptions(term);
+
+ // List results
+ let lastCategory = null;
+ for (let i = 0, len = response.data.length; i < len; i += 1) {
+ const suggestion = response.data[i];
+ // Add group header before list each group
+ if (lastCategory !== suggestion.category) {
+ options.push({ type: 'separator' });
+ options.push({
+ type: 'header',
+ content: suggestion.category,
+ });
+ lastCategory = suggestion.category;
+ }
+
+ // Add the suggestion
+ options.push({
+ id: `${suggestion.category.toLowerCase()}-${suggestion.id}`,
+ icon: this.getAvatar(suggestion),
+ category: suggestion.category,
+ text: suggestion.label,
+ url: suggestion.url,
+ });
+ }
- this.highlightFirstRow();
- this.setScrollFade();
+ callback(options);
+
+ this.loadingSuggestions = false;
+ this.highlightFirstRow();
+ this.setScrollFade();
+ })
+ .catch(() => {
+ this.loadingSuggestions = false;
+ });
+ }
+
+ getCategoryContents() {
+ const userName = gon.current_username;
+ const { projectOptions, groupOptions, dashboardOptions } = gl;
+
+ // Get options
+ let options;
+ if (isInProjectPage() && projectOptions) {
+ options = projectOptions[getProjectSlug()];
+ } else if (isInGroupsPage() && groupOptions) {
+ options = groupOptions[getGroupSlug()];
+ } else if (dashboardOptions) {
+ options = dashboardOptions;
+ }
+
+ const { issuesPath, mrPath, name, issuesDisabled } = options;
+ const baseItems = [];
+
+ if (name) {
+ baseItems.push({
+ type: 'header',
+ content: `${name}`,
+ });
+ }
+
+ const issueItems = [
+ {
+ text: s__('SearchAutocomplete|Issues assigned to me'),
+ url: `${issuesPath}/?assignee_username=${userName}`,
+ },
+ {
+ text: s__("SearchAutocomplete|Issues I've created"),
+ url: `${issuesPath}/?author_username=${userName}`,
+ },
+ ];
+ const mergeRequestItems = [
+ {
+ text: s__('SearchAutocomplete|Merge requests assigned to me'),
+ url: `${mrPath}/?assignee_username=${userName}`,
+ },
+ {
+ text: s__("SearchAutocomplete|Merge requests I've created"),
+ url: `${mrPath}/?author_username=${userName}`,
+ },
+ ];
+
+ let items;
+ if (issuesDisabled) {
+ items = baseItems.concat(mergeRequestItems);
+ } else {
+ items = baseItems.concat(...issueItems, ...mergeRequestItems);
+ }
+ return items;
}
// Add option to proceed with the search for each
@@ -238,7 +343,7 @@ export class GlobalSearchInput {
});
}
- enableDropdown() {
+ enableAutocomplete() {
this.setScrollFade();
// No need to enable anything if user is not logged in
@@ -255,7 +360,7 @@ export class GlobalSearchInput {
}
onSearchInputChange() {
- this.enableDropdown();
+ this.enableAutocomplete();
}
onSearchInputKeyUp(e) {
@@ -264,7 +369,7 @@ export class GlobalSearchInput {
this.restoreOriginalState();
break;
case KEYCODE.ENTER:
- this.disableDropdown();
+ this.disableAutocomplete();
break;
default:
}
@@ -317,7 +422,7 @@ export class GlobalSearchInput {
return results;
}
- disableDropdown() {
+ disableAutocomplete() {
if (!this.searchInput.hasClass('js-autocomplete-disabled') && this.dropdown.hasClass('show')) {
this.searchInput.addClass('js-autocomplete-disabled');
this.dropdownToggle.dropdown('toggle');
@@ -333,8 +438,16 @@ export class GlobalSearchInput {
onClick(item, $el, e) {
if (window.location.pathname.indexOf(item.url) !== -1) {
if (!e.metaKey) e.preventDefault();
+ /* eslint-disable-next-line @gitlab/require-i18n-strings */
+ if (item.category === 'Projects') {
+ this.projectInputEl.val(item.id);
+ }
+ // eslint-disable-next-line @gitlab/require-i18n-strings
+ if (item.category === 'Groups') {
+ this.groupInputEl.val(item.id);
+ }
$el.removeClass('is-active');
- this.disableDropdown();
+ this.disableAutocomplete();
return this.searchInput.val('').focus();
}
}
@@ -343,58 +456,20 @@ export class GlobalSearchInput {
this.searchInput.data('glDropdown').highlightRowAtIndex(null, 0);
}
- getCategoryContents() {
- const userName = gon.current_username;
- const { projectOptions, groupOptions, dashboardOptions } = gl;
-
- // Get options
- let options;
- if (isInProjectPage() && projectOptions) {
- options = projectOptions[getProjectSlug()];
- } else if (isInGroupsPage() && groupOptions) {
- options = groupOptions[getGroupSlug()];
- } else if (dashboardOptions) {
- options = dashboardOptions;
+ getAvatar(item) {
+ if (!Object.hasOwnProperty.call(item, 'avatar_url')) {
+ return false;
}
- const { issuesPath, mrPath, name, issuesDisabled } = options;
- const baseItems = [];
-
- if (name) {
- baseItems.push({
- type: 'header',
- content: `${name}`,
- });
- }
+ const { label, id } = item;
+ const avatarUrl = item.avatar_url;
+ const avatar = avatarUrl
+ ? `<img class="search-item-avatar" src="${avatarUrl}" />`
+ : `<div class="s16 avatar identicon ${getIdenticonBackgroundClass(id)}">${getIdenticonTitle(
+ escape(label),
+ )}</div>`;
- const issueItems = [
- {
- text: s__('SearchAutocomplete|Issues assigned to me'),
- url: `${issuesPath}/?assignee_username=${userName}`,
- },
- {
- text: s__("SearchAutocomplete|Issues I've created"),
- url: `${issuesPath}/?author_username=${userName}`,
- },
- ];
- const mergeRequestItems = [
- {
- text: s__('SearchAutocomplete|Merge requests assigned to me'),
- url: `${mrPath}/?assignee_username=${userName}`,
- },
- {
- text: s__("SearchAutocomplete|Merge requests I've created"),
- url: `${mrPath}/?author_username=${userName}`,
- },
- ];
-
- let items;
- if (issuesDisabled) {
- items = baseItems.concat(mergeRequestItems);
- } else {
- items = baseItems.concat(...issueItems, ...mergeRequestItems);
- }
- return items;
+ return avatar;
}
isScrolledUp() {
@@ -420,6 +495,6 @@ export class GlobalSearchInput {
}
}
-export default function initGlobalSearchInput(opts) {
- return new GlobalSearchInput(opts);
+export default function initSearchAutocomplete(opts) {
+ return new SearchAutocomplete(opts);
}