diff options
Diffstat (limited to 'app/assets/javascripts/search')
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', }; |