From fb0da55fcb9c6874ac59782ddb93f4f7e884eab0 Mon Sep 17 00:00:00 2001 From: Jason Schindler Date: Fri, 24 Jul 2015 11:51:31 -0500 Subject: [PATCH] feat(typeahead): Adds 'select on exact'. --- src/typeahead/docs/readme.md | 14 ++++--- src/typeahead/test/typeahead.spec.js | 57 ++++++++++++++++++++++++++++ src/typeahead/typeahead.js | 17 +++++++++ 3 files changed, 83 insertions(+), 5 deletions(-) diff --git a/src/typeahead/docs/readme.md b/src/typeahead/docs/readme.md index a6538b1a0a..ccbff4d140 100644 --- a/src/typeahead/docs/readme.md +++ b/src/typeahead/docs/readme.md @@ -27,6 +27,10 @@ The typeahead directives provide several attributes: * `typeahead-editable` _(Defaults: true)_ : Should it restrict model values to the ones selected from the popup only ? + +* `typeahead-focus-first` + _(Defaults: true)_ : + Should the first match automatically be focused as you type? * `typeahead-input-formatter` _(Defaults: undefined)_ : @@ -39,11 +43,15 @@ The typeahead directives provide several attributes: * `typeahead-min-length` _(Defaults: 1)_ : Minimal no of characters that needs to be entered before typeahead kicks-in - + * `typeahead-on-select($item, $model, $label)` _(Defaults: null)_ : A callback executed when a match is selected +* `typeahead-select-on-exact` + _(Defaults: false)_ : + Should it automatically select an item when there is one option that exactly matches the user input? + * `typeahead-template-url` : Set custom item template @@ -52,10 +60,6 @@ The typeahead directives provide several attributes: _(Defaults: 0)_ : Minimal wait time after last character typed before typeahead kicks-in -* `typeahead-focus-first` - _(Defaults: true)_ : - Should the first match automatically be focused as you type? - * `typeahead-select-on-blur` _(Defaults: false)_ : On blur, select the currently highlighted match diff --git a/src/typeahead/test/typeahead.spec.js b/src/typeahead/test/typeahead.spec.js index 885546bcc8..a8b38f6492 100644 --- a/src/typeahead/test/typeahead.spec.js +++ b/src/typeahead/test/typeahead.spec.js @@ -456,6 +456,63 @@ describe('typeahead tests', function () { expect(inputEl.val()).toEqual('AL'); }); }); + + describe('select on exact match', function(){ + + it('should select on an exact match when set', function () { + + $scope.onSelect = jasmine.createSpy('onSelect'); + var element = prepareInputEl('
'); + var inputEl = findInput(element); + + changeInputValueTo(element, 'bar'); + + expect($scope.result).toEqual('bar'); + expect(inputEl.val()).toEqual('bar'); + expect(element).toBeClosed(); + expect($scope.onSelect).toHaveBeenCalled(); + }); + + it('should not select on an exact match by default', function () { + + $scope.onSelect = jasmine.createSpy('onSelect'); + var element = prepareInputEl('
'); + var inputEl = findInput(element); + + changeInputValueTo(element, 'bar'); + + expect($scope.result).toBeUndefined(); + expect(inputEl.val()).toEqual('bar'); + expect($scope.onSelect.calls.any()).toBe(false); + }); + + it('should not be case sensitive when select on an exact match', function () { + + $scope.onSelect = jasmine.createSpy('onSelect'); + var element = prepareInputEl('
'); + var inputEl = findInput(element); + + changeInputValueTo(element, 'BaR'); + + expect($scope.result).toEqual('bar'); + expect(inputEl.val()).toEqual('bar'); + expect(element).toBeClosed(); + expect($scope.onSelect).toHaveBeenCalled(); + }); + + it('should not auto select when not a match with one potential result left', function () { + + $scope.onSelect = jasmine.createSpy('onSelect'); + var element = prepareInputEl('
'); + var inputEl = findInput(element); + + changeInputValueTo(element, 'fo'); + + expect($scope.result).toBeUndefined(); + expect(inputEl.val()).toEqual('fo'); + expect($scope.onSelect.calls.any()).toBe(false); + }); + }); describe('pop-up interaction', function () { var element; diff --git a/src/typeahead/typeahead.js b/src/typeahead/typeahead.js index 954e494b5f..4ebca90f42 100644 --- a/src/typeahead/typeahead.js +++ b/src/typeahead/typeahead.js @@ -67,6 +67,9 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false; var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false; + + //If input matches an item of the list exactly, select it automatically + var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false; //INTERNAL VARIABLES @@ -133,6 +136,15 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap element.attr('aria-activedescendant', getMatchId(index)); } }); + + var inputIsExactMatch = function(inputValue, index) { + + if (scope.matches.length > index && inputValue){ + return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase(); + } + + return false; + }; var getMatchesAsync = function(inputValue) { @@ -166,6 +178,11 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap recalculatePosition(); element.attr('aria-expanded', true); + + //Select the single remaining option if user input matches + if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)){ + scope.select(0); + } } else { resetMatches(); }