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> <script>
import { GlSearchBoxByType, GlOutsideDirective as Outside } from '@gitlab/ui'; import { GlSearchBoxByType, GlOutsideDirective as Outside } from '@gitlab/ui';
import { mapState, mapActions, mapGetters } from 'vuex'; import { mapState, mapActions, mapGetters } from 'vuex';
import { debounce } from 'lodash';
import { visitUrl } from '~/lib/utils/url_utility'; import { visitUrl } from '~/lib/utils/url_utility';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import { s__, sprintf } from '~/locale'; import { s__, sprintf } from '~/locale';
import DropdownKeyboardNavigation from '~/vue_shared/components/dropdown_keyboard_navigation.vue'; import DropdownKeyboardNavigation from '~/vue_shared/components/dropdown_keyboard_navigation.vue';
import { import {
...@@ -106,7 +108,7 @@ export default { ...@@ -106,7 +108,7 @@ export default {
}, },
}, },
methods: { methods: {
...mapActions(['setSearch', 'fetchAutocompleteOptions']), ...mapActions(['setSearch', 'fetchAutocompleteOptions', 'clearAutocomplete']),
openDropdown() { openDropdown() {
this.showDropdown = true; this.showDropdown = true;
}, },
...@@ -116,13 +118,13 @@ export default { ...@@ -116,13 +118,13 @@ export default {
submitSearch() { submitSearch() {
return visitUrl(this.currentFocusedOption?.url || this.searchQuery); return visitUrl(this.currentFocusedOption?.url || this.searchQuery);
}, },
getAutocompleteOptions(searchTerm) { getAutocompleteOptions: debounce(function debouncedSearch(searchTerm) {
if (!searchTerm) { if (!searchTerm) {
return; this.clearAutocomplete();
} else {
this.fetchAutocompleteOptions();
} }
}, DEFAULT_DEBOUNCE_AND_THROTTLE_MS),
this.fetchAutocompleteOptions();
},
}, },
SEARCH_BOX_INDEX, SEARCH_BOX_INDEX,
SEARCH_INPUT_DESCRIPTION, SEARCH_INPUT_DESCRIPTION,
...@@ -141,7 +143,6 @@ export default { ...@@ -141,7 +143,6 @@ export default {
v-model="searchText" v-model="searchText"
role="searchbox" role="searchbox"
class="gl-z-index-1" class="gl-z-index-1"
:debounce="500"
autocomplete="off" autocomplete="off"
:placeholder="$options.i18n.searchPlaceholder" :placeholder="$options.i18n.searchPlaceholder"
:aria-activedescendant="currentFocusedId" :aria-activedescendant="currentFocusedId"
......
...@@ -14,6 +14,10 @@ export const fetchAutocompleteOptions = ({ commit, getters }) => { ...@@ -14,6 +14,10 @@ export const fetchAutocompleteOptions = ({ commit, getters }) => {
}); });
}; };
export const clearAutocomplete = ({ commit }) => {
commit(types.CLEAR_AUTOCOMPLETE);
};
export const setSearch = ({ commit }, value) => { export const setSearch = ({ commit }, value) => {
commit(types.SET_SEARCH, value); commit(types.SET_SEARCH, value);
}; };
export const REQUEST_AUTOCOMPLETE = 'REQUEST_AUTOCOMPLETE'; export const REQUEST_AUTOCOMPLETE = 'REQUEST_AUTOCOMPLETE';
export const RECEIVE_AUTOCOMPLETE_SUCCESS = 'RECEIVE_AUTOCOMPLETE_SUCCESS'; export const RECEIVE_AUTOCOMPLETE_SUCCESS = 'RECEIVE_AUTOCOMPLETE_SUCCESS';
export const RECEIVE_AUTOCOMPLETE_ERROR = 'RECEIVE_AUTOCOMPLETE_ERROR'; export const RECEIVE_AUTOCOMPLETE_ERROR = 'RECEIVE_AUTOCOMPLETE_ERROR';
export const CLEAR_AUTOCOMPLETE = 'CLEAR_AUTOCOMPLETE';
export const SET_SEARCH = 'SET_SEARCH'; export const SET_SEARCH = 'SET_SEARCH';
...@@ -15,6 +15,9 @@ export default { ...@@ -15,6 +15,9 @@ export default {
state.loading = false; state.loading = false;
state.autocompleteOptions = []; state.autocompleteOptions = [];
}, },
[types.CLEAR_AUTOCOMPLETE](state) {
state.autocompleteOptions = [];
},
[types.SET_SEARCH](state, value) { [types.SET_SEARCH](state, value) {
state.search = value; state.search = value;
}, },
......
...@@ -30,6 +30,7 @@ describe('HeaderSearchApp', () => { ...@@ -30,6 +30,7 @@ describe('HeaderSearchApp', () => {
const actionSpies = { const actionSpies = {
setSearch: jest.fn(), setSearch: jest.fn(),
fetchAutocompleteOptions: jest.fn(), fetchAutocompleteOptions: jest.fn(),
clearAutocomplete: jest.fn(),
}; };
const createComponent = (initialState, mockGetters) => { const createComponent = (initialState, mockGetters) => {
...@@ -217,16 +218,40 @@ describe('HeaderSearchApp', () => { ...@@ -217,16 +218,40 @@ describe('HeaderSearchApp', () => {
}); });
describe('onInput', () => { describe('onInput', () => {
beforeEach(() => { describe('when search has text', () => {
findHeaderSearchInput().vm.$emit('input', MOCK_SEARCH); beforeEach(() => {
}); findHeaderSearchInput().vm.$emit('input', MOCK_SEARCH);
});
it('calls setSearch with search term', () => { it('calls setSearch with search term', () => {
expect(actionSpies.setSearch).toHaveBeenCalledWith(expect.any(Object), MOCK_SEARCH); 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', () => { describe('when search is emptied', () => {
expect(actionSpies.fetchAutocompleteOptions).toHaveBeenCalled(); 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', () => { ...@@ -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', () => { describe('setSearch', () => {
it('calls the SET_SEARCH mutation', () => { it('calls the SET_SEARCH mutation', () => {
return testAction({ return testAction({
......
...@@ -41,6 +41,14 @@ describe('Header Search Store Mutations', () => { ...@@ -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', () => { describe('SET_SEARCH', () => {
it('sets search to value', () => { it('sets search to value', () => {
mutations[types.SET_SEARCH](state, MOCK_SEARCH); 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