diff --git a/src/dropdown/docs/readme.md b/src/dropdown/docs/readme.md
index 09b646d072..3026673ce2 100644
--- a/src/dropdown/docs/readme.md
+++ b/src/dropdown/docs/readme.md
@@ -25,7 +25,7 @@ Each of these parts need to be used as attribute directives.
* `dropdown-append-to-body`
B
_(Default: `false`)_ -
- Appends the inner dropdown-menu to the body element.
+ Appends the inner dropdown-menu to the body element if the attribute is present without a value, or with a non `false` value.
* `is-open`
$
diff --git a/src/dropdown/dropdown.js b/src/dropdown/dropdown.js
index 7c5f805419..312e880905 100644
--- a/src/dropdown/dropdown.js
+++ b/src/dropdown/dropdown.js
@@ -144,8 +144,6 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.multiMap', 'ui.bootstrap.
getIsOpen,
setIsOpen = angular.noop,
toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
- appendToBody = false,
- appendTo = null,
keynavEnabled = false,
selectedOption = null,
body = $document.find('body');
@@ -162,26 +160,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.multiMap', 'ui.bootstrap.
});
}
- if (angular.isDefined($attrs.dropdownAppendTo)) {
- var appendToEl = $parse($attrs.dropdownAppendTo)(scope);
- if (appendToEl) {
- appendTo = angular.element(appendToEl);
- }
- }
-
- appendToBody = angular.isDefined($attrs.dropdownAppendToBody);
keynavEnabled = angular.isDefined($attrs.keyboardNav);
-
- if (appendToBody && !appendTo) {
- appendTo = body;
- }
-
- if (appendTo && self.dropdownMenu) {
- appendTo.append(self.dropdownMenu);
- $element.on('$destroy', function handleDestroyEvent() {
- self.dropdownMenu.remove();
- });
- }
};
this.toggle = function(open) {
@@ -253,7 +232,42 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.multiMap', 'ui.bootstrap.
}
};
+ function removeDropdownMenu() {
+ $element.append(self.dropdownMenu);
+ }
+
scope.$watch('isOpen', function(isOpen, wasOpen) {
+ var appendTo = null,
+ appendToBody = false;
+
+ if (angular.isDefined($attrs.dropdownAppendTo)) {
+ var appendToEl = $parse($attrs.dropdownAppendTo)(scope);
+ if (appendToEl) {
+ appendTo = angular.element(appendToEl);
+ }
+ }
+
+ if (angular.isDefined($attrs.dropdownAppendToBody)) {
+ var appendToBodyValue = $parse($attrs.dropdownAppendToBody)(scope);
+ if (appendToBodyValue !== false) {
+ appendToBody = true;
+ }
+ }
+
+ if (appendToBody && !appendTo) {
+ appendTo = body;
+ }
+
+ if (appendTo && self.dropdownMenu) {
+ if (isOpen) {
+ appendTo.append(self.dropdownMenu);
+ $element.on('$destroy', removeDropdownMenu);
+ } else {
+ $element.off('$destroy', removeDropdownMenu);
+ removeDropdownMenu();
+ }
+ }
+
if (appendTo && self.dropdownMenu) {
var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true),
css,
diff --git a/src/dropdown/test/dropdown.spec.js b/src/dropdown/test/dropdown.spec.js
index dce7542ba8..17226b5c5e 100644
--- a/src/dropdown/test/dropdown.spec.js
+++ b/src/dropdown/test/dropdown.spec.js
@@ -215,43 +215,198 @@ describe('uib-dropdown', function() {
});
describe('using dropdown-append-to-body', function() {
- function dropdown() {
- return $compile('
')($rootScope);
- }
-
- beforeEach(function() {
- element = dropdown();
- $document.find('body').append(element);
- });
-
- afterEach(function() {
- element.remove();
- });
+ describe('with no value', function() {
+ function dropdown() {
+ return $compile('')($rootScope);
+ }
- it('adds the menu to the body', function() {
- expect($document.find('#dropdown-menu').parent()[0]).toBe($document.find('body')[0]);
- });
+ beforeEach(function() {
+ element = dropdown();
+ $document.find('body').append(element);
+ });
- it('focuses the dropdown element on close', function() {
- var toggle = element.find('[uib-dropdown-toggle]');
- var menu = $document.find('#dropdown-menu a');
- toggle.trigger('click');
- menu.focus();
+ afterEach(function() {
+ element.remove();
+ });
- menu.trigger('click');
+ it('does not add the menu to the body', function() {
+ expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
+ });
- expect(document.activeElement).toBe(toggle[0]);
+ describe('when toggled open', function() {
+ var toggle;
+ beforeEach(function() {
+ toggle = element.find('[uib-dropdown-toggle]');
+ toggle.trigger('click');
+ });
+ it('adds the menu to the body', function() {
+ expect($document.find('#dropdown-menu').parent()[0]).toBe($document.find('body')[0]);
+ });
+
+ describe('when toggled closed', function() {
+ beforeEach(function() {
+ toggle.trigger('click');
+ });
+ it('removes the menu from body', function() {
+ expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
+ });
+ });
+
+ describe('when closed by clicking on menu', function() {
+ var menu;
+ beforeEach(function() {
+ menu = $document.find('#dropdown-menu a');
+ menu.focus();
+ menu.trigger('click');
+ });
+ it('focuses the dropdown element on close', function() {
+ expect(document.activeElement).toBe(toggle[0]);
+ });
+ it('removes the menu from body', function() {
+ expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
+ });
+ });
+ describe('when the dropdown is removed', function() {
+ beforeEach(function() {
+ element.remove();
+ $rootScope.$digest();
+ });
+ it('removes the menu from body', function() {
+ expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
+ });
+ });
+ });
});
- it('removes the menu when the dropdown is removed', function() {
- element.remove();
- $rootScope.$digest();
- expect($document.find('#dropdown-menu').length).toEqual(0);
+ describe('with a value', function() {
+ function dropdown() {
+ return $compile('')($rootScope);
+ }
+ describe('that is not false', function() {
+ beforeEach(function() {
+ $rootScope.appendToBody = 'sure';
+
+ element = dropdown();
+ $document.find('body').append(element);
+ });
+
+ afterEach(function() {
+ element.remove();
+ });
+ it('does not add the menu to the body', function() {
+ expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
+ });
+
+ describe('when toggled open', function() {
+ var toggle;
+ beforeEach(function() {
+ toggle = element.find('[uib-dropdown-toggle]');
+ toggle.trigger('click');
+ });
+ it('adds the menu to the body', function() {
+ expect($document.find('#dropdown-menu').parent()[0]).toBe($document.find('body')[0]);
+ });
+
+ describe('when toggled closed', function() {
+ beforeEach(function() {
+ toggle.trigger('click');
+ });
+ it('removes the menu from body', function() {
+ expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
+ });
+ });
+
+ describe('when closed by clicking on menu', function() {
+ var menu;
+ beforeEach(function() {
+ menu = $document.find('#dropdown-menu a');
+ menu.focus();
+ menu.trigger('click');
+ });
+ it('focuses the dropdown element on close', function() {
+ expect(document.activeElement).toBe(toggle[0]);
+ });
+ it('removes the menu from body', function() {
+ expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
+ });
+ });
+ describe('when the dropdown is removed', function() {
+ beforeEach(function() {
+ element.remove();
+ $rootScope.$digest();
+ });
+ it('removes the menu from body', function() {
+ expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
+ });
+ });
+ });
+ });
+
+ describe('that is false', function() {
+ beforeEach(function() {
+ $rootScope.appendToBody = false;
+
+ element = dropdown();
+ $document.find('body').append(element);
+ });
+
+ afterEach(function() {
+ element.remove();
+ });
+
+ it('does not add the menu to the body', function() {
+ expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
+ });
+
+ describe('when toggled open', function() {
+ var toggle;
+ beforeEach(function() {
+ toggle = element.find('[uib-dropdown-toggle]');
+ toggle.trigger('click');
+ });
+ it('does not add the menu to the body', function() {
+ expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
+ });
+
+ describe('when toggled closed', function() {
+ beforeEach(function() {
+ toggle.trigger('click');
+ });
+ it('does not remove the menu', function() {
+ expect($document.find('#dropdown-menu').length).not.toEqual(0);
+ });
+ });
+
+ describe('when closed by clicking on menu', function() {
+ var menu;
+ beforeEach(function() {
+ menu = $document.find('#dropdown-menu a');
+ menu.focus();
+ menu.trigger('click');
+ });
+ it('focuses the dropdown element on close', function() {
+ expect(document.activeElement).toBe(toggle[0]);
+ });
+ it('does not removes the menu from body', function() {
+ expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
+ });
+ });
+ describe('when the dropdown is removed', function() {
+ beforeEach(function() {
+ element.remove();
+ $rootScope.$digest();
+ });
+ it('removes the menu from body', function() {
+ expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
+ });
+ });
+ });
+ });
});
});
describe('using dropdown-append-to', function() {
- var initialPage;
+ var initialPage, container;
function dropdown() {
return $compile('')($rootScope);
@@ -260,7 +415,7 @@ describe('uib-dropdown', function() {
beforeEach(function() {
$document.find('body').append(angular.element(''));
- $rootScope.appendTo = $document.find('#dropdown-container');
+ $rootScope.appendTo = container = $document.find('#dropdown-container');
element = dropdown();
$document.find('body').append(element);
@@ -271,35 +426,64 @@ describe('uib-dropdown', function() {
$document.find('#dropdown-container').remove();
});
- it('appends to container', function() {
- expect($document.find('#dropdown-menu').parent()[0].id).toBe('dropdown-container');
+ it('does not add the menu to the container', function() {
+ expect($document.find('#dropdown-menu').parent()[0]).not.toBe(container[0]);
});
-
- it('toggles open class on container', function() {
- var container = $document.find('#dropdown-container');
-
- expect(container).not.toHaveClass('uib-dropdown-open');
- element.find('[uib-dropdown-toggle]').click();
- expect(container).toHaveClass('uib-dropdown-open');
- element.find('[uib-dropdown-toggle]').click();
+ it('does not add open class on container', function() {
expect(container).not.toHaveClass('uib-dropdown-open');
});
- it('focuses the dropdown element on close', function() {
- var toggle = element.find('[uib-dropdown-toggle]');
- var menu = $document.find('#dropdown-menu a');
- toggle.trigger('click');
- menu.focus();
-
- menu.trigger('click');
+ describe('when toggled open', function() {
+ var toggle;
+ beforeEach(function() {
+ toggle = element.find('[uib-dropdown-toggle]');
+ toggle.trigger('click');
+ });
+ it('adds the menu to the container', function() {
+ expect($document.find('#dropdown-menu').parent()[0]).toBe(container[0]);
+ });
+ it('adds open class on container', function() {
+ expect(container).toHaveClass('uib-dropdown-open');
+ });
- expect(document.activeElement).toBe(toggle[0]);
- });
+ describe('when toggled closed', function() {
+ beforeEach(function() {
+ toggle.trigger('click');
+ });
+ it('removes the menu from the container', function() {
+ expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
+ });
+ it('removes open class from container', function() {
+ expect(container).not.toHaveClass('uib-dropdown-open');
+ });
+ });
- it('removes the menu when the dropdown is removed', function() {
- element.remove();
- $rootScope.$digest();
- expect($document.find('#dropdown-menu').length).toEqual(0);
+ describe('when closed by clicking on menu', function() {
+ var menu;
+ beforeEach(function() {
+ menu = $document.find('#dropdown-menu a');
+ menu.focus();
+ menu.trigger('click');
+ });
+ it('focuses the dropdown element on close', function() {
+ expect(document.activeElement).toBe(toggle[0]);
+ });
+ it('removes the menu from the container', function() {
+ expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
+ });
+ it('removes open class from container', function() {
+ expect(container).not.toHaveClass('uib-dropdown-open');
+ });
+ });
+ describe('when the dropdown is removed', function() {
+ beforeEach(function() {
+ element.remove();
+ $rootScope.$digest();
+ });
+ it('removes the menu from the container', function() {
+ expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
+ });
+ });
});
});