summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/search
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/search')
-rw-r--r--app/assets/javascripts/search/index.js5
-rw-r--r--app/assets/javascripts/search/store/actions.js1
-rw-r--r--app/assets/javascripts/search/topbar/components/app.vue2
-rw-r--r--app/assets/javascripts/search/topbar/components/group_filter.vue4
-rw-r--r--app/assets/javascripts/search/topbar/components/project_filter.vue4
-rw-r--r--app/assets/javascripts/search/topbar/components/searchable_dropdown.vue38
-rw-r--r--app/assets/javascripts/search/topbar/components/searchable_dropdown_item.vue73
-rw-r--r--app/assets/javascripts/search/topbar/constants.js8
8 files changed, 107 insertions, 28 deletions
diff --git a/app/assets/javascripts/search/index.js b/app/assets/javascripts/search/index.js
index 10c41315972..d9d4056466a 100644
--- a/app/assets/javascripts/search/index.js
+++ b/app/assets/javascripts/search/index.js
@@ -8,10 +8,7 @@ import createStore from './store';
import { initTopbar } from './topbar';
export const initSearchApp = () => {
- // Similar to url_utility.decodeUrlParameter
- // Our query treats + as %20. This replaces the query + symbols with %20.
- const sanitizedSearch = window.location.search.replace(/\+/g, '%20');
- const query = queryToObject(sanitizedSearch);
+ const query = queryToObject(window.location.search);
const store = createStore({ query });
diff --git a/app/assets/javascripts/search/store/actions.js b/app/assets/javascripts/search/store/actions.js
index 0af679644f3..0c3f273fec7 100644
--- a/app/assets/javascripts/search/store/actions.js
+++ b/app/assets/javascripts/search/store/actions.js
@@ -29,6 +29,7 @@ export const fetchProjects = ({ commit, state }, search) => {
};
if (groupId) {
+ // TODO (https://gitlab.com/gitlab-org/gitlab/-/issues/323331): For errors `createFlash` is called twice; in `callback` and in `Api.groupProjects`
Api.groupProjects(groupId, search, {}, callback);
} else {
// The .catch() is due to the API method not handling a rejection properly
diff --git a/app/assets/javascripts/search/topbar/components/app.vue b/app/assets/javascripts/search/topbar/components/app.vue
index 2439ab55923..a490adbc11a 100644
--- a/app/assets/javascripts/search/topbar/components/app.vue
+++ b/app/assets/javascripts/search/topbar/components/app.vue
@@ -48,7 +48,7 @@ export default {
<template>
<gl-form class="search-page-form" @submit.prevent="applyQuery">
<section class="gl-lg-display-flex gl-align-items-flex-end">
- <div class="gl-flex-fill-1 gl-mb-4 gl-lg-mb-0 gl-lg-mr-2">
+ <div class="gl-flex-grow-1 gl-mb-4 gl-lg-mb-0 gl-lg-mr-2">
<label>{{ __('What are you searching for?') }}</label>
<gl-search-box-by-type
id="dashboard_search"
diff --git a/app/assets/javascripts/search/topbar/components/group_filter.vue b/app/assets/javascripts/search/topbar/components/group_filter.vue
index 2acab4e805d..da9252eeacd 100644
--- a/app/assets/javascripts/search/topbar/components/group_filter.vue
+++ b/app/assets/javascripts/search/topbar/components/group_filter.vue
@@ -39,8 +39,8 @@ export default {
<searchable-dropdown
data-testid="group-filter"
:header-text="$options.GROUP_DATA.headerText"
- :selected-display-value="$options.GROUP_DATA.selectedDisplayValue"
- :items-display-value="$options.GROUP_DATA.itemsDisplayValue"
+ :name="$options.GROUP_DATA.name"
+ :full-name="$options.GROUP_DATA.fullName"
:loading="fetchingGroups"
:selected-item="selectedGroup"
:items="groups"
diff --git a/app/assets/javascripts/search/topbar/components/project_filter.vue b/app/assets/javascripts/search/topbar/components/project_filter.vue
index b2dd79fcfa3..dbe8ba54216 100644
--- a/app/assets/javascripts/search/topbar/components/project_filter.vue
+++ b/app/assets/javascripts/search/topbar/components/project_filter.vue
@@ -42,8 +42,8 @@ export default {
<searchable-dropdown
data-testid="project-filter"
:header-text="$options.PROJECT_DATA.headerText"
- :selected-display-value="$options.PROJECT_DATA.selectedDisplayValue"
- :items-display-value="$options.PROJECT_DATA.itemsDisplayValue"
+ :name="$options.PROJECT_DATA.name"
+ :full-name="$options.PROJECT_DATA.fullName"
:loading="fetchingProjects"
:selected-item="selectedProject"
:items="projects"
diff --git a/app/assets/javascripts/search/topbar/components/searchable_dropdown.vue b/app/assets/javascripts/search/topbar/components/searchable_dropdown.vue
index d16850cd889..2e2aa052dd8 100644
--- a/app/assets/javascripts/search/topbar/components/searchable_dropdown.vue
+++ b/app/assets/javascripts/search/topbar/components/searchable_dropdown.vue
@@ -11,6 +11,7 @@ import {
} from '@gitlab/ui';
import { __ } from '~/locale';
import { ANY_OPTION } from '../constants';
+import SearchableDropdownItem from './searchable_dropdown_item.vue';
export default {
i18n: {
@@ -25,6 +26,7 @@ export default {
GlIcon,
GlButton,
GlSkeletonLoader,
+ SearchableDropdownItem,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -35,12 +37,12 @@ export default {
required: false,
default: "__('Filter')",
},
- selectedDisplayValue: {
+ name: {
type: String,
required: false,
default: 'name',
},
- itemsDisplayValue: {
+ fullName: {
type: String,
required: false,
default: 'name',
@@ -75,6 +77,9 @@ export default {
resetDropdown() {
this.$emit('change', ANY_OPTION);
},
+ updateDropdown(item) {
+ this.$emit('change', item);
+ },
},
ANY_OPTION,
};
@@ -83,15 +88,16 @@ export default {
<template>
<gl-dropdown
class="gl-w-full"
- menu-class="gl-w-full!"
+ menu-class="global-search-dropdown-menu"
toggle-class="gl-text-truncate"
:header-text="headerText"
- @show="$emit('search', searchText)"
+ :right="true"
+ @show="openDropdown"
@shown="$refs.searchBox.focusInput()"
>
<template #button-content>
<span class="dropdown-toggle-text gl-flex-grow-1 gl-text-truncate">
- {{ selectedItem[selectedDisplayValue] }}
+ {{ selectedItem[name] }}
</span>
<gl-loading-icon v-if="loading" inline class="gl-mr-3" />
<gl-button
@@ -115,27 +121,29 @@ export default {
v-model="searchText"
class="gl-m-3"
:debounce="500"
- @input="$emit('search', searchText)"
+ @input="openDropdown"
/>
<gl-dropdown-item
class="gl-border-b-solid gl-border-b-gray-100 gl-border-b-1 gl-pb-2! gl-mb-2"
:is-check-item="true"
:is-checked="isSelected($options.ANY_OPTION)"
- @click="resetDropdown"
+ :is-check-centered="true"
+ @click="updateDropdown($options.ANY_OPTION)"
>
- {{ $options.ANY_OPTION.name }}
+ <span data-testid="item-title">{{ $options.ANY_OPTION.name }}</span>
</gl-dropdown-item>
</div>
<div v-if="!loading">
- <gl-dropdown-item
+ <searchable-dropdown-item
v-for="item in items"
:key="item.id"
- :is-check-item="true"
- :is-checked="isSelected(item)"
- @click="$emit('change', item)"
- >
- {{ item[itemsDisplayValue] }}
- </gl-dropdown-item>
+ :item="item"
+ :selected-item="selectedItem"
+ :search-text="searchText"
+ :name="name"
+ :full-name="fullName"
+ @change="updateDropdown"
+ />
</div>
<div v-if="loading" class="gl-mx-4 gl-mt-3">
<gl-skeleton-loader :height="100">
diff --git a/app/assets/javascripts/search/topbar/components/searchable_dropdown_item.vue b/app/assets/javascripts/search/topbar/components/searchable_dropdown_item.vue
new file mode 100644
index 00000000000..498d4af59b4
--- /dev/null
+++ b/app/assets/javascripts/search/topbar/components/searchable_dropdown_item.vue
@@ -0,0 +1,73 @@
+<script>
+import { GlDropdownItem, GlAvatar } from '@gitlab/ui';
+import highlight from '~/lib/utils/highlight';
+import { truncateNamespace } from '~/lib/utils/text_utility';
+
+export default {
+ name: 'SearchableDropdownItem',
+ components: {
+ GlDropdownItem,
+ GlAvatar,
+ },
+ props: {
+ item: {
+ type: Object,
+ required: true,
+ },
+ selectedItem: {
+ type: Object,
+ required: true,
+ },
+ searchText: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ name: {
+ type: String,
+ required: true,
+ },
+ fullName: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ isSelected() {
+ return this.item.id === this.selectedItem.id;
+ },
+ truncatedNamespace() {
+ return truncateNamespace(this.item[this.fullName]);
+ },
+ highlightedItemName() {
+ return highlight(this.item[this.name], this.searchText);
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-dropdown-item
+ :is-check-item="true"
+ :is-checked="isSelected"
+ :is-check-centered="true"
+ @click="$emit('change', item)"
+ >
+ <div class="gl-display-flex gl-align-items-center">
+ <gl-avatar
+ :src="item.avatar_url"
+ :entity-id="item.id"
+ :entity-name="item[name]"
+ shape="rect"
+ :size="32"
+ />
+ <div class="gl-display-flex gl-flex-direction-column">
+ <!-- eslint-disable-next-line vue/no-v-html -->
+ <span data-testid="item-title" v-html="highlightedItemName">{{ item[name] }}</span>
+ <span class="gl-font-sm gl-text-gray-700" data-testid="item-namespace">{{
+ truncatedNamespace
+ }}</span>
+ </div>
+ </div>
+ </gl-dropdown-item>
+</template>
diff --git a/app/assets/javascripts/search/topbar/constants.js b/app/assets/javascripts/search/topbar/constants.js
index 3944b2c8374..dc040fdef34 100644
--- a/app/assets/javascripts/search/topbar/constants.js
+++ b/app/assets/javascripts/search/topbar/constants.js
@@ -9,13 +9,13 @@ export const ANY_OPTION = Object.freeze({
export const GROUP_DATA = {
headerText: __('Filter results by group'),
queryParam: 'group_id',
- selectedDisplayValue: 'name',
- itemsDisplayValue: 'full_name',
+ name: 'name',
+ fullName: 'full_name',
};
export const PROJECT_DATA = {
headerText: __('Filter results by project'),
queryParam: 'project_id',
- selectedDisplayValue: 'name_with_namespace',
- itemsDisplayValue: 'name_with_namespace',
+ name: 'name',
+ fullName: 'name_with_namespace',
};