From 43c01669725310261c4703453f900610b53635ad Mon Sep 17 00:00:00 2001 From: Jay Klehr Date: Mon, 28 Sep 2015 13:52:45 -0600 Subject: [PATCH 1/2] feat(dropdown): adding appendTo to with tests --- src/dropdown/dropdown.js | 45 +++++++++++++++++++++----- src/dropdown/test/dropdown.spec.js | 51 ++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 8 deletions(-) diff --git a/src/dropdown/dropdown.js b/src/dropdown/dropdown.js index 44acbb4362..329da73994 100644 --- a/src/dropdown/dropdown.js +++ b/src/dropdown/dropdown.js @@ -74,6 +74,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position']) setIsOpen = angular.noop, toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop, appendToBody = false, + appendTo = null, keynavEnabled = false, selectedOption = null, body = $document.find('body'); @@ -90,12 +91,23 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position']) }); } + 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 && self.dropdownMenu) { - body.append(self.dropdownMenu); - body.addClass('dropdown'); + if (appendToBody && !appendTo) { + appendTo = body; + } + + if (appendTo && self.dropdownMenu) { + appendTo.append(self.dropdownMenu); + appendTo.addClass('dropdown'); element.on('$destroy', function handleDestroyEvent() { self.dropdownMenu.remove(); }); @@ -167,14 +179,17 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position']) }; scope.$watch('isOpen', function(isOpen, wasOpen) { - if (appendToBody && self.dropdownMenu) { - var pos = $position.positionElements(self.$element, self.dropdownMenu, 'bottom-left', true); - var css = { + if (appendTo && self.dropdownMenu) { + var pos = $position.positionElements(self.$element, self.dropdownMenu, 'bottom-left', true), + css, + rightalign; + + css = { top: pos.top + 'px', display: isOpen ? 'block' : 'none' }; - var rightalign = self.dropdownMenu.hasClass('dropdown-menu-right'); + rightalign = self.dropdownMenu.hasClass('dropdown-menu-right'); if (!rightalign) { css.left = pos.left + 'px'; css.right = 'auto'; @@ -183,10 +198,24 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position']) css.right = (window.innerWidth - (pos.left + self.$element.prop('offsetWidth'))) + 'px'; } + // Need to adjust our positioning to be relative to the appendTo container + // if it's not the body element + if (!appendToBody) { + var appendOffset = $position.offset(appendTo); + + css.top = pos.top - appendOffset.top + 'px'; + + if (!rightalign) { + css.left = pos.left - appendOffset.left + 'px'; + } else { + css.right = (window.innerWidth - (pos.left - appendOffset.left + self.$element.prop('offsetWidth'))) + 'px'; + } + } + self.dropdownMenu.css(css); } - var openContainer = appendToBody ? body : self.$element; + var openContainer = appendTo ? appendTo : self.$element; $animate[isOpen ? 'addClass' : 'removeClass'](openContainer, openClass).then(function() { if (angular.isDefined(isOpen) && isOpen !== wasOpen) { diff --git a/src/dropdown/test/dropdown.spec.js b/src/dropdown/test/dropdown.spec.js index 92f883fe5f..3120752638 100644 --- a/src/dropdown/test/dropdown.spec.js +++ b/src/dropdown/test/dropdown.spec.js @@ -220,6 +220,10 @@ describe('dropdownToggle', function() { element = dropdown(); }); + afterEach(function() { + $document.find('body').removeClass('dropdown'); + }); + it('adds the menu to the body', function() { expect($document.find('#dropdown-menu').parent()[0]).toBe($document.find('body')[0]); }); @@ -249,6 +253,53 @@ describe('dropdownToggle', function() { }); }); + describe('using dropdown-append-to', function() { + var initialPage; + + function dropdown() { + return $compile('
  • ')($rootScope); + } + + beforeEach(function() { + $document.find('body').append(angular.element('')); + + $rootScope.appendTo = $document.find('#dropdown-container'); + $rootScope.$digest(); + + element = dropdown(); + $document.find('body').append(element); + }); + + afterEach(function() { + // Cleanup the extra elements we appended + $document.find('#dropdown-container').remove(); + }); + + it('appends to container', function() { + expect($document.find('#dropdown-menu').parent()[0].id).toBe('dropdown-container'); + }); + + it('adds dropdown class to container', function() { + expect($document.find('#dropdown-container').hasClass('dropdown')).toBe(true); + }); + + it('toggles open class on container', function() { + var container = $document.find('#dropdown-container'); + + expect(container.hasClass('open')).toBe(false); + element.find('[dropdown-toggle]').click(); + expect(container.hasClass('open')).toBe(true); + element.find('[dropdown-toggle]').click(); + expect(container.hasClass('open')).toBe(false); + }); + + it('removes the menu when the dropdown is removed', function() { + element.remove(); + $rootScope.$digest(); + expect($document.find('#dropdown-menu').length).toEqual(0); + }); + }); + describe('integration with $location URL rewriting', function() { function dropdown() { // Simulate URL rewriting behavior From 3678181117075f76bc9449c19b5022e9c1ff4527 Mon Sep 17 00:00:00 2001 From: Jay Klehr Date: Mon, 28 Sep 2015 15:59:55 -0600 Subject: [PATCH 2/2] docs(dropdown): Adding docs and demo for appendTo feature --- src/dropdown/docs/demo.html | 48 ++++++++++++++++++++++++++++++++++++- src/dropdown/docs/demo.js | 2 ++ src/dropdown/docs/readme.md | 2 ++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/dropdown/docs/demo.html b/src/dropdown/docs/demo.html index 481cb75252..947e0107e6 100644 --- a/src/dropdown/docs/demo.html +++ b/src/dropdown/docs/demo.html @@ -55,7 +55,7 @@
  • Separated link
  • - +
    +
    + +

    append-to vs. append-to-body vs. inline example

    + +