Skip to content

Commit

Permalink
feat(dropdown): Make Auto-Close Dropdowns optional.
Browse files Browse the repository at this point in the history
  • Loading branch information
mariocasciaro authored and fernando-sendMail committed Jul 16, 2015
1 parent 384c11a commit 3ff0da0
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 5 deletions.
5 changes: 0 additions & 5 deletions src/dropdown/docs/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<a dropdown-toggle>` 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.

15 changes: 15 additions & 0 deletions src/dropdown/dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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();
Expand Down
88 changes: 88 additions & 0 deletions src/dropdown/test/dropdown.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -324,4 +324,92 @@ describe('dropdownToggle', function() {
expect($rootScope.toggleHandler).toHaveBeenCalledWith(false);
});
});

describe('`auto-close` option', function() {
function dropdown(autoClose) {
return $compile('<li dropdown ' +
(autoClose === void 0 ? '' : 'auto-close="'+autoClose+'"') +
'><a href dropdown-toggle></a><ul><li><a href>Hello</a></li></ul></li>')($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('<li dropdown is-open="isopen" auto-close="disabled"><a href dropdown-toggle></a><ul><li>Hello</li></ul></li>')($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);
});
});
});

0 comments on commit 3ff0da0

Please sign in to comment.