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

fix(dropdown): fix keyboard-nav #6154

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions src/dropdown/dropdown.js
Original file line number Diff line number Diff line change
@@ -24,10 +24,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
if (openScope === dropdownScope) {
openScope = null;
$document.off('click', closeDropdown);
var dropdownMenu = dropdownScope.getDropdownElement();
if (dropdownMenu) {
dropdownMenu.off('keydown', this.keybindFilter);
}
$document.off('keydown', this.keybindFilter);
}
};

@@ -60,11 +57,15 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
};

this.keybindFilter = function(evt) {
var dropdownElement = openScope.getDropdownElement();
var toggleElement = openScope.getToggleElement();
var dropdownElementTargeted = dropdownElement && dropdownElement[0].contains(evt.target);
var toggleElementTargeted = toggleElement && toggleElement[0].contains(evt.target);
if (evt.which === 27) {
evt.stopPropagation();
openScope.focusToggleElement();
closeDropdown();
} else if (openScope.isKeynavEnabled() && [38, 40].indexOf(evt.which) !== -1 && openScope.isOpen) {
} else if (openScope.isKeynavEnabled() && [38, 40].indexOf(evt.which) !== -1 && openScope.isOpen && (dropdownElementTargeted || toggleElementTargeted)) {
evt.preventDefault();
evt.stopPropagation();
openScope.focusDropdownEntry(evt.which);
@@ -256,13 +257,11 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
var newEl = dropdownElement;
self.dropdownMenu.replaceWith(newEl);
self.dropdownMenu = newEl;
self.dropdownMenu.on('keydown', uibDropdownService.keybindFilter);
$document.on('keydown', uibDropdownService.keybindFilter);
});
});
} else {
if (self.dropdownMenu) {
self.dropdownMenu.on('keydown', uibDropdownService.keybindFilter);
}
$document.on('keydown', uibDropdownService.keybindFilter);
}

scope.focusToggleElement();
52 changes: 24 additions & 28 deletions src/dropdown/test/dropdown.spec.js
Original file line number Diff line number Diff line change
@@ -550,70 +550,67 @@ describe('uib-dropdown', function() {
function dropdown() {
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);
}
function getFocusedElement() {
return angular.element(document.activeElement);
}
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(dropdownMenu, 40);
triggerKeyDown(getFocusedElement(), 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(dropdownMenu, 40);
triggerKeyDown(getFocusedElement(), 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(dropdownMenu, 40);
triggerKeyDown(dropdownMenu, 40);
triggerKeyDown(getFocusedElement(), 40);
triggerKeyDown(getFocusedElement(), 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(dropdownMenu, 38);
triggerKeyDown(getFocusedElement(), 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(dropdownMenu, 38);
triggerKeyDown(getFocusedElement(), 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(dropdownMenu, 37);
triggerKeyDown(getFocusedElement(), 37);

expect(element).toHaveClass(dropdownConfig.openClass);
var focusEl = element.find('ul').eq(0).find('a');
@@ -622,25 +619,23 @@ 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(dropdownMenu, 40);
triggerKeyDown(dropdownMenu, 40);
triggerKeyDown(getFocusedElement(), 40);
triggerKeyDown(getFocusedElement(), 40);

triggerKeyDown(dropdownMenu, 38);
triggerKeyDown(getFocusedElement(), 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(dropdownMenu, 40);
triggerKeyDown(dropdownMenu, 40);
triggerKeyDown(getFocusedElement(), 40);
triggerKeyDown(getFocusedElement(), 40);

expect(element).toHaveClass(dropdownConfig.openClass);
var focusEl = element.find('ul').eq(0).find('a').eq(1);
@@ -652,47 +647,48 @@ 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(dropdownMenu, 40);
triggerKeyDown(getFocusedElement(), 40);

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

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

describe('with dropdown-append-to-body', function() {
function dropdown() {
return $compile('<li uib-dropdown dropdown-append-to-body keyboard-nav><a href uib-dropdown-toggle></a><ul uib-dropdown-menu id="dropdown-menu"><li><a href>Hello On Body</a></li><li><a href>Hello Again</a></li></ul></li>')($rootScope);
return $compile('<li uib-dropdown dropdown-append-to-body keyboard-nav><a href uib-dropdown-toggle>foo</a><ul uib-dropdown-menu id="dropdown-menu"><li><a href>Hello On Body</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() {
$document.find('body').append(element);
clickDropdownToggle();

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

triggerKeyDown(dropdownMenu, 40);
triggerKeyDown(getFocusedElement(), 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() {
$document.find('body').append(element);
clickDropdownToggle();
var dropdownMenu = $document.find('#dropdown-menu');
triggerKeyDown(dropdownMenu, 40);
triggerKeyDown(dropdownMenu, 40);
triggerKeyDown(dropdownMenu, 40);
triggerKeyDown(getFocusedElement(), 40);
triggerKeyDown(getFocusedElement(), 40);
triggerKeyDown(getFocusedElement(), 40);

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