diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-09-15 21:11:29 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-09-15 21:11:29 +0300 |
commit | 27d1ed4ddff6c2649544a968c2842140272d9c9d (patch) | |
tree | 93a68b94ece233b47284a9c7ad8cabf31465212c /app/assets/javascripts/header_search | |
parent | 6e2dde590e694c13efdd441e058a925dcff17258 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/header_search')
10 files changed, 174 insertions, 6 deletions
diff --git a/app/assets/javascripts/header_search/components/app.vue b/app/assets/javascripts/header_search/components/app.vue index cdec3d8eec3..580c27f6c61 100644 --- a/app/assets/javascripts/header_search/components/app.vue +++ b/app/assets/javascripts/header_search/components/app.vue @@ -1,7 +1,10 @@ <script> import { GlSearchBoxByType, GlOutsideDirective as Outside } from '@gitlab/ui'; +import { mapState, mapActions, mapGetters } from 'vuex'; +import { visitUrl } from '~/lib/utils/url_utility'; import { __ } from '~/locale'; import HeaderSearchDefaultItems from './header_search_default_items.vue'; +import HeaderSearchScopedItems from './header_search_scoped_items.vue'; export default { name: 'HeaderSearchApp', @@ -12,6 +15,7 @@ export default { components: { GlSearchBoxByType, HeaderSearchDefaultItems, + HeaderSearchScopedItems, }, data() { return { @@ -19,17 +23,34 @@ export default { }; }, computed: { + ...mapState(['search']), + ...mapGetters(['searchQuery']), + searchText: { + get() { + return this.search; + }, + set(value) { + this.setSearch(value); + }, + }, showSearchDropdown() { return this.showDropdown && gon?.current_username; }, + showDefaultItems() { + return !this.searchText; + }, }, methods: { + ...mapActions(['setSearch']), openDropdown() { this.showDropdown = true; }, closeDropdown() { this.showDropdown = false; }, + submitSearch() { + return visitUrl(this.searchQuery); + }, }, }; </script> @@ -37,10 +58,13 @@ export default { <template> <section v-outside="closeDropdown" class="header-search gl-relative"> <gl-search-box-by-type + v-model="searchText" + :debounce="500" autocomplete="off" :placeholder="$options.i18n.searchPlaceholder" @focus="openDropdown" @click="openDropdown" + @keydown.enter="submitSearch" @keydown.esc="closeDropdown" /> <div @@ -49,7 +73,10 @@ export default { class="header-search-dropdown-menu gl-overflow-y-auto gl-absolute gl-left-0 gl-z-index-1 gl-w-full gl-bg-white gl-border-1 gl-rounded-base gl-border-solid gl-border-gray-200 gl-shadow-x0-y2-b4-s0" > <div class="header-search-dropdown-content gl-overflow-y-auto gl-py-2"> - <header-search-default-items /> + <header-search-default-items v-if="showDefaultItems" /> + <template v-else> + <header-search-scoped-items /> + </template> </div> </div> </section> diff --git a/app/assets/javascripts/header_search/components/header_search_scoped_items.vue b/app/assets/javascripts/header_search/components/header_search_scoped_items.vue new file mode 100644 index 00000000000..645eba05148 --- /dev/null +++ b/app/assets/javascripts/header_search/components/header_search_scoped_items.vue @@ -0,0 +1,31 @@ +<script> +import { GlDropdownItem } from '@gitlab/ui'; +import { mapState, mapGetters } from 'vuex'; + +export default { + name: 'HeaderSearchScopedItems', + components: { + GlDropdownItem, + }, + computed: { + ...mapState(['search']), + ...mapGetters(['scopedSearchOptions']), + }, +}; +</script> + +<template> + <div> + <gl-dropdown-item + v-for="(option, index) in scopedSearchOptions" + :id="`scoped-${index}`" + :key="index" + tabindex="-1" + :href="option.url" + > + "<span class="gl-font-weight-bold">{{ search }}</span + >" {{ option.description }} + <span v-if="option.scope" class="gl-font-style-italic">{{ option.scope }}</span> + </gl-dropdown-item> + </div> +</template> diff --git a/app/assets/javascripts/header_search/constants.js b/app/assets/javascripts/header_search/constants.js index 64e56156c2f..fffed7bcbdb 100644 --- a/app/assets/javascripts/header_search/constants.js +++ b/app/assets/javascripts/header_search/constants.js @@ -9,3 +9,9 @@ export const MSG_MR_ASSIGNED_TO_ME = __('Merge requests assigned to me'); export const MSG_MR_IM_REVIEWER = __("Merge requests that I'm a reviewer"); export const MSG_MR_IVE_CREATED = __("Merge requests I've created"); + +export const MSG_IN_ALL_GITLAB = __('in all GitLab'); + +export const MSG_IN_GROUP = __('in group'); + +export const MSG_IN_PROJECT = __('in project'); diff --git a/app/assets/javascripts/header_search/index.js b/app/assets/javascripts/header_search/index.js index 0881db16be3..2d37ee137fc 100644 --- a/app/assets/javascripts/header_search/index.js +++ b/app/assets/javascripts/header_search/index.js @@ -12,13 +12,13 @@ export const initHeaderSearchApp = () => { return false; } - const { issuesPath, mrPath } = el.dataset; + const { searchPath, issuesPath, mrPath } = el.dataset; let { searchContext } = el.dataset; searchContext = JSON.parse(searchContext); return new Vue({ el, - store: createStore({ issuesPath, mrPath, searchContext }), + store: createStore({ searchPath, issuesPath, mrPath, searchContext }), render(createElement) { return createElement(HeaderSearchApp); }, diff --git a/app/assets/javascripts/header_search/store/actions.js b/app/assets/javascripts/header_search/store/actions.js new file mode 100644 index 00000000000..841aee04029 --- /dev/null +++ b/app/assets/javascripts/header_search/store/actions.js @@ -0,0 +1,5 @@ +import * as types from './mutation_types'; + +export const setSearch = ({ commit }, value) => { + commit(types.SET_SEARCH, value); +}; diff --git a/app/assets/javascripts/header_search/store/getters.js b/app/assets/javascripts/header_search/store/getters.js index 1feb0e519ba..d1e1fc8ad73 100644 --- a/app/assets/javascripts/header_search/store/getters.js +++ b/app/assets/javascripts/header_search/store/getters.js @@ -1,11 +1,28 @@ +import { objectToQuery } from '~/lib/utils/url_utility'; + import { MSG_ISSUES_ASSIGNED_TO_ME, MSG_ISSUES_IVE_CREATED, MSG_MR_ASSIGNED_TO_ME, MSG_MR_IM_REVIEWER, MSG_MR_IVE_CREATED, + MSG_IN_PROJECT, + MSG_IN_GROUP, + MSG_IN_ALL_GITLAB, } from '../constants'; +export const searchQuery = (state) => { + const query = { + search: state.search, + nav_source: 'navbar', + project_id: state.searchContext.project?.id, + group_id: state.searchContext.group?.id, + scope: state.searchContext.scope, + }; + + return `${state.searchPath}?${objectToQuery(query)}`; +}; + export const scopedIssuesPath = (state) => { return ( state.searchContext.project_metadata?.issues_path || @@ -48,3 +65,71 @@ export const defaultSearchOptions = (state, getters) => { }, ]; }; + +export const projectUrl = (state) => { + if (!state.searchContext.project || !state.searchContext.group) { + return null; + } + + const query = { + search: state.search, + nav_source: 'navbar', + project_id: state.searchContext.project.id, + group_id: state.searchContext.group.id, + scope: state.searchContext.scope, + }; + + return `${state.searchPath}?${objectToQuery(query)}`; +}; + +export const groupUrl = (state) => { + if (!state.searchContext.group) { + return null; + } + + const query = { + search: state.search, + nav_source: 'navbar', + group_id: state.searchContext.group.id, + scope: state.searchContext.scope, + }; + + return `${state.searchPath}?${objectToQuery(query)}`; +}; + +export const allUrl = (state) => { + const query = { + search: state.search, + nav_source: 'navbar', + scope: state.searchContext.scope, + }; + + return `${state.searchPath}?${objectToQuery(query)}`; +}; + +export const scopedSearchOptions = (state, getters) => { + const options = []; + + if (state.searchContext.project) { + options.push({ + scope: state.searchContext.project.name, + description: MSG_IN_PROJECT, + url: getters.projectUrl, + }); + } + + if (state.searchContext.group) { + options.push({ + scope: state.searchContext.group.name, + description: MSG_IN_GROUP, + url: getters.groupUrl, + }); + } + + options.push({ + description: MSG_IN_ALL_GITLAB, + url: getters.allUrl, + }); + + return options; +}; diff --git a/app/assets/javascripts/header_search/store/index.js b/app/assets/javascripts/header_search/store/index.js index 066e02aed9f..8b74f8662a5 100644 --- a/app/assets/javascripts/header_search/store/index.js +++ b/app/assets/javascripts/header_search/store/index.js @@ -1,13 +1,17 @@ import Vue from 'vue'; import Vuex from 'vuex'; +import * as actions from './actions'; import * as getters from './getters'; +import mutations from './mutations'; import createState from './state'; Vue.use(Vuex); -export const getStoreConfig = ({ issuesPath, mrPath, searchContext }) => ({ +export const getStoreConfig = ({ searchPath, issuesPath, mrPath, searchContext }) => ({ + actions, getters, - state: createState({ issuesPath, mrPath, searchContext }), + mutations, + state: createState({ searchPath, issuesPath, mrPath, searchContext }), }); const createStore = (config) => new Vuex.Store(getStoreConfig(config)); diff --git a/app/assets/javascripts/header_search/store/mutation_types.js b/app/assets/javascripts/header_search/store/mutation_types.js new file mode 100644 index 00000000000..0bc94ae055f --- /dev/null +++ b/app/assets/javascripts/header_search/store/mutation_types.js @@ -0,0 +1 @@ +export const SET_SEARCH = 'SET_SEARCH'; diff --git a/app/assets/javascripts/header_search/store/mutations.js b/app/assets/javascripts/header_search/store/mutations.js new file mode 100644 index 00000000000..5b1438929d4 --- /dev/null +++ b/app/assets/javascripts/header_search/store/mutations.js @@ -0,0 +1,7 @@ +import * as types from './mutation_types'; + +export default { + [types.SET_SEARCH](state, value) { + state.search = value; + }, +}; diff --git a/app/assets/javascripts/header_search/store/state.js b/app/assets/javascripts/header_search/store/state.js index 94a238a24ee..fb2c83dbbe3 100644 --- a/app/assets/javascripts/header_search/store/state.js +++ b/app/assets/javascripts/header_search/store/state.js @@ -1,6 +1,8 @@ -const createState = ({ issuesPath, mrPath, searchContext }) => ({ +const createState = ({ searchPath, issuesPath, mrPath, searchContext }) => ({ + searchPath, issuesPath, mrPath, searchContext, + search: '', }); export default createState; |