Merge "Bug fixes Magic Search"
This commit is contained in:
commit
87a888c0a2
@ -109,6 +109,8 @@
|
|||||||
var key = service.getEventCode($event);
|
var key = service.getEventCode($event);
|
||||||
if (key === 9) { // prevent default when we can.
|
if (key === 9) { // prevent default when we can.
|
||||||
$event.preventDefault();
|
$event.preventDefault();
|
||||||
|
} else if (key === 8) {
|
||||||
|
backspaceKeyDown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,7 +120,6 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ctrl.facetClicked(0, '', ctrl.filteredObj[0].name);
|
ctrl.facetClicked(0, '', ctrl.filteredObj[0].name);
|
||||||
setSearchInput('');
|
|
||||||
} else {
|
} else {
|
||||||
if (angular.isUndefined(ctrl.filteredOptions) ||
|
if (angular.isUndefined(ctrl.filteredOptions) ||
|
||||||
ctrl.filteredOptions.length !== 1) {
|
ctrl.filteredOptions.length !== 1) {
|
||||||
@ -130,7 +131,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function escapeKeyUp() {
|
function escapeKeyUp() {
|
||||||
setMenuOpen(false);
|
if (angular.isDefined(ctrl.facetSelected)) {
|
||||||
|
setMenuOpen(true);
|
||||||
|
} else {
|
||||||
|
setMenuOpen(false);
|
||||||
|
}
|
||||||
resetState();
|
resetState();
|
||||||
var textFilter = ctrl.textSearch;
|
var textFilter = ctrl.textSearch;
|
||||||
if (angular.isUndefined(textFilter)) {
|
if (angular.isUndefined(textFilter)) {
|
||||||
@ -142,43 +147,71 @@
|
|||||||
function enterKeyUp() {
|
function enterKeyUp() {
|
||||||
var searchVal = searchInput.val();
|
var searchVal = searchInput.val();
|
||||||
// if tag search, treat as regular facet
|
// if tag search, treat as regular facet
|
||||||
if (ctrl.facetSelected && angular.isUndefined(ctrl.facetSelected.options)) {
|
if (searchVal !== '') {
|
||||||
var curr = ctrl.facetSelected;
|
if (ctrl.facetSelected) {
|
||||||
curr.name = curr.name.split('=')[0] + '=' + searchVal;
|
var curr = ctrl.facetSelected;
|
||||||
curr.label[1] = searchVal;
|
curr.name = curr.name.split('=')[0] + '=' + searchVal;
|
||||||
ctrl.currentSearch.push(curr);
|
curr.label[1] = searchVal;
|
||||||
resetState();
|
ctrl.currentSearch.push(curr);
|
||||||
emitQuery();
|
resetState();
|
||||||
|
emitQuery();
|
||||||
|
setMenuOpen(true);
|
||||||
|
} else {
|
||||||
|
// if text search treat as search
|
||||||
|
ctrl.currentSearch = ctrl.currentSearch.filter(notTextSearch);
|
||||||
|
ctrl.currentSearch.push(service.getTextFacet(searchVal, $scope.strings.text));
|
||||||
|
$scope.$apply();
|
||||||
|
setMenuOpen(true);
|
||||||
|
setSearchInput('');
|
||||||
|
emitTextSearch(searchVal);
|
||||||
|
ctrl.textSearch = searchVal;
|
||||||
|
}
|
||||||
|
} else if (ctrl.isMenuOpen) {
|
||||||
setMenuOpen(false);
|
setMenuOpen(false);
|
||||||
} else {
|
} else {
|
||||||
// if text search treat as search
|
setMenuOpen(true);
|
||||||
ctrl.currentSearch = ctrl.currentSearch.filter(notTextSearch);
|
|
||||||
ctrl.currentSearch.push(service.getTextFacet(searchVal, $scope.strings.text));
|
|
||||||
$scope.$apply();
|
|
||||||
setMenuOpen(false);
|
|
||||||
setSearchInput('');
|
|
||||||
emitTextSearch(searchVal);
|
|
||||||
ctrl.textSearch = searchVal;
|
|
||||||
}
|
}
|
||||||
ctrl.filteredObj = ctrl.unusedFacetChoices;
|
ctrl.filteredObj = ctrl.unusedFacetChoices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function backspaceKeyDown() {
|
||||||
|
var searchVal = searchInput.val();
|
||||||
|
if (searchVal === '') {
|
||||||
|
if (ctrl.currentSearch.length > 0 && angular.isUndefined(ctrl.facetSelected)) {
|
||||||
|
ctrl.removeFacet(ctrl.currentSearch.length - 1);
|
||||||
|
setMenuOpen(true);
|
||||||
|
} else {
|
||||||
|
escapeKeyUp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function backspaceKeyUp() {
|
||||||
|
var searchVal = searchInput.val();
|
||||||
|
// if there's no current search and facet selected, then clear all search
|
||||||
|
if (searchVal === '' && angular.isUndefined(ctrl.facetSelected)) {
|
||||||
|
if (ctrl.currentSearch.length === 0) {
|
||||||
|
ctrl.clearSearch();
|
||||||
|
} else {
|
||||||
|
resetState();
|
||||||
|
emitTextSearch(ctrl.textSearch || '');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filterFacets(searchVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteKeyUp() {
|
||||||
|
return backspaceKeyUp();
|
||||||
|
}
|
||||||
|
|
||||||
function notTextSearch(item) {
|
function notTextSearch(item) {
|
||||||
return item.name.indexOf('text') !== 0;
|
return item.name.indexOf('text') !== 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function defaultKeyUp() {
|
function defaultKeyUp() {
|
||||||
var searchVal = searchInput.val();
|
var searchVal = searchInput.val();
|
||||||
if (searchVal === '') {
|
filterFacets(searchVal);
|
||||||
ctrl.filteredObj = ctrl.unusedFacetChoices;
|
|
||||||
$scope.$apply();
|
|
||||||
emitTextSearch('');
|
|
||||||
if (ctrl.facetSelected && angular.isUndefined(ctrl.facetSelected.options)) {
|
|
||||||
resetState();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
filterFacets(searchVal);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function keyUpHandler($event) { // handle ctrl-char input
|
function keyUpHandler($event) { // handle ctrl-char input
|
||||||
@ -186,7 +219,13 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var key = service.getEventCode($event);
|
var key = service.getEventCode($event);
|
||||||
var handlers = { 9: tabKeyUp, 27: escapeKeyUp, 13: enterKeyUp };
|
var handlers = {
|
||||||
|
8: backspaceKeyUp,
|
||||||
|
9: tabKeyUp,
|
||||||
|
27: escapeKeyUp,
|
||||||
|
13: enterKeyUp,
|
||||||
|
46: deleteKeyUp
|
||||||
|
};
|
||||||
if (handlers[key]) {
|
if (handlers[key]) {
|
||||||
handlers[key]();
|
handlers[key]();
|
||||||
} else {
|
} else {
|
||||||
@ -208,16 +247,10 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (searchVal === '') {
|
if (searchVal === '') {
|
||||||
ctrl.filteredObj = ctrl.unusedFacetChoices;
|
|
||||||
$scope.$apply();
|
|
||||||
emitTextSearch('');
|
|
||||||
if (ctrl.facetSelected && angular.isUndefined(ctrl.facetSelected.options)) {
|
|
||||||
resetState();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Backspace, Delete
|
// Backspace, Delete and arrow keys
|
||||||
if (key !== 8 && key !== 46) {
|
if (key !== 8 && key !== 46 && !(key >= 37 && key <= 40)) {
|
||||||
filterFacets(searchVal);
|
filterFacets(searchVal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -257,7 +290,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function facetClickHandler($index) {
|
function facetClickHandler($index) {
|
||||||
setMenuOpen(false);
|
|
||||||
var facet = ctrl.filteredObj[$index];
|
var facet = ctrl.filteredObj[$index];
|
||||||
var label = facet.label;
|
var label = facet.label;
|
||||||
if (angular.isArray(label)) {
|
if (angular.isArray(label)) {
|
||||||
@ -268,12 +300,8 @@
|
|||||||
if (angular.isDefined(facet.options)) {
|
if (angular.isDefined(facet.options)) {
|
||||||
ctrl.filteredOptions = ctrl.facetOptions = facet.options;
|
ctrl.filteredOptions = ctrl.facetOptions = facet.options;
|
||||||
setMenuOpen(true);
|
setMenuOpen(true);
|
||||||
}
|
} else {
|
||||||
var searchVal = searchInput.val();
|
setMenuOpen(false);
|
||||||
if (searchVal) {
|
|
||||||
ctrl.currentSearch = ctrl.currentSearch.filter(notTextSearch);
|
|
||||||
ctrl.currentSearch.push(service.getTextFacet(searchVal, $scope.strings.text));
|
|
||||||
ctrl.textSearch = searchVal;
|
|
||||||
}
|
}
|
||||||
setSearchInput('');
|
setSearchInput('');
|
||||||
setPrompt('');
|
setPrompt('');
|
||||||
|
@ -148,7 +148,7 @@
|
|||||||
expect(keyDownHandler).toBeDefined();
|
expect(keyDownHandler).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does nothing with keys other than 9", function() {
|
it("does nothing with keys other than 9 and 8", function() {
|
||||||
spyOn(evt, 'preventDefault');
|
spyOn(evt, 'preventDefault');
|
||||||
keyDownHandler(evt);
|
keyDownHandler(evt);
|
||||||
expect(evt.preventDefault).not.toHaveBeenCalled();
|
expect(evt.preventDefault).not.toHaveBeenCalled();
|
||||||
@ -160,6 +160,30 @@
|
|||||||
keyDownHandler(evt);
|
keyDownHandler(evt);
|
||||||
expect(evt.preventDefault).toHaveBeenCalled();
|
expect(evt.preventDefault).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("'Backspace' key", function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
evt.keyCode = 8;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("removes last facet if length larger than 1 and searchVal empty", function() {
|
||||||
|
spyOn(searchInput, 'val').and.returnValue('');
|
||||||
|
spyOn(ctrl, 'removeFacet');
|
||||||
|
delete ctrl.facetSelected;
|
||||||
|
ctrl.currentSearch = [{name: 'name=foo'}, {name: 'flavor=m1'}, {name: 'key=value'}];
|
||||||
|
keyDownHandler(evt);
|
||||||
|
$timeout.flush();
|
||||||
|
expect(ctrl.removeFacet).toHaveBeenCalledWith(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("removes selectedFacet if searchVal is empty", function() {
|
||||||
|
spyOn(searchInput, 'val').and.returnValue('');
|
||||||
|
ctrl.facetSelected = {name: 'waldo=undefined', label: ['a']};
|
||||||
|
keyDownHandler(evt);
|
||||||
|
$timeout.flush();
|
||||||
|
expect(ctrl.facetSelect).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("keyup handler", function() {
|
describe("keyup handler", function() {
|
||||||
@ -181,6 +205,34 @@
|
|||||||
expect(scope.$emit).not.toHaveBeenCalled();
|
expect(scope.$emit).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("'Backspace' key", function() {
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
evt.keyCode = 8;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calls clearSearch if facetSelected undefined and currentSearch empty", function() {
|
||||||
|
spyOn(searchInput, 'val').and.returnValue('');
|
||||||
|
spyOn(ctrl, 'clearSearch');
|
||||||
|
delete ctrl.facetSelected;
|
||||||
|
ctrl.currentSearch = [];
|
||||||
|
keyUpHandler(evt);
|
||||||
|
expect(ctrl.clearSearch).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("emits textSearch if facetSeleted undefined and currentSearch not empty", function() {
|
||||||
|
spyOn(searchInput, 'val').and.returnValue('');
|
||||||
|
spyOn(scope, '$emit');
|
||||||
|
delete ctrl.facetSelected;
|
||||||
|
ctrl.currentSearch = [{name: 'textstuff'}, {name: 'texting'}];
|
||||||
|
scope.filter_keys = [1,2,3];
|
||||||
|
keyUpHandler(evt);
|
||||||
|
expectResetState();
|
||||||
|
expect(scope.$emit).toHaveBeenCalledWith(magicSearchEvents.TEXT_SEARCH, '', [1, 2, 3]);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
describe("'Escape' key", function() {
|
describe("'Escape' key", function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
evt.keyCode = 27;
|
evt.keyCode = 27;
|
||||||
@ -298,6 +350,26 @@
|
|||||||
expect(ctrl.currentSearch).toEqual([{name: 'nontext'}, {name: 'nottext'},
|
expect(ctrl.currentSearch).toEqual([{name: 'nontext'}, {name: 'nottext'},
|
||||||
{name: 'text=searchval', label: ['stringtext', 'searchval']}]);
|
{name: 'text=searchval', label: ['stringtext', 'searchval']}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("opens menu when searchVal is an empty string", function() {
|
||||||
|
ctrl.isMenuOpen = false;
|
||||||
|
spyOn(searchInput, 'val').and.returnValue('');
|
||||||
|
spyOn(scope, '$emit');
|
||||||
|
scope.filter_keys = [1,2,3];
|
||||||
|
keyUpHandler(evt);
|
||||||
|
$timeout.flush();
|
||||||
|
expect(ctrl.isMenuOpen).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("emits a Query when not empty string and a facet is selected", function() {
|
||||||
|
spyOn(searchInput, 'val').and.returnValue('foo');
|
||||||
|
ctrl.currentSearch = [];
|
||||||
|
keyUpHandler(evt);
|
||||||
|
$timeout.flush();
|
||||||
|
expect(ctrl.currentSearch).toEqual([{name: 'waldo=foo', label: ['a', 'foo']}]);
|
||||||
|
expectResetState();
|
||||||
|
expect(ctrl.isMenuOpen).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Any other key", function() {
|
describe("Any other key", function() {
|
||||||
@ -314,14 +386,6 @@
|
|||||||
magicSearchEvents.TEXT_SEARCH, '', ['a', 'b', 'c']);
|
magicSearchEvents.TEXT_SEARCH, '', ['a', 'b', 'c']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("resets state if facetSelected and no options", function() {
|
|
||||||
spyOn(searchInput, 'val').and.returnValue('');
|
|
||||||
scope.filter_keys = ['a', 'b', 'c'];
|
|
||||||
ctrl.facetSelected = {};
|
|
||||||
keyUpHandler(evt);
|
|
||||||
expectResetState();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("filters if there is a search term", function() {
|
it("filters if there is a search term", function() {
|
||||||
spyOn(searchInput, 'val').and.returnValue('searchterm');
|
spyOn(searchInput, 'val').and.returnValue('searchterm');
|
||||||
spyOn(scope, '$emit');
|
spyOn(scope, '$emit');
|
||||||
@ -357,36 +421,9 @@
|
|||||||
|
|
||||||
it("opens menu when searchVal is a space", function() {
|
it("opens menu when searchVal is a space", function() {
|
||||||
evt.which = 32;
|
evt.which = 32;
|
||||||
spyOn(searchInput, 'val').and.returnValue(' ');
|
|
||||||
spyOn(scope, '$emit');
|
|
||||||
scope.filter_keys = [1,2,3];
|
|
||||||
keyPressHandler(evt);
|
keyPressHandler(evt);
|
||||||
expect(scope.$emit).toHaveBeenCalledWith(
|
$timeout.flush();
|
||||||
magicSearchEvents.TEXT_SEARCH, ' ', [1,2,3]);
|
expect(ctrl.isMenuOpen).toBe(true);
|
||||||
});
|
|
||||||
|
|
||||||
it("opens menu when searchVal is an empty string", function() {
|
|
||||||
spyOn(searchInput, 'val').and.returnValue('');
|
|
||||||
spyOn(scope, '$emit');
|
|
||||||
evt.which = 13; // not alter search
|
|
||||||
scope.filter_keys = [1,2,3];
|
|
||||||
keyPressHandler(evt);
|
|
||||||
expect(scope.$emit).toHaveBeenCalledWith(
|
|
||||||
magicSearchEvents.TEXT_SEARCH, '', [1,2,3]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("resets state when ctrl.facetSelected exists but has no options", function() {
|
|
||||||
spyOn(searchInput, 'val').and.returnValue('');
|
|
||||||
spyOn(scope, '$emit');
|
|
||||||
evt.which = 13; // not alter search
|
|
||||||
scope.filter_keys = [1,2,3];
|
|
||||||
ctrl.facetSelected = {};
|
|
||||||
ctrl.facetOptions = {};
|
|
||||||
ctrl.filteredOptions = {};
|
|
||||||
keyPressHandler(evt);
|
|
||||||
expect(scope.$emit).toHaveBeenCalledWith(
|
|
||||||
magicSearchEvents.TEXT_SEARCH, '', [1,2,3]);
|
|
||||||
expectResetState();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("filters when searchval has content and key is not delete/backspace", function() {
|
it("filters when searchval has content and key is not delete/backspace", function() {
|
||||||
@ -406,6 +443,7 @@
|
|||||||
keyPressHandler(evt);
|
keyPressHandler(evt);
|
||||||
expect(scope.$emit).not.toHaveBeenCalled();
|
expect(scope.$emit).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("optionClicked", function() {
|
describe("optionClicked", function() {
|
||||||
|
@ -89,6 +89,10 @@
|
|||||||
.search-entry {
|
.search-entry {
|
||||||
flex: 1 0 auto;
|
flex: 1 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropdown-menu {
|
||||||
|
left: initial;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fi-filter {
|
.fi-filter {
|
||||||
|
8
releasenotes/notes/bug-1618235-59865fa0e5991e63.yaml
Normal file
8
releasenotes/notes/bug-1618235-59865fa0e5991e63.yaml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
[`bug 1618235 <https://bugs.launchpad.net/horizon/+bug/1618235>`__]
|
||||||
|
User can now delete all characters typed in input search without causing
|
||||||
|
the selected facet to disappear when the last character is deleted.
|
||||||
|
other:
|
||||||
|
- Menu follows the search input position as the user adds more facets
|
7
releasenotes/notes/bug-1635505-3807fd0151702a5f.yaml
Normal file
7
releasenotes/notes/bug-1635505-3807fd0151702a5f.yaml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
[`bug 1635505 <https://bugs.launchpad.net/horizon/+bug/1635505>`__]
|
||||||
|
Horizon now properly allows to use arrow keys inside of the input search,
|
||||||
|
without triggering a new text search that refreshes the content of the
|
||||||
|
table below.
|
Loading…
Reference in New Issue
Block a user