Commit fc277453 authored by Vitaly Slobodin's avatar Vitaly Slobodin

Merge branch '345745_03-fix-search-debounce' into 'master'

Global Search Refactor - Fix Search Debounce

See merge request gitlab-org/gitlab!76496
parents 641dfe75 bdbe7a55
<script>
import { GlSearchBoxByType, GlOutsideDirective as Outside } from '@gitlab/ui';
import { mapState, mapActions, mapGetters } from 'vuex';
import { debounce } from 'lodash';
import { visitUrl } from '~/lib/utils/url_utility';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import { s__, sprintf } from '~/locale';
import DropdownKeyboardNavigation from '~/vue_shared/components/dropdown_keyboard_navigation.vue';
import {
......@@ -106,7 +108,7 @@ export default {
},
},
methods: {
...mapActions(['setSearch', 'fetchAutocompleteOptions']),
...mapActions(['setSearch', 'fetchAutocompleteOptions', 'clearAutocomplete']),
openDropdown() {
this.showDropdown = true;
},
......@@ -116,13 +118,13 @@ export default {
submitSearch() {
return visitUrl(this.currentFocusedOption?.url || this.searchQuery);
},
getAutocompleteOptions(searchTerm) {
getAutocompleteOptions: debounce(function debouncedSearch(searchTerm) {
if (!searchTerm) {
return;
this.clearAutocomplete();
} else {
this.fetchAutocompleteOptions();
}
this.fetchAutocompleteOptions();
},
}, DEFAULT_DEBOUNCE_AND_THROTTLE_MS),
},
SEARCH_BOX_INDEX,
SEARCH_INPUT_DESCRIPTION,
......@@ -141,7 +143,6 @@ export default {
v-model="searchText"
role="searchbox"
class="gl-z-index-1"
:debounce="500"
autocomplete="off"
:placeholder="$options.i18n.searchPlaceholder"
:aria-activedescendant="currentFocusedId"
......
......@@ -14,6 +14,10 @@ export const fetchAutocompleteOptions = ({ commit, getters }) => {
});
};
export const clearAutocomplete = ({ commit }) => {
commit(types.CLEAR_AUTOCOMPLETE);
};
export const setSearch = ({ commit }, value) => {
commit(types.SET_SEARCH, value);
};
export const REQUEST_AUTOCOMPLETE = 'REQUEST_AUTOCOMPLETE';
export const RECEIVE_AUTOCOMPLETE_SUCCESS = 'RECEIVE_AUTOCOMPLETE_SUCCESS';
export const RECEIVE_AUTOCOMPLETE_ERROR = 'RECEIVE_AUTOCOMPLETE_ERROR';
export const CLEAR_AUTOCOMPLETE = 'CLEAR_AUTOCOMPLETE';
export const SET_SEARCH = 'SET_SEARCH';
......@@ -15,6 +15,9 @@ export default {
state.loading = false;
state.autocompleteOptions = [];
},
[types.CLEAR_AUTOCOMPLETE](state) {
state.autocompleteOptions = [];
},
[types.SET_SEARCH](state, value) {
state.search = value;
},
......
......@@ -30,6 +30,7 @@ describe('HeaderSearchApp', () => {
const actionSpies = {
setSearch: jest.fn(),
fetchAutocompleteOptions: jest.fn(),
clearAutocomplete: jest.fn(),
};
const createComponent = (initialState, mockGetters) => {
......@@ -217,16 +218,40 @@ describe('HeaderSearchApp', () => {
});
describe('onInput', () => {
beforeEach(() => {
findHeaderSearchInput().vm.$emit('input', MOCK_SEARCH);
});
describe('when search has text', () => {
beforeEach(() => {
findHeaderSearchInput().vm.$emit('input', MOCK_SEARCH);
});
it('calls setSearch with search term', () => {
expect(actionSpies.setSearch).toHaveBeenCalledWith(expect.any(Object), MOCK_SEARCH);
it('calls setSearch with search term', () => {
expect(actionSpies.setSearch).toHaveBeenCalledWith(expect.any(Object), MOCK_SEARCH);
});
it('calls fetchAutocompleteOptions', () => {
expect(actionSpies.fetchAutocompleteOptions).toHaveBeenCalled();
});
it('does not call clearAutocomplete', () => {
expect(actionSpies.clearAutocomplete).not.toHaveBeenCalled();
});
});
it('calls fetchAutocompleteOptions', () => {
expect(actionSpies.fetchAutocompleteOptions).toHaveBeenCalled();
describe('when search is emptied', () => {
beforeEach(() => {
findHeaderSearchInput().vm.$emit('input', '');
});
it('calls setSearch with empty term', () => {
expect(actionSpies.setSearch).toHaveBeenCalledWith(expect.any(Object), '');
});
it('does not call fetchAutocompleteOptions', () => {
expect(actionSpies.fetchAutocompleteOptions).not.toHaveBeenCalled();
});
it('calls clearAutocomplete', () => {
expect(actionSpies.clearAutocomplete).toHaveBeenCalled();
});
});
});
});
......
......@@ -47,6 +47,16 @@ describe('Header Search Store Actions', () => {
});
});
describe('clearAutocomplete', () => {
it('calls the CLEAR_AUTOCOMPLETE mutation', () => {
return testAction({
action: actions.clearAutocomplete,
state,
expectedMutations: [{ type: types.CLEAR_AUTOCOMPLETE }],
});
});
});
describe('setSearch', () => {
it('calls the SET_SEARCH mutation', () => {
return testAction({
......
......@@ -41,6 +41,14 @@ describe('Header Search Store Mutations', () => {
});
});
describe('CLEAR_AUTOCOMPLETE', () => {
it('empties autocompleteOptions array', () => {
mutations[types.CLEAR_AUTOCOMPLETE](state);
expect(state.autocompleteOptions).toStrictEqual([]);
});
});
describe('SET_SEARCH', () => {
it('sets search to value', () => {
mutations[types.SET_SEARCH](state, MOCK_SEARCH);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment