Adding features to MagicSearch
The previous MagicSearch patch was a function-for-function copy of the source at GitHub. There are some useful features that were missing from that. This patch adds a 'clientFullTextSearch' property to the magic search bar so the full-text search option may be turned off. Change-Id: Id46b169c5ba18606d498701b6664fc6374083011 Implements: blueprint integrate-magic-search
This commit is contained in:
parent
3088f77d09
commit
ed9376fa32
|
@ -26,6 +26,7 @@
|
|||
* @element
|
||||
* @param {object} filterFacets Facets allowed for searching
|
||||
* @param {object=} filterStrings Help content shown in search bar
|
||||
* @param {object=} clientFullTextSearch if full text search is to be done on the client
|
||||
* @description
|
||||
* The `magicSearchBar` directive provides a template for a
|
||||
* client side faceted search that utilizes Smart-Table's
|
||||
|
@ -71,6 +72,13 @@
|
|||
* <hz-magic-search-bar
|
||||
* filter-facets="filterFacets">
|
||||
* </hz-magic-search-bar>
|
||||
*
|
||||
* or
|
||||
*
|
||||
* <hz-magic-search-bar
|
||||
* client-full-text-search="false"
|
||||
* filter-strings="filterStrings">
|
||||
* </hz-magic-search-bar>
|
||||
* ```
|
||||
*/
|
||||
function hzMagicSearchBar(basePath) {
|
||||
|
@ -80,7 +88,9 @@
|
|||
restrict: 'E',
|
||||
scope: {
|
||||
filterStrings: '=?',
|
||||
filterFacets: '='
|
||||
filterFacets: '=',
|
||||
clientFullTextSearch: '=?',
|
||||
searchSettingsCallback: '=?'
|
||||
},
|
||||
templateUrl: basePath + 'magic-search/hz-magic-search-bar.html'
|
||||
};
|
||||
|
@ -90,22 +100,33 @@
|
|||
//////////
|
||||
|
||||
function link(scope) {
|
||||
scope.clientFullTextSearch = angular.isDefined(scope.clientFullTextSearch)
|
||||
? scope.clientFullTextSearch
|
||||
: true;
|
||||
// if filterStrings is not defined, set defaults
|
||||
var defaultFilterStrings = {
|
||||
cancel: gettext('Cancel'),
|
||||
prompt: gettext('Click here for filters.'),
|
||||
remove: gettext('Remove'),
|
||||
text: gettext('In current results')
|
||||
text: (scope.clientFullTextSearch ?
|
||||
gettext('Search in current results') :
|
||||
gettext('Full Text Search'))
|
||||
};
|
||||
scope.filterStrings = scope.filterStrings ? scope.filterStrings : defaultFilterStrings;
|
||||
scope.filterStrings = scope.filterStrings || defaultFilterStrings;
|
||||
|
||||
if (angular.isDefined(scope.searchSettingsCallback)) {
|
||||
scope.showSettings = true;
|
||||
} else {
|
||||
scope.showSettings = false;
|
||||
}
|
||||
}
|
||||
|
||||
function compile(element) {
|
||||
/**
|
||||
* Need to set template here since MagicSearch template
|
||||
* attribute is not interpolated. Can't hardcode the
|
||||
* template location and need to use basePath.
|
||||
*/
|
||||
* Need to set template here since MagicSearch template
|
||||
* attribute is not interpolated. Can't hardcode the
|
||||
* template location and need to use basePath.
|
||||
*/
|
||||
var templateUrl = basePath + 'magic-search/magic-search.html';
|
||||
element.find('magic-search').attr('template', templateUrl);
|
||||
element.addClass('hz-magic-search-bar');
|
||||
|
|
|
@ -93,6 +93,47 @@
|
|||
expect(searchBar.length).toBe(1);
|
||||
});
|
||||
|
||||
it('sets the clientFullTextSearch to false', function () {
|
||||
var markup = '<table st-table="rows">' +
|
||||
'<thead>' +
|
||||
' <tr>' +
|
||||
' <th>' +
|
||||
' <hz-magic-search-bar ' +
|
||||
' client-full-text-search="false"' +
|
||||
' filter-facets="filterFacets">' +
|
||||
' </hz-magic-search-bar>' +
|
||||
' </th>' +
|
||||
' </tr>' +
|
||||
'</thead>' +
|
||||
'<tbody></tbody>' +
|
||||
'</table>';
|
||||
|
||||
var $element = $compile(angular.element(markup))($scope);
|
||||
$scope.$apply();
|
||||
|
||||
expect($element.find('magic-search').scope().clientFullTextSearch).toEqual(false);
|
||||
});
|
||||
|
||||
it('sets the clientFullTextSearch to true', function () {
|
||||
var markup = '<table st-table="rows">' +
|
||||
'<thead>' +
|
||||
' <tr>' +
|
||||
' <th>' +
|
||||
' <hz-magic-search-bar ' +
|
||||
' filter-facets="filterFacets">' +
|
||||
' </hz-magic-search-bar>' +
|
||||
' </th>' +
|
||||
' </tr>' +
|
||||
'</thead>' +
|
||||
'<tbody></tbody>' +
|
||||
'</table>';
|
||||
|
||||
var $element = $compile(angular.element(markup))($scope);
|
||||
$scope.$apply();
|
||||
|
||||
expect($element.find('magic-search').scope().clientFullTextSearch).toEqual(true);
|
||||
});
|
||||
|
||||
it('use filterStrings defaults if not provided as attribute', function () {
|
||||
var markup = '<table st-table="rows">' +
|
||||
'<thead>' +
|
||||
|
|
|
@ -330,9 +330,13 @@
|
|||
* Broadcast event when facet options are returned via AJAX.
|
||||
* Should magic_search.js absorb this?
|
||||
*/
|
||||
var facetsChangedWatcher = $scope.$on('facetsChanged', function () {
|
||||
var facetsChangedWatcher = $scope.$on('facetsChanged', function (event, data) {
|
||||
$timeout(function () {
|
||||
initSearch([]);
|
||||
if (data && data.magicSearchQuery) {
|
||||
initSearch(data.magicSearchQuery.split('&'));
|
||||
} else {
|
||||
initSearch(ctrl.currentSearch.map(function(x) { return x.name; }));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -347,7 +351,6 @@
|
|||
}
|
||||
ctrl.currentSearch = service.getFacetsFromSearchTerms(searchTerms,
|
||||
ctrl.textSearch, $scope.strings.text, tmpFacetChoices);
|
||||
|
||||
ctrl.filteredObj = ctrl.unusedFacetChoices =
|
||||
service.getUnusedFacetChoices(tmpFacetChoices, searchTerms);
|
||||
|
||||
|
|
|
@ -214,7 +214,12 @@
|
|||
var buff = [];
|
||||
searchTerms.map(getSearchTermObject).forEach(getFacetFromObj);
|
||||
if (angular.isDefined(textSearch)) {
|
||||
buff.push(getTextFacet(textSearch, textSearchLabel));
|
||||
var currentTextSearch = searchTerms.filter(function(searchField) {
|
||||
return searchField.indexOf(textSearch) === 0;
|
||||
});
|
||||
if (currentTextSearch.length === 0) {
|
||||
buff.push(getTextFacet(textSearch, textSearchLabel));
|
||||
}
|
||||
}
|
||||
return buff;
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
.module('horizon.framework.widgets.magic-search')
|
||||
.directive('stMagicSearch', stMagicSearch);
|
||||
|
||||
stMagicSearch.$inject = ['$timeout', '$window'];
|
||||
stMagicSearch.$inject = ['$timeout'];
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
|
@ -45,7 +45,7 @@
|
|||
* </st-magic-search>
|
||||
* ```
|
||||
*/
|
||||
function stMagicSearch($timeout, $window) {
|
||||
function stMagicSearch($timeout) {
|
||||
var directive = {
|
||||
link: link,
|
||||
require: '^stTable',
|
||||
|
@ -55,6 +55,12 @@
|
|||
return directive;
|
||||
|
||||
function link(scope, element, attr, tableCtrl) {
|
||||
var clientFullTextSearch = (angular.isDefined(scope.clientFullTextSearch)
|
||||
? scope.clientFullTextSearch
|
||||
: true);
|
||||
|
||||
scope.currentServerSearchParams = {};
|
||||
|
||||
// Generate predicate object from dot notation string
|
||||
function setPredObj(predicates, predObj, input) {
|
||||
var lastPred = predicates.pop();
|
||||
|
@ -66,26 +72,52 @@
|
|||
predObj[lastPred] = input;
|
||||
}
|
||||
|
||||
function setServerFacetSearch(scope, query) {
|
||||
var currentServerSearchParams = angular.copy(scope.currentServerSearchParams);
|
||||
currentServerSearchParams.magicSearchQuery = query;
|
||||
checkAndEmit(scope, currentServerSearchParams);
|
||||
}
|
||||
|
||||
function setServerTextSearch(scope, text) {
|
||||
var currentServerSearchParams = angular.copy(scope.currentServerSearchParams);
|
||||
currentServerSearchParams.queryString = text;
|
||||
checkAndEmit(scope, currentServerSearchParams);
|
||||
}
|
||||
|
||||
function checkAndEmit(scope, serverSearchParams) {
|
||||
if (serverSearchParams !== scope.currentServerSearchParams) {
|
||||
serverSearchParams.magicSearchQueryChanged =
|
||||
!angular.equals(scope.currentServerSearchParams.magicSearchQuery,
|
||||
serverSearchParams.magicSearchQuery);
|
||||
|
||||
serverSearchParams.queryStringChanged =
|
||||
!angular.equals(scope.currentServerSearchParams.queryString,
|
||||
serverSearchParams.queryString);
|
||||
|
||||
scope.currentServerSearchParams = serverSearchParams;
|
||||
|
||||
if (serverSearchParams.queryStringChanged || serverSearchParams.magicSearchQueryChanged) {
|
||||
scope.$emit('serverSearchUpdated', angular.copy(scope.currentServerSearchParams));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When user types a character, search the table
|
||||
var textSearchWatcher = scope.$on('textSearch', function(event, text) {
|
||||
// Timeout needed to prevent
|
||||
// $apply already in progress error
|
||||
$timeout(function() {
|
||||
tableCtrl.search(text);
|
||||
if (clientFullTextSearch) {
|
||||
tableCtrl.search(text);
|
||||
} else {
|
||||
setServerTextSearch(scope, text);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// When user changes a facet, use API filter
|
||||
var searchUpdatedWatcher = scope.$on('searchUpdated', function(event, query) {
|
||||
// update url
|
||||
var url = $window.location.href;
|
||||
if (url.indexOf('?') > -1) {
|
||||
url = url.split('?')[0];
|
||||
}
|
||||
if (query.length > 0) {
|
||||
url = url + '?' + query;
|
||||
}
|
||||
$window.history.pushState(query, '', url);
|
||||
setServerFacetSearch(scope, query);
|
||||
|
||||
// clear each time since Smart-Table
|
||||
// search is cumulative
|
||||
|
|
|
@ -109,38 +109,146 @@
|
|||
'<tbody>' +
|
||||
' <tr ng-repeat="row in rows">' +
|
||||
' <td>{{ row.name }}</td>' +
|
||||
' <td>{{ row.status }}</td>' +
|
||||
' </tr>' +
|
||||
'</tbody>' +
|
||||
'</table>';
|
||||
|
||||
$element = $compile(angular.element(markup))($scope);
|
||||
$element = $compile(angular.element(markup));
|
||||
|
||||
$scope.$apply();
|
||||
}));
|
||||
|
||||
it('should filter table to two rows if text searching with "active"', function () {
|
||||
it('should filter table to two rows if text searching with "shutdown"', function () {
|
||||
var element = $element($scope);
|
||||
$scope.$apply();
|
||||
|
||||
$scope.$broadcast('textSearch', 'shutdown');
|
||||
$timeout.flush();
|
||||
|
||||
expect(element.find('tbody tr').length).toBe(2);
|
||||
});
|
||||
|
||||
it('should skip text searching if clientFullTextSearch is false and raise events', function () {
|
||||
spyOn($scope, '$emit').and.callThrough();
|
||||
$scope.clientFullTextSearch = false;
|
||||
var element = $element($scope);
|
||||
$scope.$apply();
|
||||
|
||||
$scope.$broadcast('textSearch', 'active');
|
||||
$timeout.flush();
|
||||
expect($element.find('tbody tr').length).toBe(2);
|
||||
|
||||
expect(element.find('tbody tr').length).toBe(6);
|
||||
expect($scope.$emit).toHaveBeenCalledWith(
|
||||
'serverSearchUpdated',
|
||||
{
|
||||
//magicSearchQuery: '',
|
||||
magicSearchQueryChanged: false,
|
||||
queryString: 'active',
|
||||
queryStringChanged: true
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should not raise serverSearchUpdated event if nothing has changed', function () {
|
||||
spyOn($scope, '$emit').and.callThrough();
|
||||
$scope.clientFullTextSearch = false;
|
||||
var element = $element($scope);
|
||||
$scope.$apply();
|
||||
|
||||
$scope.$broadcast('textSearch', 'active');
|
||||
$timeout.flush();
|
||||
|
||||
$scope.$broadcast('textSearch', 'active');
|
||||
$timeout.flush();
|
||||
|
||||
expect(element.find('tbody tr').length).toBe(6);
|
||||
/*
|
||||
expect($scope.$emit).toHaveBeenCalledWith(
|
||||
'serverSearchUpdated',
|
||||
{
|
||||
magicSearchQuery: '',
|
||||
magicSearchQueryChanged: true,
|
||||
queryStringChanged: false
|
||||
}
|
||||
);
|
||||
*/
|
||||
expect($scope.$emit).toHaveBeenCalledWith(
|
||||
'serverSearchUpdated',
|
||||
{
|
||||
//magicSearchQuery: '',
|
||||
magicSearchQueryChanged: false,
|
||||
queryString: 'active',
|
||||
queryStringChanged: true
|
||||
}
|
||||
);
|
||||
// Originally expected to be 2.
|
||||
expect($scope.$emit.calls.count()).toEqual(1);
|
||||
});
|
||||
|
||||
it('should filter table to two rows if facet with static === "shutdown"', function () {
|
||||
var element = $element($scope);
|
||||
$scope.$apply();
|
||||
|
||||
$scope.$broadcast('searchUpdated', 'status=shutdown');
|
||||
$timeout.flush();
|
||||
expect($element.find('tbody tr').length).toBe(2);
|
||||
|
||||
expect(element.find('tbody tr').length).toBe(2);
|
||||
});
|
||||
|
||||
it('should filter table to 1 row if facet with name === "name 1"', function () {
|
||||
var element = $element($scope);
|
||||
$scope.$apply();
|
||||
|
||||
$scope.$broadcast('searchUpdated', 'name=name 1');
|
||||
$scope.$broadcast('textSearch', 'active');
|
||||
$timeout.flush();
|
||||
expect($element.find('tbody tr').length).toBe(1);
|
||||
|
||||
expect(element.find('tbody tr').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should not filter table if filter is server side', function () {
|
||||
it('should not filter table if filter is server side and raise event', function () {
|
||||
spyOn($scope, '$emit').and.callThrough();
|
||||
var element = $element($scope);
|
||||
$scope.$apply();
|
||||
|
||||
$scope.$broadcast('searchUpdated', 'server_name=server 1');
|
||||
$timeout.flush();
|
||||
expect($element.find('tbody tr').length).toBe(6);
|
||||
|
||||
expect(element.find('tbody tr').length).toBe(6);
|
||||
expect($scope.$emit).toHaveBeenCalledWith(
|
||||
'serverSearchUpdated',
|
||||
{
|
||||
magicSearchQuery: 'server_name=server 1',
|
||||
magicSearchQueryChanged: true,
|
||||
queryStringChanged: false
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should not raise serverSearchUpdated if filter has not changed', function () {
|
||||
spyOn($scope, '$emit').and.callThrough();
|
||||
var element = $element($scope);
|
||||
$scope.$apply();
|
||||
|
||||
$scope.$broadcast('searchUpdated', 'server_name=server 1');
|
||||
$timeout.flush();
|
||||
|
||||
$scope.$broadcast('searchUpdated', 'server_name=server 1');
|
||||
$timeout.flush();
|
||||
|
||||
expect(element.find('tbody tr').length).toBe(6);
|
||||
expect($scope.$emit).toHaveBeenCalledWith(
|
||||
'serverSearchUpdated',
|
||||
{
|
||||
magicSearchQuery: 'server_name=server 1',
|
||||
magicSearchQueryChanged: true,
|
||||
queryStringChanged: false
|
||||
}
|
||||
);
|
||||
|
||||
// Original expectation was 2.
|
||||
expect($scope.$emit.calls.count()).toEqual(1);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue