diff --git a/src/typeahead/docs/readme.md b/src/typeahead/docs/readme.md index a749fb92b8..6248af76a7 100644 --- a/src/typeahead/docs/readme.md +++ b/src/typeahead/docs/readme.md @@ -78,3 +78,7 @@ The typeahead directives provide several attributes: * `typeahead-focus-on-select` _(Defaults: true) : On selection, focus the input element the typeahead directive is associated with + +* `typeahead-is-open` + _(Defaults: angular.noop)_ : + Binding to a variable that indicates if dropdown is open diff --git a/src/typeahead/test/typeahead.spec.js b/src/typeahead/test/typeahead.spec.js index 0ccb243220..b03b13f131 100644 --- a/src/typeahead/test/typeahead.spec.js +++ b/src/typeahead/test/typeahead.spec.js @@ -588,6 +588,78 @@ describe('typeahead tests', function() { }); }); + describe('is-open indicator', function () { + var element; + + beforeEach(function () { + element = prepareInputEl('
'); + }); + + it('should bind is-open indicator as true when matches are returned', function () { + expect($scope.isOpen).toBeFalsy(); + changeInputValueTo(element, 'b'); + expect($scope.isOpen).toBeTruthy(); + }); + + it('should bind is-open indicator as false when no matches returned', function () { + expect($scope.isOpen).toBeFalsy(); + changeInputValueTo(element, 'b'); + expect($scope.isOpen).toBeTruthy(); + changeInputValueTo(element, 'not match'); + expect($scope.isOpen).toBeFalsy(); + }); + + it('should bind is-open indicator as false when a match is clicked', function () { + expect($scope.isOpen).toBeFalsy(); + changeInputValueTo(element, 'b'); + expect($scope.isOpen).toBeTruthy(); + var match = findMatches(element).find('a').eq(0); + + match.click(); + $scope.$digest(); + expect($scope.isOpen).toBeFalsy(); + }); + it('should bind is-open indicator as false when click outside', function () { + expect($scope.isOpen).toBeFalsy(); + changeInputValueTo(element, 'b'); + expect($scope.isOpen).toBeTruthy(); + $document.find('body').click(); + $scope.$digest(); + expect($scope.isOpen).toBeFalsy(); + }); + + it('should bind is-open indicator as false on enter', function () { + expect($scope.isOpen).toBeFalsy(); + changeInputValueTo(element, 'b'); + expect($scope.isOpen).toBeTruthy(); + triggerKeyDown(element, 13); + expect($scope.isOpen).toBeFalsy(); + }); + + it('should bind is-open indicator as false on tab', function () { + expect($scope.isOpen).toBeFalsy(); + changeInputValueTo(element, 'b'); + expect($scope.isOpen).toBeTruthy(); + triggerKeyDown(element, 9); + expect($scope.isOpen).toBeFalsy(); + }); + + it('should bind is-open indicator as false on escape key', function () { + expect($scope.isOpen).toBeFalsy(); + changeInputValueTo(element, 'b'); + expect($scope.isOpen).toBeTruthy(); + triggerKeyDown(element, 27); + expect($scope.isOpen).toBeFalsy(); + }); + + it('should bind is-open indicator as false input value smaller than a defined threshold', function () { + var element = prepareInputEl(''); + expect($scope.isToggled).toBeFalsy(); + changeInputValueTo(element, 'b'); + expect($scope.isToggled).toBeFalsy(); + }); + }); + describe('pop-up interaction', function() { var element; diff --git a/src/typeahead/typeahead.js b/src/typeahead/typeahead.js index 37b041a81e..bacbe7217b 100644 --- a/src/typeahead/typeahead.js +++ b/src/typeahead/typeahead.js @@ -68,6 +68,9 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position']) //If input matches an item of the list exactly, select it automatically var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false; + //binding to a variable that indicates if dropdown is open + var isOpenSetter = $parse(attrs.typeaheadIsOpen).assign || angular.noop; + //INTERNAL VARIABLES //model setter executed upon match selection @@ -117,7 +120,8 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position']) select: 'select(activeIdx)', 'move-in-progress': 'moveInProgress', query: 'query', - position: 'position' + position: 'position', + 'assign-is-open': 'assignIsOpen(isOpen)' }); //custom item template if (angular.isDefined(attrs.typeaheadTemplateUrl)) { @@ -268,6 +272,10 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position']) resetMatches(); + scope.assignIsOpen = function (isOpen) { + isOpenSetter(originalScope, isOpen); + }; + scope.select = function(activeIdx) { //called from within the $digest() cycle var locals = {}; @@ -463,7 +471,8 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position']) active: '=', position: '&', moveInProgress: '=', - select: '&' + select: '&', + assignIsOpen: '&', }, replace: true, templateUrl: function(element, attrs) { @@ -472,8 +481,10 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position']) link: function(scope, element, attrs) { scope.templateUrl = attrs.templateUrl; - scope.isOpen = function() { - return scope.matches.length > 0; + scope.isOpen = function () { + var isDropdownOpen = scope.matches.length > 0; + scope.assignIsOpen({ isOpen: isDropdownOpen }); + return isDropdownOpen; }; scope.isActive = function(matchIdx) {