Skip to content

Commit c489c6d

Browse files
mariocasciarofernando-sendMail
authored andcommitted
feat(dropdown): Make Auto-Close Dropdowns optional.
Fixes angular-ui#2218 Closes angular-ui#3045
1 parent d8b0601 commit c489c6d

File tree

3 files changed

+103
-5
lines changed

3 files changed

+103
-5
lines changed

src/dropdown/docs/readme.md

-5
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,8 @@
22
Dropdown is a simple directive which will toggle a dropdown menu on click or programmatically.
33
You can either use `is-open` to toggle or add inside a `<a dropdown-toggle>` element to toggle it when is clicked.
44
There is also the `on-toggle(open)` optional expression fired when dropdown changes state.
5-
6-
Add `dropdown-append-to-body` to the `dropdown` element to append to the inner `dropdown-menu` to the body.
7-
This is useful when the dropdown button is inside a div with `overflow: hidden`, and the menu would otherwise be hidden.
8-
95
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:
106

117
* `always` - (Default) automatically closes the dropdown when any of its elements is clicked.
128
* `outsideClick` - closes the dropdown automatically only when the user clicks any element outside the dropdown.
139
* `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.
14-

src/dropdown/dropdown.js

+15
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,18 @@ angular.module('ui.bootstrap.dropdown', [])
3333
// unbound this event handler. So check openScope before proceeding.
3434
if (!openScope) { return; }
3535

36+
if( evt && openScope.getAutoClose() === 'disabled' ) { return ; }
37+
3638
var toggleElement = openScope.getToggleElement();
3739
if ( evt && toggleElement && toggleElement[0].contains(evt.target) ) {
3840
return;
3941
}
4042

43+
var $element = openScope.getElement();
44+
if( evt && openScope.getAutoClose() === 'outsideClick' && $element && $element[0].contains(evt.target) ) {
45+
return;
46+
}
47+
4148
openScope.isOpen = false;
4249

4350
if (!$rootScope.$$phase) {
@@ -87,6 +94,14 @@ angular.module('ui.bootstrap.dropdown', [])
8794
return self.toggleElement;
8895
};
8996

97+
scope.getAutoClose = function() {
98+
return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled'
99+
};
100+
101+
scope.getElement = function() {
102+
return self.$element;
103+
};
104+
90105
scope.focusToggleElement = function() {
91106
if ( self.toggleElement ) {
92107
self.toggleElement[0].focus();

src/dropdown/test/dropdown.spec.js

+88
Original file line numberDiff line numberDiff line change
@@ -324,4 +324,92 @@ describe('dropdownToggle', function() {
324324
expect($rootScope.toggleHandler).toHaveBeenCalledWith(false);
325325
});
326326
});
327+
328+
describe('`auto-close` option', function() {
329+
function dropdown(autoClose) {
330+
return $compile('<li dropdown ' +
331+
(autoClose === void 0 ? '' : 'auto-close="'+autoClose+'"') +
332+
'><a href dropdown-toggle></a><ul><li><a href>Hello</a></li></ul></li>')($rootScope);
333+
}
334+
335+
it('should close on document click if no auto-close is specified', function() {
336+
element = dropdown();
337+
clickDropdownToggle();
338+
expect(element.hasClass('open')).toBe(true);
339+
$document.click();
340+
expect(element.hasClass('open')).toBe(false);
341+
});
342+
343+
it('should close on document click if empty auto-close is specified', function() {
344+
element = dropdown('');
345+
clickDropdownToggle();
346+
expect(element.hasClass('open')).toBe(true);
347+
$document.click();
348+
expect(element.hasClass('open')).toBe(false);
349+
});
350+
351+
it('auto-close="disabled"', function() {
352+
element = dropdown('disabled');
353+
clickDropdownToggle();
354+
expect(element.hasClass('open')).toBe(true);
355+
$document.click();
356+
expect(element.hasClass('open')).toBe(true);
357+
});
358+
359+
it('auto-close="outsideClick"', function() {
360+
element = dropdown('outsideClick');
361+
clickDropdownToggle();
362+
expect(element.hasClass('open')).toBe(true);
363+
element.find('ul li a').click();
364+
expect(element.hasClass('open')).toBe(true);
365+
$document.click();
366+
expect(element.hasClass('open')).toBe(false);
367+
});
368+
369+
it('control with is-open', function() {
370+
$rootScope.isopen = true;
371+
element = $compile('<li dropdown is-open="isopen" auto-close="disabled"><a href dropdown-toggle></a><ul><li>Hello</li></ul></li>')($rootScope);
372+
$rootScope.$digest();
373+
374+
expect(element.hasClass('open')).toBe(true);
375+
//should remain open
376+
$document.click();
377+
expect(element.hasClass('open')).toBe(true);
378+
//now should close
379+
$rootScope.isopen = false;
380+
$rootScope.$digest();
381+
expect(element.hasClass('open')).toBe(false);
382+
});
383+
384+
it('should close anyway if toggle is clicked', function() {
385+
element = dropdown('disabled');
386+
clickDropdownToggle();
387+
expect(element.hasClass('open')).toBe(true);
388+
clickDropdownToggle();
389+
expect(element.hasClass('open')).toBe(false);
390+
});
391+
392+
it('should close anyway if esc is pressed', function() {
393+
element = dropdown('disabled');
394+
$document.find('body').append(element);
395+
clickDropdownToggle();
396+
triggerKeyDown($document, 27);
397+
expect(element.hasClass('open')).toBe(false);
398+
expect(isFocused(element.find('a'))).toBe(true);
399+
element.remove();
400+
});
401+
402+
it('should close anyway if another dropdown is opened', function() {
403+
var elm1 = dropdown('disabled');
404+
var elm2 = dropdown();
405+
expect(elm1.hasClass('open')).toBe(false);
406+
expect(elm2.hasClass('open')).toBe(false);
407+
clickDropdownToggle(elm1);
408+
expect(elm1.hasClass('open')).toBe(true);
409+
expect(elm2.hasClass('open')).toBe(false);
410+
clickDropdownToggle(elm2);
411+
expect(elm1.hasClass('open')).toBe(false);
412+
expect(elm2.hasClass('open')).toBe(true);
413+
});
414+
});
327415
});

0 commit comments

Comments
 (0)