Skip to content
This repository has been archived by the owner on Nov 22, 2021. It is now read-only.

Commit

Permalink
fix(tagsInput): Added focus outline
Browse files Browse the repository at this point in the history
Implemented a visual outline for the tags box which is visible when the
directive is focused so it's easy for keyboard users to determine where
they are on the page.

Closes #32.
  • Loading branch information
mbenford committed Dec 6, 2013
1 parent 69415a2 commit 7d3c51a
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 23 deletions.
7 changes: 7 additions & 0 deletions build/ng-tags-input.css
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@
font-size: 14px;
}

.ngTagsInput .tags.focused {
outline: none;
-webkit-box-shadow: 0px 0px 3px 1px rgba(5,139,242,0.6);
-moz-box-shadow: 0px 0px 3px 1px rgba(5,139,242,0.6);
box-shadow: 0px 0px 3px 1px rgba(5,139,242,0.6);
}

.ngTagsInput .tags ul {
margin: 0px;
padding: 0px;
Expand Down
19 changes: 13 additions & 6 deletions build/ng-tags-input.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ angular.module('tags-input').directive('tagsInput', ["$timeout","$document","con
replace: false,
transclude: true,
template: '<div class="ngTagsInput" tabindex="-1" ng-class="options.customClass" transclude-append>' +
' <div class="tags">' +
' <div class="tags" ng-class="{focused: hasFocus}">' +
' <ul>' +
' <li ng-repeat="tag in tags" ng-class="getCssClass($index)">' +
' <span>{{ tag }}</span>' +
Expand Down Expand Up @@ -237,17 +237,24 @@ angular.module('tags-input').directive('tagsInput', ["$timeout","$document","con
}
}
})
.on('blur', function() {
if (!scope.options.addOnBlur) {
.on('focus', function() {
if (scope.hasFocus) {
return;
}

scope.hasFocus = true;
scope.$apply();
})
.on('blur', function() {
$timeout(function() {
var parentElement = angular.element($document[0].activeElement).parent();
if (parentElement[0] !== element[0] && scope.tryAdd()) {
if (parentElement[0] !== element[0]) {
scope.hasFocus = false;
if (scope.options.addOnBlur) {
scope.tryAdd();
}
scope.$apply();
}
}, 0);
}, 0, false);
});

element.find('div').on('click', function() {
Expand Down
Binary file modified build/ng-tags-input.min.zip
Binary file not shown.
Binary file modified build/ng-tags-input.zip
Binary file not shown.
7 changes: 7 additions & 0 deletions css/tags-input.css
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@
font-size: 14px;
}

.ngTagsInput .tags.focused {
outline: none;
-webkit-box-shadow: 0px 0px 3px 1px rgba(5,139,242,0.6);
-moz-box-shadow: 0px 0px 3px 1px rgba(5,139,242,0.6);
box-shadow: 0px 0px 3px 1px rgba(5,139,242,0.6);
}

.ngTagsInput .tags ul {
margin: 0px;
padding: 0px;
Expand Down
19 changes: 13 additions & 6 deletions src/tags-input.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ angular.module('tags-input').directive('tagsInput', function($timeout, $document
replace: false,
transclude: true,
template: '<div class="ngTagsInput" tabindex="-1" ng-class="options.customClass" transclude-append>' +
' <div class="tags">' +
' <div class="tags" ng-class="{focused: hasFocus}">' +
' <ul>' +
' <li ng-repeat="tag in tags" ng-class="getCssClass($index)">' +
' <span>{{ tag }}</span>' +
Expand Down Expand Up @@ -226,17 +226,24 @@ angular.module('tags-input').directive('tagsInput', function($timeout, $document
}
}
})
.on('blur', function() {
if (!scope.options.addOnBlur) {
.on('focus', function() {
if (scope.hasFocus) {
return;
}

scope.hasFocus = true;
scope.$apply();
})
.on('blur', function() {
$timeout(function() {
var parentElement = angular.element($document[0].activeElement).parent();
if (parentElement[0] !== element[0] && scope.tryAdd()) {
if (parentElement[0] !== element[0]) {
scope.hasFocus = false;
if (scope.options.addOnBlur) {
scope.tryAdd();
}
scope.$apply();
}
}, 0);
}, 0, false);
});

element.find('div').on('click', function() {
Expand Down
89 changes: 79 additions & 10 deletions test/tags-input.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,77 @@ describe('tags-input-directive', function() {
expect(element.find('div').hasClass('myClass')).toBe(true);
});
});

describe('focus outline', function() {
beforeEach(function() {
compile();
});

it('outlines the tags div when the focused property is true', function() {
// Arrange
isolateScope.hasFocus = true;

// Act
$scope.$digest();

// Assert
expect(element.find('div.tags').hasClass('focused')).toBe(true);
});

it('does not outline the tags div when the focused property is false', function() {
// Arrange
isolateScope.hasFocus = false;

// Act
$scope.$digest();

// Assert
expect(element.find('div.tags').hasClass('focused')).toBe(false);
});

it('sets the focused property to true when the input field gains focus', function() {
// Arrange
isolateScope.hasFocus = false;
spyOn($scope, '$digest');

// Act
getInput().triggerHandler('focus');

// Assert
expect(isolateScope.hasFocus).toBe(true);
expect($scope.$digest).toHaveBeenCalled();
});

it('sets the focused property to false when the input field loses focus', function() {
// Arrange
var body = $document.find('body');
body.append(element);
body.focus();

isolateScope.hasFocus = true;
spyOn($scope, '$digest');

// Act
getInput().triggerHandler('blur');
$timeout.flush();

// Assert
expect(isolateScope.hasFocus).toBe(false);
expect($scope.$digest).toHaveBeenCalled();
});

it('does not trigger a digest cycle when the input field is focused already', function() {
// Arrange
isolateScope.hasFocus = true;
spyOn($scope, '$digest');

// Act
getInput().triggerHandler('focus');

// Assert
expect($scope.$digest).not.toHaveBeenCalled();
});
});

describe('tabindex option', function() {
it('sets the input field tab index', function() {
Expand Down Expand Up @@ -371,24 +442,22 @@ describe('tags-input-directive', function() {
});

describe('option is true', function() {
var anotherElement;
var body;

beforeEach(function() {
compile('add-on-blur="true"');
anotherElement = angular.element('<div></div>');

$document.find('body')
.append(element)
.append(anotherElement);
body = $document.find('body');
body.append(element);
});

it('adds a tag when the input field loses focus to any element on the page but the directive itself', function() {
// Arrange
isolateScope.newTag = 'foo';
anotherElement[0].focus();
body.focus();

// Act
getInput().trigger('blur');
getInput().triggerHandler('blur');
$timeout.flush();

// Assert
Expand All @@ -398,10 +467,10 @@ describe('tags-input-directive', function() {
it('does not add a tag when the input field loses focus to the directive itself', function() {
// Arrange
isolateScope.newTag = 'foo';
element.find('div')[0].focus();
element.find('div').focus();

// Act
getInput().trigger('blur');
getInput().triggerHandler('blur');
$timeout.flush();

// Assert
Expand All @@ -416,7 +485,7 @@ describe('tags-input-directive', function() {
isolateScope.newTag = 'foo';

// Act
getInput().trigger('blur');
getInput().triggerHandler('blur');

// Assert
expect($scope.tags).toEqual([]);
Expand Down
2 changes: 1 addition & 1 deletion test/test-page.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<tags-input ng-model="tags"
placeholder="{{ placeholder.value }}"
replace-spaces-with-dashes="false"
add-on-blur="false">
add-on-blur="true">
<auto-complete source="loadItems($query)"
debounce-delay="0"
min-length="1"
Expand Down

0 comments on commit 7d3c51a

Please sign in to comment.