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

Commit

Permalink
fix(dropdown): bind event listener on dropdown menu
Browse files Browse the repository at this point in the history
- Bind keypress listener to dropdown menu for handling keyboard navigation

Closes #5982
  • Loading branch information
wesleycho committed Jun 13, 2016
1 parent fb38d0d commit 6038403
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 38 deletions.
10 changes: 6 additions & 4 deletions src/dropdown/dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
this.open = function(dropdownScope, element) {
if (!openScope) {
$document.on('click', closeDropdown);
element.on('keydown', keybindFilter);
}

if (openScope && openScope !== dropdownScope) {
Expand All @@ -25,7 +24,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
if (openScope === dropdownScope) {
openScope = null;
$document.off('click', closeDropdown);
element.off('keydown', keybindFilter);
dropdownScope.getDropdownElement().off('keydown', this.keybindFilter);
}
};

Expand Down Expand Up @@ -57,7 +56,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
}
};

var keybindFilter = function(evt) {
this.keybindFilter = function(evt) {
if (evt.which === 27) {
evt.stopPropagation();
openScope.focusToggleElement();
Expand Down Expand Up @@ -248,13 +247,17 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
var newEl = dropdownElement;
self.dropdownMenu.replaceWith(newEl);
self.dropdownMenu = newEl;
self.dropdownMenu.on('keydown', uibDropdownService.keybindFilter);
});
});
} else {
scope.getDropdownElement().on('keydown', uibDropdownService.keybindFilter);
}

scope.focusToggleElement();
uibDropdownService.open(scope, $element);
} else {
uibDropdownService.close(scope, $element);
if (self.dropdownMenuTemplateUrl) {
if (templateScope) {
templateScope.$destroy();
Expand All @@ -264,7 +267,6 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
self.dropdownMenu = newEl;
}

uibDropdownService.close(scope, $element);
self.selectedOption = null;
}

Expand Down
78 changes: 44 additions & 34 deletions src/dropdown/test/dropdown.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ describe('uib-dropdown', function() {

describe('basic', function() {
function dropdown() {
return $compile('<li uib-dropdown><a href uib-dropdown-toggle></a><ul><li><a href>Hello</a></li></ul></li>')($rootScope);
return $compile('<li uib-dropdown><a href uib-dropdown-toggle></a><ul uib-dropdown-menu><li><a href>Hello</a></li></ul></li>')($rootScope);
}

beforeEach(function() {
Expand Down Expand Up @@ -69,9 +69,10 @@ describe('uib-dropdown', function() {
});

it('should close on escape key & focus toggle element', function() {
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
clickDropdownToggle();
var event = triggerKeyDown(element, 27);
var event = triggerKeyDown(dropdownMenu, 27);
expect(element).not.toHaveClass(dropdownConfig.openClass);
expect(element.find('a')).toHaveFocus();
expect(event.stopPropagation).toHaveBeenCalled();
Expand Down Expand Up @@ -108,7 +109,7 @@ describe('uib-dropdown', function() {
});

it('should not toggle if the element has `disabled` class', function() {
var elm = $compile('<li uib-dropdown><a class="disabled" uib-dropdown-toggle></a><ul><li>Hello</li></ul></li>')($rootScope);
var elm = $compile('<li uib-dropdown><a class="disabled" uib-dropdown-toggle></a><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
clickDropdownToggle( elm );
expect(elm).not.toHaveClass(dropdownConfig.openClass);
});
Expand All @@ -121,7 +122,7 @@ describe('uib-dropdown', function() {

it('should not toggle if the element has `ng-disabled` as true', function() {
$rootScope.isdisabled = true;
var elm = $compile('<li uib-dropdown><div ng-disabled="isdisabled" uib-dropdown-toggle></div><ul><li>Hello</li></ul></li>')($rootScope);
var elm = $compile('<li uib-dropdown><div ng-disabled="isdisabled" uib-dropdown-toggle></div><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
$rootScope.$digest();
elm.find('div').click();
expect(elm).not.toHaveClass(dropdownConfig.openClass);
Expand All @@ -134,7 +135,7 @@ describe('uib-dropdown', function() {

it('should unbind events on scope destroy', function() {
var $scope = $rootScope.$new();
var elm = $compile('<li uib-dropdown><button ng-disabled="isdisabled" uib-dropdown-toggle></button><ul><li>Hello</li></ul></li>')($scope);
var elm = $compile('<li uib-dropdown><button ng-disabled="isdisabled" uib-dropdown-toggle></button><ul uib-dropdown-menu><li>Hello</li></ul></li>')($scope);
$scope.$digest();

var buttonEl = elm.find('button');
Expand Down Expand Up @@ -306,7 +307,7 @@ describe('uib-dropdown', function() {
describe('with uib-dropdown-toggle', function() {
beforeEach(function() {
$rootScope.isopen = true;
element = $compile('<li uib-dropdown is-open="isopen"><a href uib-dropdown-toggle></a><ul><li>Hello</li></ul></li>')($rootScope);
element = $compile('<li uib-dropdown is-open="isopen"><a href uib-dropdown-toggle></a><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
$rootScope.$digest();
});

Expand Down Expand Up @@ -340,7 +341,7 @@ describe('uib-dropdown', function() {
describe('without uib-dropdown-toggle', function() {
beforeEach(function() {
$rootScope.isopen = true;
element = $compile('<li uib-dropdown is-open="isopen"><ul><li>Hello</li></ul></li>')($rootScope);
element = $compile('<li uib-dropdown is-open="isopen"><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
$rootScope.$digest();
});

Expand All @@ -361,7 +362,7 @@ describe('uib-dropdown', function() {
beforeEach(function() {
$rootScope.toggleHandler = jasmine.createSpy('toggleHandler');
$rootScope.isopen = false;
element = $compile('<li uib-dropdown on-toggle="toggleHandler(open)" is-open="isopen"><a uib-dropdown-toggle></a><ul><li>Hello</li></ul></li>')($rootScope);
element = $compile('<li uib-dropdown on-toggle="toggleHandler(open)" is-open="isopen"><a uib-dropdown-toggle></a><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
$rootScope.$digest();
});

Expand All @@ -388,7 +389,7 @@ describe('uib-dropdown', function() {
beforeEach(function() {
$rootScope.toggleHandler = jasmine.createSpy('toggleHandler');
$rootScope.isopen = true;
element = $compile('<li uib-dropdown on-toggle="toggleHandler(open)" is-open="isopen"><a uib-dropdown-toggle></a><ul><li>Hello</li></ul></li>')($rootScope);
element = $compile('<li uib-dropdown on-toggle="toggleHandler(open)" is-open="isopen"><a uib-dropdown-toggle></a><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
$rootScope.$digest();
});

Expand Down Expand Up @@ -416,7 +417,7 @@ describe('uib-dropdown', function() {
describe('without is-open', function() {
beforeEach(function() {
$rootScope.toggleHandler = jasmine.createSpy('toggleHandler');
element = $compile('<li uib-dropdown on-toggle="toggleHandler(open)"><a uib-dropdown-toggle></a><ul><li>Hello</li></ul></li>')($rootScope);
element = $compile('<li uib-dropdown on-toggle="toggleHandler(open)"><a uib-dropdown-toggle></a><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
$rootScope.$digest();
});

Expand Down Expand Up @@ -444,7 +445,7 @@ describe('uib-dropdown', function() {
function dropdown(autoClose) {
return $compile('<li uib-dropdown ' +
(autoClose === undefined ? '' : 'auto-close="' + autoClose + '"') +
'><a href uib-dropdown-toggle></a><ul><li><a href>Hello</a></li></ul></li>')($rootScope);
'><a href uib-dropdown-toggle></a><ul uib-dropdown-menu><li><a href>Hello</a></li></ul></li>')($rootScope);
}

describe('always', function() {
Expand Down Expand Up @@ -476,7 +477,7 @@ describe('uib-dropdown', function() {

it('control with is-open', function() {
$rootScope.isopen = true;
element = $compile('<li uib-dropdown is-open="isopen" auto-close="disabled"><a href uib-dropdown-toggle></a><ul><li>Hello</li></ul></li>')($rootScope);
element = $compile('<li uib-dropdown is-open="isopen" auto-close="disabled"><a href uib-dropdown-toggle></a><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
$rootScope.$digest();

expect(element).toHaveClass(dropdownConfig.openClass);
Expand All @@ -499,9 +500,10 @@ describe('uib-dropdown', function() {

it('should close anyway if esc is pressed', function() {
element = dropdown('disabled');
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
clickDropdownToggle();
triggerKeyDown(element, 27);
triggerKeyDown(dropdownMenu, 27);
expect(element).not.toHaveClass(dropdownConfig.openClass);
expect(element.find('a')).toHaveFocus();
});
Expand Down Expand Up @@ -546,66 +548,72 @@ describe('uib-dropdown', function() {

describe('using keyboard-nav', function() {
function dropdown() {
return $compile('<li uib-dropdown keyboard-nav><a href uib-dropdown-toggle></a><ul><li><a href>Hello</a></li><li><a href>Hello Again</a></li></ul></li>')($rootScope);
return $compile('<li uib-dropdown keyboard-nav><a href uib-dropdown-toggle></a><ul uib-dropdown-menu><li><a href>Hello</a></li><li><a href>Hello Again</a></li></ul></li>')($rootScope);
}
beforeEach(function() {
element = dropdown();
});

it('should focus first list element when down arrow pressed', function() {
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
clickDropdownToggle();
triggerKeyDown(element, 40);
triggerKeyDown(dropdownMenu, 40);

expect(element).toHaveClass(dropdownConfig.openClass);
var optionEl = element.find('ul').eq(0).find('a').eq(0);
expect(optionEl).toHaveFocus();
});

it('should not focus first list element when down arrow pressed if closed', function() {
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
triggerKeyDown(element, 40);
triggerKeyDown(dropdownMenu, 40);

expect(element).not.toHaveClass(dropdownConfig.openClass);
var focusEl = element.find('ul').eq(0).find('a').eq(0);
expect(focusEl).not.toHaveFocus();
});

it('should focus second list element when down arrow pressed twice', function() {
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
clickDropdownToggle();
triggerKeyDown(element, 40);
triggerKeyDown(element, 40);
triggerKeyDown(dropdownMenu, 40);
triggerKeyDown(dropdownMenu, 40);

expect(element).toHaveClass(dropdownConfig.openClass);
var focusEl = element.find('ul').eq(0).find('a').eq(1);
expect(focusEl).toHaveFocus();
});

it('should not focus first list element when up arrow pressed after dropdown toggled', function() {
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
clickDropdownToggle();
expect(element).toHaveClass(dropdownConfig.openClass);

triggerKeyDown(element, 38);
triggerKeyDown(dropdownMenu, 38);
var focusEl = element.find('ul').eq(0).find('a').eq(0);
expect(focusEl).not.toHaveFocus();
});

it('should focus last list element when up arrow pressed after dropdown toggled', function() {
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
clickDropdownToggle();
triggerKeyDown(element, 38);
triggerKeyDown(dropdownMenu, 38);

expect(element).toHaveClass(dropdownConfig.openClass);
var focusEl = element.find('ul').eq(0).find('a').eq(1);
expect(focusEl).toHaveFocus();
});

it('should not change focus when other keys are pressed', function() {
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
clickDropdownToggle();
triggerKeyDown(element, 37);
triggerKeyDown(dropdownMenu, 37);

expect(element).toHaveClass(dropdownConfig.openClass);
var focusEl = element.find('ul').eq(0).find('a');
Expand All @@ -614,23 +622,25 @@ describe('uib-dropdown', function() {
});

it('should focus first list element when down arrow pressed 2x and up pressed 1x', function() {
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
clickDropdownToggle();
triggerKeyDown(element, 40);
triggerKeyDown(element, 40);
triggerKeyDown(dropdownMenu, 40);
triggerKeyDown(dropdownMenu, 40);

triggerKeyDown(element, 38);
triggerKeyDown(dropdownMenu, 38);

expect(element).toHaveClass(dropdownConfig.openClass);
var focusEl = element.find('ul').eq(0).find('a').eq(0);
expect(focusEl).toHaveFocus();
});

it('should stay focused on final list element if down pressed at list end', function() {
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
clickDropdownToggle();
triggerKeyDown(element, 40);
triggerKeyDown(element, 40);
triggerKeyDown(dropdownMenu, 40);
triggerKeyDown(dropdownMenu, 40);

expect(element).toHaveClass(dropdownConfig.openClass);
var focusEl = element.find('ul').eq(0).find('a').eq(1);
Expand All @@ -642,16 +652,17 @@ describe('uib-dropdown', function() {

it('should close if esc is pressed while focused', function() {
element = dropdown('disabled');
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
clickDropdownToggle();

triggerKeyDown(element, 40);
triggerKeyDown(dropdownMenu, 40);

expect(element).toHaveClass(dropdownConfig.openClass);
var focusEl = element.find('ul').eq(0).find('a').eq(0);
expect(focusEl).toHaveFocus();

triggerKeyDown(element, 27);
triggerKeyDown(dropdownMenu, 27);
expect(element).not.toHaveClass(dropdownConfig.openClass);
});

Expand All @@ -667,22 +678,21 @@ describe('uib-dropdown', function() {
it('should focus first list element when down arrow pressed', function() {
clickDropdownToggle();

triggerKeyDown(element, 40);

var dropdownMenu = $document.find('#dropdown-menu');

triggerKeyDown(dropdownMenu, 40);

expect(dropdownMenu.parent()).toHaveClass(dropdownConfig.appendToOpenClass);
var focusEl = $document.find('ul').eq(0).find('a');
expect(focusEl).toHaveFocus();
});

it('should focus second list element when down arrow pressed twice', function() {
clickDropdownToggle();
triggerKeyDown(element, 40);
triggerKeyDown(element, 40);
triggerKeyDown(element, 40);

var dropdownMenu = $document.find('#dropdown-menu');
triggerKeyDown(dropdownMenu, 40);
triggerKeyDown(dropdownMenu, 40);
triggerKeyDown(dropdownMenu, 40);

expect(dropdownMenu.parent()).toHaveClass(dropdownConfig.appendToOpenClass);
var elem1 = $document.find('ul');
Expand Down

0 comments on commit 6038403

Please sign in to comment.