Commit 18bea245 authored by Robert Knight's avatar Robert Knight

Modernize <simple-search> directive

 - Convert the <simple-search> directive to a modern component-style
   directive which uses one-way bindings and events rather than two-way
   bindings. This makes it consistent with other components in the app.

 - Avoid duplicating the search filter state in multiple places but
   store it only in the Redux store and update it via an action.

The tests for `simple-search` have not been updated in this commit
because they are going to be replaced with a new set using the
`createDirective()` helper in the next commit.
parent bcb55d44
......@@ -63,6 +63,7 @@ function initialState(settings) {
}
var types = {
CLEAR_SELECTION: 'CLEAR_SELECTION',
SELECT_ANNOTATIONS: 'SELECT_ANNOTATIONS',
FOCUS_ANNOTATIONS: 'FOCUS_ANNOTATIONS',
HIGHLIGHT_ANNOTATIONS: 'HIGHLIGHT_ANNOTATIONS',
......@@ -107,6 +108,11 @@ function reducer(state, action) {
state = annotationsReducer(state, action);
switch (action.type) {
case types.CLEAR_SELECTION:
return Object.assign({}, state, {
filterQuery: null,
selectedAnnotationMap: null,
});
case types.SELECT_ANNOTATIONS:
return Object.assign({}, state, {selectedAnnotationMap: action.selection});
case types.FOCUS_ANNOTATIONS:
......@@ -278,7 +284,7 @@ module.exports = function (settings) {
/** De-select all annotations. */
clearSelectedAnnotations: function () {
select({});
store.dispatch({type: 'CLEAR_SELECTION'});
},
/** Add annotations to the currently displayed set. */
......
'use strict';
var angular = require('angular');
var scrollIntoView = require('scroll-into-view');
var events = require('./events');
......@@ -131,20 +130,15 @@ module.exports = function AppController(
};
$scope.clearSelection = function () {
$scope.search.query = '';
annotationUI.clearSelectedAnnotations();
};
$scope.search = {
query: $location.search().q,
clear: function () {
$location.search('q', null);
query: function () {
return annotationUI.getState().filterQuery;
},
update: function (query) {
if (!angular.equals($location.search().q, query)) {
$location.search('q', query || null);
annotationUI.clearSelectedAnnotations();
}
}
annotationUI.setFilterQuery(query);
},
};
};
module.exports = ['$http', '$parse', ($http, $parse) ->
link: (scope, elem, attr, ctrl) ->
button = elem.find('button')
input = elem.find('input')
module.exports = ->
bindToController: true
controllerAs: 'vm'
controller: ['$element', '$http', '$scope', ($element, $http, $scope) ->
self = this
button = $element.find('button')
input = $element.find('input')[0]
form = $element.find('form')[0]
button.on('click', -> input[0].focus())
button.on('click', -> input.focus())
scope.reset = (event) ->
event.preventDefault()
scope.query = ''
scope.searchtext = ''
$scope.$watch (-> $http.pendingRequests.length), (pending) ->
self.loading = (pending > 0)
scope.search = (event) ->
event.preventDefault()
scope.query = scope.searchtext
form.onsubmit = (e) ->
e.preventDefault()
self.onSearch({$query: input.value})
scope.$watch (-> $http.pendingRequests.length), (pending) ->
scope.loading = (pending > 0)
this.inputClasses = ->
'is-expanded': self.alwaysExpanded || self.query.length > 0
scope.$watch 'query', (query) ->
return if query is undefined
scope.searchtext = query
if query
scope.onSearch?(query: scope.searchtext)
else
scope.onClear?()
this.$onChanges = (changes) ->
if changes.query
input.value = changes.query.currentValue
self
]
restrict: 'E'
scope:
# Specifies whether the search input field should always be expanded,
......@@ -32,21 +32,23 @@ module.exports = ['$http', '$parse', ($http, $parse) ->
#
# If false, it is only expanded when focused or when 'query' is non-empty
alwaysExpanded: '<'
query: '='
query: '<'
onSearch: '&'
onClear: '&'
template: '''
<form class="simple-search-form" ng-class="!searchtext && 'simple-search-inactive'" name="searchBox" ng-submit="search($event)">
<input class="simple-search-input" type="text" ng-model="searchtext" name="searchText"
placeholder="{{loading && 'Loading' || 'Search'}}…"
ng-disabled="loading"
ng-class="(alwaysExpanded || searchtext.length > 0) ? 'is-expanded' : ''"/>
<button type="button" class="simple-search-icon top-bar__btn" ng-hide="loading">
<form class="simple-search-form"
name="searchForm"
ng-class="!vm.query && 'simple-search-inactive'">
<input class="simple-search-input"
type="text"
name="query"
placeholder="{{vm.loading && 'Loading' || 'Search'}}…"
ng-disabled="vm.loading"
ng-class="vm.inputClasses()"/>
<button type="button" class="simple-search-icon top-bar__btn" ng-hide="vm.loading">
<i class="h-icon-search"></i>
</button>
<button type="button" class="simple-search-icon btn btn-clean" ng-show="loading" disabled>
<button type="button" class="simple-search-icon btn btn-clean" ng-show="vm.loading" disabled>
<span class="btn-icon"><span class="spinner"></span></span>
</button>
</form>
'''
]
......@@ -49,15 +49,9 @@ function RootThread($rootScope, annotationUI, searchFilter, viewFilter) {
function buildRootThread(state) {
var sortFn = sortFns[state.sortKey];
var filters;
var filterQuery = state.filterQuery;
if (filterQuery) {
filters = searchFilter.generateFacetedFilter(filterQuery);
}
var filterFn;
if (filterQuery) {
if (state.filterQuery) {
var filters = searchFilter.generateFacetedFilter(state.filterQuery);
filterFn = function (annot) {
return viewFilter.filter([annot], filters).length > 0;
};
......
......@@ -2,13 +2,13 @@ angular = require('angular')
module.exports = class StreamController
this.$inject = [
'$scope', '$route', '$rootScope', '$routeParams',
'$scope', '$location', '$route', '$rootScope', '$routeParams',
'annotationUI',
'queryParser', 'rootThread', 'searchFilter', 'store',
'streamer', 'streamFilter', 'annotationMapper'
]
constructor: (
$scope, $route, $rootScope, $routeParams
$scope, $location, $route, $rootScope, $routeParams
annotationUI,
queryParser, rootThread, searchFilter, store,
streamer, streamFilter, annotationMapper
......@@ -52,6 +52,11 @@ module.exports = class StreamController
$scope.forceVisible = (id) ->
annotationUI.setForceVisible(id, true)
Object.assign $scope.search, {
query: -> $routeParams.q || ''
update: (q) -> $location.search({q: q})
}
thread = ->
rootThread.thread(annotationUI.getState())
......
......@@ -202,6 +202,12 @@ describe('annotationUI', function () {
annotationUI.clearSelectedAnnotations();
assert.isNull(annotationUI.getState().selectedAnnotationMap);
});
it('clears the current search query', function () {
annotationUI.setFilterQuery('foo');
annotationUI.clearSelectedAnnotations();
assert.isNull(annotationUI.getState().filterQuery);
});
});
describe('#setFilterQuery()', function () {
......
......@@ -96,6 +96,7 @@ describe 'StreamController', ->
beforeEach inject (_$controller_, $rootScope) ->
$controller = _$controller_
$scope = $rootScope.$new()
$scope.search = {}
afterEach ->
sandbox.restore()
......
......@@ -264,12 +264,6 @@ module.exports = function WidgetController(
return crossframe.frames;
}, loadAnnotations);
// Watch the inputs that determine which annotations are currently
// visible and how they are sorted and rebuild the thread when they change
$scope.$watch('search.query', function (query) {
annotationUI.setFilterQuery(query);
});
$scope.setCollapsed = function (id, collapsed) {
annotationUI.setCollapsed(id, collapsed);
};
......
......@@ -5,9 +5,8 @@
<div class="top-bar__inner content" ng-if="::!isSidebar">
<simple-search
class="simple-search"
query="searchController.query"
on-search="searchController.update(query)"
on-clear="searchController.clear()"
query="searchController.query()"
on-search="searchController.update($query)"
always-expanded="true">
</simple-search>
<div class="top-bar__expander"></div>
......@@ -29,9 +28,8 @@
<div class="top-bar__expander"></div>
<simple-search
class="simple-search"
query="searchController.query"
on-search="searchController.update(query)"
on-clear="searchController.clear()"
query="searchController.query()"
on-search="searchController.update($query)"
title="Filter the annotation list">
</simple-search>
<sort-dropdown
......
......@@ -8,7 +8,7 @@
<search-status-bar
ng-show="!isLoading()"
ng-if="!isStream"
filter-active="search.query"
filter-active="!!search.query()"
filter-match-count="visibleCount()"
on-clear-selection="clearSelection()"
search-query="search ? search.query : ''"
......
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