From 3ff0da00bd731938c13333553ff44b94614142be Mon Sep 17 00:00:00 2001 From: Mario Casciaro Date: Mon, 30 Mar 2015 12:20:13 +0100 Subject: [PATCH] feat(dropdown): Make Auto-Close Dropdowns optional. Fixes #2218 Closes #3045 --- src/dropdown/docs/readme.md | 5 -- src/dropdown/dropdown.js | 15 +++++ src/dropdown/test/dropdown.spec.js | 88 ++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 5 deletions(-) diff --git a/src/dropdown/docs/readme.md b/src/dropdown/docs/readme.md index 02970d0a5f..5f42e0e829 100644 --- a/src/dropdown/docs/readme.md +++ b/src/dropdown/docs/readme.md @@ -2,13 +2,8 @@ Dropdown is a simple directive which will toggle a dropdown menu on click or programmatically. You can either use `is-open` to toggle or add inside a `` element to toggle it when is clicked. There is also the `on-toggle(open)` optional expression fired when dropdown changes state. - -Add `dropdown-append-to-body` to the `dropdown` element to append to the inner `dropdown-menu` to the body. -This is useful when the dropdown button is inside a div with `overflow: hidden`, and the menu would otherwise be hidden. - By default the dropdown will automatically close if any of its elements is clicked, you can change this behavior by setting the `auto-close` option as follows: * `always` - (Default) automatically closes the dropdown when any of its elements is clicked. * `outsideClick` - closes the dropdown automatically only when the user clicks any element outside the dropdown. * `disabled` - disables the auto close. You can then control the open/close status of the dropdown manually, by using `is-open`. Please notice that the dropdown will still close if the toggle is clicked, the `esc` key is pressed or another dropdown is open. - diff --git a/src/dropdown/dropdown.js b/src/dropdown/dropdown.js index 14c255a13b..b469b0bec5 100644 --- a/src/dropdown/dropdown.js +++ b/src/dropdown/dropdown.js @@ -33,11 +33,18 @@ angular.module('ui.bootstrap.dropdown', []) // unbound this event handler. So check openScope before proceeding. if (!openScope) { return; } + if( evt && openScope.getAutoClose() === 'disabled' ) { return ; } + var toggleElement = openScope.getToggleElement(); if ( evt && toggleElement && toggleElement[0].contains(evt.target) ) { return; } + var $element = openScope.getElement(); + if( evt && openScope.getAutoClose() === 'outsideClick' && $element && $element[0].contains(evt.target) ) { + return; + } + openScope.isOpen = false; if (!$rootScope.$$phase) { @@ -87,6 +94,14 @@ angular.module('ui.bootstrap.dropdown', []) return self.toggleElement; }; + scope.getAutoClose = function() { + return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled' + }; + + scope.getElement = function() { + return self.$element; + }; + scope.focusToggleElement = function() { if ( self.toggleElement ) { self.toggleElement[0].focus(); diff --git a/src/dropdown/test/dropdown.spec.js b/src/dropdown/test/dropdown.spec.js index c1c46a85ee..5e7c7dab9e 100644 --- a/src/dropdown/test/dropdown.spec.js +++ b/src/dropdown/test/dropdown.spec.js @@ -324,4 +324,92 @@ describe('dropdownToggle', function() { expect($rootScope.toggleHandler).toHaveBeenCalledWith(false); }); }); + + describe('`auto-close` option', function() { + function dropdown(autoClose) { + return $compile('
  • ')($rootScope); + } + + it('should close on document click if no auto-close is specified', function() { + element = dropdown(); + clickDropdownToggle(); + expect(element.hasClass('open')).toBe(true); + $document.click(); + expect(element.hasClass('open')).toBe(false); + }); + + it('should close on document click if empty auto-close is specified', function() { + element = dropdown(''); + clickDropdownToggle(); + expect(element.hasClass('open')).toBe(true); + $document.click(); + expect(element.hasClass('open')).toBe(false); + }); + + it('auto-close="disabled"', function() { + element = dropdown('disabled'); + clickDropdownToggle(); + expect(element.hasClass('open')).toBe(true); + $document.click(); + expect(element.hasClass('open')).toBe(true); + }); + + it('auto-close="outsideClick"', function() { + element = dropdown('outsideClick'); + clickDropdownToggle(); + expect(element.hasClass('open')).toBe(true); + element.find('ul li a').click(); + expect(element.hasClass('open')).toBe(true); + $document.click(); + expect(element.hasClass('open')).toBe(false); + }); + + it('control with is-open', function() { + $rootScope.isopen = true; + element = $compile('
  • ')($rootScope); + $rootScope.$digest(); + + expect(element.hasClass('open')).toBe(true); + //should remain open + $document.click(); + expect(element.hasClass('open')).toBe(true); + //now should close + $rootScope.isopen = false; + $rootScope.$digest(); + expect(element.hasClass('open')).toBe(false); + }); + + it('should close anyway if toggle is clicked', function() { + element = dropdown('disabled'); + clickDropdownToggle(); + expect(element.hasClass('open')).toBe(true); + clickDropdownToggle(); + expect(element.hasClass('open')).toBe(false); + }); + + it('should close anyway if esc is pressed', function() { + element = dropdown('disabled'); + $document.find('body').append(element); + clickDropdownToggle(); + triggerKeyDown($document, 27); + expect(element.hasClass('open')).toBe(false); + expect(isFocused(element.find('a'))).toBe(true); + element.remove(); + }); + + it('should close anyway if another dropdown is opened', function() { + var elm1 = dropdown('disabled'); + var elm2 = dropdown(); + expect(elm1.hasClass('open')).toBe(false); + expect(elm2.hasClass('open')).toBe(false); + clickDropdownToggle(elm1); + expect(elm1.hasClass('open')).toBe(true); + expect(elm2.hasClass('open')).toBe(false); + clickDropdownToggle(elm2); + expect(elm1.hasClass('open')).toBe(false); + expect(elm2.hasClass('open')).toBe(true); + }); + }); });