Skip to content
This repository has been archived by the owner on May 29, 2019. It is now read-only.

Commit

Permalink
feat(typeahead): add focus-first option
Browse files Browse the repository at this point in the history
Add typeahead-focus-first option to prevent first match from being
focused.

Currently, the first result is automatically focused as you type. Now, set
`typeahead-focus-first="false"` and the first result is *not*
automatically focused as you type.

Closes #908
Closes #2916
  • Loading branch information
wkonkel authored and chrisirhc committed Nov 9, 2014
1 parent 992b232 commit 35d0cc1
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 4 deletions.
4 changes: 4 additions & 0 deletions src/typeahead/docs/readme.md
Expand Up @@ -51,3 +51,7 @@ The typeahead directives provide several attributes:
* `typeahead-wait-ms` <i class="glyphicon glyphicon-eye-open"></i>
_(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?
97 changes: 95 additions & 2 deletions src/typeahead/test/typeahead.spec.js
Expand Up @@ -80,7 +80,12 @@ describe('typeahead tests', function () {
this.message = function () {
return 'Expected "' + this.actual + '" to be opened.';
};
return typeaheadEl.length === 1 && typeaheadEl.hasClass('ng-hide') === false && liEls.length === noOfMatches && $(liEls[activeIdx]).hasClass('active');

return (typeaheadEl.length === 1 &&
typeaheadEl.hasClass('ng-hide') === false &&
liEls.length === noOfMatches &&
(activeIdx === -1 ? !$(liEls).hasClass('active') : $(liEls[activeIdx]).hasClass('active'))
);
}
});
});
Expand Down Expand Up @@ -409,7 +414,7 @@ describe('typeahead tests', function () {
triggerKeyDown(element, 38);
expect(element).toBeOpenWithActive(2, 1);

// Up arrow key goes back to last element
// Up arrow key goes back to first element
triggerKeyDown(element, 38);
expect(element).toBeOpenWithActive(2, 0);
});
Expand Down Expand Up @@ -670,4 +675,92 @@ describe('typeahead tests', function () {
});
});

describe('focus first', function () {
it('should focus the first element by default', function () {
var element = prepareInputEl('<div><input ng-model="result" typeahead="item for item in source | filter:$viewValue"></div>');
changeInputValueTo(element, 'b');
expect(element).toBeOpenWithActive(2, 0);

// Down arrow key
triggerKeyDown(element, 40);
expect(element).toBeOpenWithActive(2, 1);

// Down arrow key goes back to first element
triggerKeyDown(element, 40);
expect(element).toBeOpenWithActive(2, 0);

// Up arrow key goes back to last element
triggerKeyDown(element, 38);
expect(element).toBeOpenWithActive(2, 1);

// Up arrow key goes back to first element
triggerKeyDown(element, 38);
expect(element).toBeOpenWithActive(2, 0);
});

it('should not focus the first element until keys are pressed', function () {
var element = prepareInputEl('<div><input ng-model="result" typeahead="item for item in source | filter:$viewValue" typeahead-focus-first="false"></div>');
changeInputValueTo(element, 'b');
expect(element).toBeOpenWithActive(2, -1);

// Down arrow key goes to first element
triggerKeyDown(element, 40);
expect(element).toBeOpenWithActive(2, 0);

// Down arrow key goes to second element
triggerKeyDown(element, 40);
expect(element).toBeOpenWithActive(2, 1);

// Down arrow key goes back to first element
triggerKeyDown(element, 40);
expect(element).toBeOpenWithActive(2, 0);

// Up arrow key goes back to last element
triggerKeyDown(element, 38);
expect(element).toBeOpenWithActive(2, 1);

// Up arrow key goes back to first element
triggerKeyDown(element, 38);
expect(element).toBeOpenWithActive(2, 0);

// New input goes back to no focus
changeInputValueTo(element, 'a');
changeInputValueTo(element, 'b');
expect(element).toBeOpenWithActive(2, -1);

// Up arrow key goes to last element
triggerKeyDown(element, 38);
expect(element).toBeOpenWithActive(2, 1);
});
});

it('should not capture enter or tab until an item is focused', function () {
$scope.select_count = 0;
$scope.onSelect = function ($item, $model, $label) {
$scope.select_count = $scope.select_count + 1;
};
var element = prepareInputEl('<div><input ng-model="result" ng-keydown="keyDownEvent = $event" typeahead="item for item in source | filter:$viewValue" typeahead-on-select="onSelect($item, $model, $label)" typeahead-focus-first="false"></div>');
changeInputValueTo(element, 'b');

// enter key should not be captured when nothing is focused
triggerKeyDown(element, 13);
expect($scope.keyDownEvent.isDefaultPrevented()).toBeFalsy();
expect($scope.select_count).toEqual(0);

// tab key should not be captured when nothing is focused
triggerKeyDown(element, 9);
expect($scope.keyDownEvent.isDefaultPrevented()).toBeFalsy();
expect($scope.select_count).toEqual(0);

// down key should be captured and focus first element
triggerKeyDown(element, 40);
expect($scope.keyDownEvent.isDefaultPrevented()).toBeTruthy();
expect(element).toBeOpenWithActive(2, 0);

// enter key should be captured now that something is focused
triggerKeyDown(element, 13);
expect($scope.keyDownEvent.isDefaultPrevented()).toBeTruthy();
expect($scope.select_count).toEqual(1);
});

});
11 changes: 9 additions & 2 deletions src/typeahead/typeahead.js
Expand Up @@ -59,6 +59,8 @@ 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;

//INTERNAL VARIABLES

//model setter executed upon match selection
Expand Down Expand Up @@ -131,7 +133,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
if (onCurrentRequest && hasFocus) {
if (matches.length > 0) {

scope.activeIdx = 0;
scope.activeIdx = focusFirst ? 0 : -1;
scope.matches.length = 0;

//transform labels
Expand Down Expand Up @@ -272,14 +274,19 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
return;
}

// if there's nothing selected (i.e. focusFirst) and enter is hit, don't do anything
if (scope.activeIdx == -1 && (evt.which === 13 || evt.which === 9)) {
return;
}

evt.preventDefault();

if (evt.which === 40) {
scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
scope.$digest();

} else if (evt.which === 38) {
scope.activeIdx = (scope.activeIdx ? scope.activeIdx : scope.matches.length) - 1;
scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1;
scope.$digest();

} else if (evt.which === 13 || evt.which === 9) {
Expand Down

0 comments on commit 35d0cc1

Please sign in to comment.