From 6bad7592b9d7838099d920c98cddc7cb13933c9c Mon Sep 17 00:00:00 2001 From: Matt Lewis Date: Thu, 11 Aug 2016 10:26:10 +0100 Subject: [PATCH] fix(dropdown): fix keyboard-nav - Fix keyboard-nav for when not using append-to or append-to-body Fixes #6102 Closes #6154 --- src/dropdown/dropdown.js | 17 +++++----- src/dropdown/test/dropdown.spec.js | 52 ++++++++++++++---------------- 2 files changed, 32 insertions(+), 37 deletions(-) diff --git a/src/dropdown/dropdown.js b/src/dropdown/dropdown.js index ab60a2db33..cd15309cf2 100644 --- a/src/dropdown/dropdown.js +++ b/src/dropdown/dropdown.js @@ -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(); diff --git a/src/dropdown/test/dropdown.spec.js b/src/dropdown/test/dropdown.spec.js index 7a932f2c37..6f3b99d4e2 100644 --- a/src/dropdown/test/dropdown.spec.js +++ b/src/dropdown/test/dropdown.spec.js @@ -550,15 +550,17 @@ describe('uib-dropdown', function() { function dropdown() { return $compile('
  • ')($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); @@ -566,9 +568,8 @@ describe('uib-dropdown', function() { }); 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); @@ -576,11 +577,10 @@ describe('uib-dropdown', function() { }); 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); @@ -588,21 +588,19 @@ describe('uib-dropdown', function() { }); 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); @@ -610,10 +608,9 @@ describe('uib-dropdown', function() { }); 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,13 +619,12 @@ 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); @@ -636,11 +632,10 @@ describe('uib-dropdown', function() { }); 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,23 +647,22 @@ 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('
  • ')($rootScope); + return $compile('
  • foo
  • ')($rootScope); } beforeEach(function() { @@ -676,11 +670,12 @@ describe('uib-dropdown', function() { }); 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'); @@ -688,11 +683,12 @@ describe('uib-dropdown', function() { }); 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');