Commit 451b1c3b authored by Heinrich Lee Yu's avatar Heinrich Lee Yu Committed by Fatih Acet

Fix keyboard shortcuts in header search bar

Fixes a bug where hidden.bs.dropdown was manually triggered
but the dropdown was not actually hidden. Triggering the event
caused the event handlers to be unloaded. The events are supposed
to be attached again when the dropdown is opened but in this case
the dropdown is still open.
parent 14227cac
......@@ -617,7 +617,7 @@ GitLabDropdown = (function() {
GitLabDropdown.prototype.hidden = function(e) {
var $input;
this.resetRows();
this.removeArrayKeyEvent();
this.removeArrowKeyEvent();
$input = this.dropdown.find('.dropdown-input-field');
if (this.options.filterable) {
$input.blur();
......@@ -900,7 +900,7 @@ GitLabDropdown = (function() {
);
};
GitLabDropdown.prototype.removeArrayKeyEvent = function() {
GitLabDropdown.prototype.removeArrowKeyEvent = function() {
return $('body').off('keydown');
};
......
/* eslint-disable no-return-assign, one-var, no-var, consistent-return, class-methods-use-this, no-lonely-if, vars-on-top */
/* eslint-disable no-return-assign, one-var, no-var, consistent-return, class-methods-use-this, vars-on-top */
import $ from 'jquery';
import { escape, throttle } from 'underscore';
......@@ -95,7 +95,6 @@ export class SearchAutocomplete {
this.createAutocomplete();
}
this.saveTextLength();
this.bindEvents();
this.dropdownToggle.dropdown();
this.searchInput.addClass('js-autocomplete-disabled');
......@@ -107,7 +106,7 @@ export class SearchAutocomplete {
this.onClearInputClick = this.onClearInputClick.bind(this);
this.onSearchInputFocus = this.onSearchInputFocus.bind(this);
this.onSearchInputKeyUp = this.onSearchInputKeyUp.bind(this);
this.onSearchInputKeyDown = this.onSearchInputKeyDown.bind(this);
this.onSearchInputChange = this.onSearchInputChange.bind(this);
this.setScrollFade = this.setScrollFade.bind(this);
}
getElement(selector) {
......@@ -118,10 +117,6 @@ export class SearchAutocomplete {
return (this.originalState = this.serializeState());
}
saveTextLength() {
return (this.lastTextLength = this.searchInput.val().length);
}
createAutocomplete() {
return this.searchInput.glDropdown({
filterInputBlur: false,
......@@ -318,12 +313,16 @@ export class SearchAutocomplete {
}
bindEvents() {
this.searchInput.on('keydown', this.onSearchInputKeyDown);
this.searchInput.on('input', this.onSearchInputChange);
this.searchInput.on('keyup', this.onSearchInputKeyUp);
this.searchInput.on('focus', this.onSearchInputFocus);
this.searchInput.on('blur', this.onSearchInputBlur);
this.clearInput.on('click', this.onClearInputClick);
this.dropdownContent.on('scroll', throttle(this.setScrollFade, 250));
this.searchInput.on('click', e => {
e.stopPropagation();
});
}
enableAutocomplete() {
......@@ -342,43 +341,19 @@ export class SearchAutocomplete {
}
}
// Saves last length of the entered text
onSearchInputKeyDown() {
return this.saveTextLength();
onSearchInputChange() {
this.enableAutocomplete();
}
onSearchInputKeyUp(e) {
switch (e.keyCode) {
case KEYCODE.BACKSPACE:
// When removing the last character and no badge is present
if (this.lastTextLength === 1) {
this.disableAutocomplete();
}
// When removing any character from existin value
if (this.lastTextLength > 1) {
this.enableAutocomplete();
}
break;
case KEYCODE.ESCAPE:
this.restoreOriginalState();
break;
case KEYCODE.ENTER:
this.disableAutocomplete();
break;
case KEYCODE.UP:
case KEYCODE.DOWN:
return;
default:
// Handle the case when deleting the input value other than backspace
// e.g. Pressing ctrl + backspace or ctrl + x
if (this.searchInput.val() === '') {
this.disableAutocomplete();
} else {
// We should display the menu only when input is not empty
if (e.keyCode !== KEYCODE.ENTER) {
this.enableAutocomplete();
}
}
}
this.wrap.toggleClass('has-value', Boolean(e.target.value));
}
......@@ -434,7 +409,7 @@ export class SearchAutocomplete {
disableAutocomplete() {
if (!this.searchInput.hasClass('js-autocomplete-disabled') && this.dropdown.hasClass('show')) {
this.searchInput.addClass('js-autocomplete-disabled');
this.dropdown.removeClass('show').trigger('hidden.bs.dropdown');
this.dropdown.dropdown('toggle');
this.restoreMenu();
}
}
......
---
title: Fix keyboard shortcuts in header search autocomplete
merge_request: 18685
author:
type: fixed
......@@ -26,6 +26,16 @@ describe 'User uses header search field', :js do
end
end
context 'when using the keyboard shortcut' do
before do
find('body').native.send_keys('s')
end
it 'shows the category search dropdown' do
expect(page).to have_selector('.dropdown-header', text: /#{scope_name}/i)
end
end
context 'when clicking the search field' do
before do
page.find('#search.js-autocomplete-disabled').click
......@@ -77,15 +87,21 @@ describe 'User uses header search field', :js do
end
context 'when entering text into the search field' do
before do
it 'does not display the category search dropdown' do
page.within('.search-input-wrap') do
fill_in('search', with: scope_name.first(4))
end
end
it 'does not display the category search dropdown' do
expect(page).not_to have_selector('.dropdown-header', text: /#{scope_name}/i)
end
it 'hides the dropdown when there are no results' do
page.within('.search-input-wrap') do
fill_in('search', with: 'a_search_term_with_no_results')
end
expect(page).not_to have_selector('.dropdown-menu')
end
end
end
......
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