diff --git a/src/dropdown/dropdown.js b/src/dropdown/dropdown.js
index 6f39ae3dee..52f198dcc4 100644
--- a/src/dropdown/dropdown.js
+++ b/src/dropdown/dropdown.js
@@ -1,6 +1,6 @@
angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
-.constant('dropdownConfig', {
+.constant('uibDropdownConfig', {
openClass: 'open'
})
@@ -65,11 +65,11 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
};
}])
-.controller('UibDropdownController', ['$scope', '$attrs', '$parse', 'dropdownConfig', 'uibDropdownService', '$animate', '$position', '$document', '$compile', '$templateRequest', function($scope, $attrs, $parse, dropdownConfig, uibDropdownService, $animate, $position, $document, $compile, $templateRequest) {
+.controller('UibDropdownController', ['$scope', '$attrs', '$parse', 'uibDropdownConfig', 'uibDropdownService', '$animate', '$uibPosition', '$document', '$compile', '$templateRequest', function($scope, $attrs, $parse, uibDropdownConfig, uibDropdownService, $animate, $uibPosition, $document, $compile, $templateRequest) {
var self = this,
scope = $scope.$new(), // create a child scope so we are not polluting original one
templateScope,
- openClass = dropdownConfig.openClass,
+ openClass = uibDropdownConfig.openClass,
getIsOpen,
setIsOpen = angular.noop,
toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
@@ -168,7 +168,7 @@ 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 pos = $uibPosition.positionElements(self.$element, self.dropdownMenu, 'bottom-left', true);
var css = {
top: pos.top + 'px',
display: isOpen ? 'block' : 'none'
@@ -344,17 +344,21 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
};
});
-/* Depreciated dropdown below */
+
+/* Deprecated dropdown below */
angular.module('ui.bootstrap.dropdown')
.value('$dropdownSuppressWarning', false)
.directive('dropdown', ['$log', '$dropdownSuppressWarning', function($log, $dropdownSuppressWarning) {
return {
+ controller: 'UibDropdownController',
link: function(scope, element, attrs, dropdownCtrl) {
- if (!$dropdownSuppressWarning) {
- $log.warn('dropdown is now deprecated. Use uib-dropdown instead.');
- }
+ if (!$dropdownSuppressWarning) {
+ $log.warn('dropdown is now deprecated. Use uib-dropdown instead.');
}
+ dropdownCtrl.init(element);
+ element.addClass('dropdown');
+ }
};
}])
@@ -363,10 +367,20 @@ angular.module('ui.bootstrap.dropdown')
restrict: 'AC',
require: '?^dropdown',
link: function(scope, element, attrs, dropdownCtrl) {
- if (!$dropdownSuppressWarning) {
- $log.warn('dropdown-menu is now deprecated. Use uib-dropdown-menu instead.');
- }
+ if (!$dropdownSuppressWarning) {
+ $log.warn('dropdown-menu is now deprecated. Use uib-dropdown-menu instead.');
+ }
+ if (!dropdownCtrl) {
+ return;
}
+ var tplUrl = attrs.templateUrl;
+ if (tplUrl) {
+ dropdownCtrl.dropdownMenuTemplateUrl = tplUrl;
+ }
+ if (!dropdownCtrl.dropdownMenu) {
+ dropdownCtrl.dropdownMenu = element;
+ }
+ }
};
}])
@@ -375,10 +389,40 @@ angular.module('ui.bootstrap.dropdown')
restrict: 'A',
require: '?^dropdown',
link: function(scope, element, attrs, dropdownCtrl) {
- if (!$dropdownSuppressWarning) {
- $log.warn('keyboard-nav is now deprecated. Use uib-keyboard-nav instead.');
- }
+ if (!$dropdownSuppressWarning) {
+ $log.warn('keyboard-nav is now deprecated. Use uib-keyboard-nav instead.');
}
+ element.bind('keydown', function(e) {
+ if ([38, 40].indexOf(e.which) !== -1) {
+ e.preventDefault();
+ e.stopPropagation();
+
+ var elems = dropdownCtrl.dropdownMenu.find('a');
+
+ switch (e.which) {
+ case (40): { // Down
+ if (!angular.isNumber(dropdownCtrl.selectedOption)) {
+ dropdownCtrl.selectedOption = 0;
+ } else {
+ dropdownCtrl.selectedOption = dropdownCtrl.selectedOption === elems.length -1 ?
+ dropdownCtrl.selectedOption : dropdownCtrl.selectedOption + 1;
+ }
+ break;
+ }
+ case (38): { // Up
+ if (!angular.isNumber(dropdownCtrl.selectedOption)) {
+ dropdownCtrl.selectedOption = elems.length - 1;
+ } else {
+ dropdownCtrl.selectedOption = dropdownCtrl.selectedOption === 0 ?
+ 0 : dropdownCtrl.selectedOption - 1;
+ }
+ break;
+ }
+ }
+ elems[dropdownCtrl.selectedOption].focus();
+ }
+ });
+ }
};
}])
@@ -386,10 +430,39 @@ angular.module('ui.bootstrap.dropdown')
return {
require: '?^dropdown',
link: function(scope, element, attrs, dropdownCtrl) {
- if (!$dropdownSuppressWarning) {
- $log.warn('dropdown-toggle is now deprecated. Use uib-dropdown-toggle instead.');
+ if (!$dropdownSuppressWarning) {
+ $log.warn('dropdown-toggle is now deprecated. Use uib-dropdown-toggle instead.');
+ }
+ if (!dropdownCtrl) {
+ return;
+ }
+
+ element.addClass('dropdown-toggle');
+
+ dropdownCtrl.toggleElement = element;
+
+ var toggleDropdown = function(event) {
+ event.preventDefault();
+
+ if (!element.hasClass('disabled') && !attrs.disabled) {
+ scope.$apply(function() {
+ dropdownCtrl.toggle();
+ });
}
- }
+ };
+
+ element.bind('click', toggleDropdown);
+
+ // WAI-ARIA
+ element.attr({ 'aria-haspopup': true, 'aria-expanded': false });
+ scope.$watch(dropdownCtrl.isOpen, function(isOpen) {
+ element.attr('aria-expanded', !!isOpen);
+ });
+
+ scope.$on('$destroy', function() {
+ element.unbind('click', toggleDropdown);
+ });
+ }
};
}]);
diff --git a/src/dropdown/test/dropdown.spec.js b/src/dropdown/test/dropdown.spec.js
index 68375f06ca..63868b2eb1 100644
--- a/src/dropdown/test/dropdown.spec.js
+++ b/src/dropdown/test/dropdown.spec.js
@@ -1,16 +1,17 @@
describe('dropdownToggle', function() {
- var $animate, $compile, $rootScope, $document, $templateCache, dropdownConfig, element, $browser, $log;
+ var $animate, $compile, $rootScope, $document, $templateCache, uibDropdownConfig, element, $browser, $log;
beforeEach(module('ngAnimateMock'));
beforeEach(module('ui.bootstrap.dropdown'));
- beforeEach(inject(function(_$animate_, _$compile_, _$rootScope_, _$document_, _$templateCache_, _dropdownConfig_, _$browser_, _$log_) {
+
+ beforeEach(inject(function(_$animate_, _$compile_, _$rootScope_, _$document_, _$templateCache_, _uibDropdownConfig_, _$browser_, _$log_) {
$animate = _$animate_;
$compile = _$compile_;
$rootScope = _$rootScope_;
$document = _$document_;
$templateCache = _$templateCache_;
- dropdownConfig = _dropdownConfig_;
+ uibDropdownConfig = _uibDropdownConfig_;
$browser = _$browser_;
$log = _$log_;
}));
@@ -44,78 +45,78 @@ describe('dropdownToggle', function() {
});
it('should toggle on `a` click', function() {
- expect(element.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(false);
clickDropdownToggle();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(true);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(true);
clickDropdownToggle();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(false);
});
it('should toggle when an option is clicked', function() {
$document.find('body').append(element);
- expect(element.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(false);
clickDropdownToggle();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(true);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(true);
var optionEl = element.find('ul > li').eq(0).find('a').eq(0);
optionEl.click();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(false);
});
it('should close on document click', function() {
clickDropdownToggle();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(true);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(true);
$document.click();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(false);
});
it('should close on escape key & focus toggle element', function() {
$document.find('body').append(element);
clickDropdownToggle();
triggerKeyDown($document, 27);
- expect(element.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(false);
expect(isFocused(element.find('a'))).toBe(true);
});
it('should not close on backspace key', function() {
clickDropdownToggle();
triggerKeyDown($document, 8);
- expect(element.hasClass(dropdownConfig.openClass)).toBe(true);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(true);
});
it('should close on $location change', function() {
clickDropdownToggle();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(true);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(true);
$rootScope.$broadcast('$locationChangeSuccess');
$rootScope.$apply();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(false);
});
it('should only allow one dropdown to be open at once', function() {
var elm1 = dropdown();
var elm2 = dropdown();
- expect(elm1.hasClass(dropdownConfig.openClass)).toBe(false);
- expect(elm2.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(elm1.hasClass(uibDropdownConfig.openClass)).toBe(false);
+ expect(elm2.hasClass(uibDropdownConfig.openClass)).toBe(false);
clickDropdownToggle( elm1 );
- expect(elm1.hasClass(dropdownConfig.openClass)).toBe(true);
- expect(elm2.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(elm1.hasClass(uibDropdownConfig.openClass)).toBe(true);
+ expect(elm2.hasClass(uibDropdownConfig.openClass)).toBe(false);
clickDropdownToggle( elm2 );
- expect(elm1.hasClass(dropdownConfig.openClass)).toBe(false);
- expect(elm2.hasClass(dropdownConfig.openClass)).toBe(true);
+ expect(elm1.hasClass(uibDropdownConfig.openClass)).toBe(false);
+ expect(elm2.hasClass(uibDropdownConfig.openClass)).toBe(true);
});
it('should not toggle if the element has `disabled` class', function() {
var elm = $compile('
Hello
')($rootScope);
clickDropdownToggle( elm );
- expect(elm.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(elm.hasClass(uibDropdownConfig.openClass)).toBe(false);
});
it('should not toggle if the element is disabled', function() {
var elm = $compile('
Hello
')($rootScope);
elm.find('button').click();
- expect(elm.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(elm.hasClass(uibDropdownConfig.openClass)).toBe(false);
});
it('should not toggle if the element has `ng-disabled` as true', function() {
@@ -123,12 +124,12 @@ describe('dropdownToggle', function() {
var elm = $compile('
Hello
')($rootScope);
$rootScope.$digest();
elm.find('div').click();
- expect(elm.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(elm.hasClass(uibDropdownConfig.openClass)).toBe(false);
$rootScope.isdisabled = false;
$rootScope.$digest();
elm.find('div').click();
- expect(elm.hasClass(dropdownConfig.openClass)).toBe(true);
+ expect(elm.hasClass(uibDropdownConfig.openClass)).toBe(true);
});
it('should unbind events on scope destroy', function() {
@@ -138,13 +139,13 @@ describe('dropdownToggle', function() {
var buttonEl = elm.find('button');
buttonEl.click();
- expect(elm.hasClass(dropdownConfig.openClass)).toBe(true);
+ expect(elm.hasClass(uibDropdownConfig.openClass)).toBe(true);
buttonEl.click();
- expect(elm.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(elm.hasClass(uibDropdownConfig.openClass)).toBe(false);
$scope.$destroy();
buttonEl.click();
- expect(elm.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(elm.hasClass(uibDropdownConfig.openClass)).toBe(false);
});
// issue 270
@@ -152,11 +153,11 @@ describe('dropdownToggle', function() {
var checkboxEl = $compile('')($rootScope);
$rootScope.$digest();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(false);
expect($rootScope.clicked).toBeFalsy();
clickDropdownToggle();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(true);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(true);
expect($rootScope.clicked).toBeFalsy();
checkboxEl.click();
@@ -178,13 +179,13 @@ describe('dropdownToggle', function() {
// pr/issue 3274
it('should not raise $digest:inprog if dismissed during a digest cycle', function() {
clickDropdownToggle();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(true);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(true);
$rootScope.$apply(function() {
$document.click();
});
- expect(element.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(false);
});
});
@@ -269,10 +270,10 @@ describe('dropdownToggle', function() {
it('should close without errors on $location change', function() {
$document.find('body').append(element);
clickDropdownToggle();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(true);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(true);
var optionEl = element.find('ul > li').eq(0).find('a').eq(0);
optionEl.click();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(false);
});
});
@@ -284,13 +285,13 @@ describe('dropdownToggle', function() {
});
it('should be open initially', function() {
- expect(element.hasClass(dropdownConfig.openClass)).toBe(true);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(true);
});
it('should toggle when `is-open` changes', function() {
$rootScope.isopen = false;
$rootScope.$digest();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(false);
});
});
@@ -302,7 +303,7 @@ describe('dropdownToggle', function() {
});
it('should be open initially', function() {
- expect(element.hasClass(dropdownConfig.openClass)).toBe(true);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(true);
});
it('should change `is-open` binding when toggles', function() {
@@ -313,7 +314,7 @@ describe('dropdownToggle', function() {
it('should toggle when `is-open` changes', function() {
$rootScope.isopen = false;
$rootScope.$digest();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(false);
});
it('focus toggle element when opening', function() {
@@ -420,47 +421,47 @@ describe('dropdownToggle', function() {
it('should close on document click if no auto-close is specified', function() {
element = dropdown();
clickDropdownToggle();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(true);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(true);
$document.click();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(false);
});
it('should close on document click if empty auto-close is specified', function() {
element = dropdown('');
clickDropdownToggle();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(true);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(true);
$document.click();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(false);
});
it('auto-close="disabled"', function() {
element = dropdown('disabled');
clickDropdownToggle();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(true);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(true);
$document.click();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(true);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(true);
});
describe('outsideClick', function() {
it('should close only on a click outside of the dropdown menu', function() {
element = dropdown('outsideClick');
clickDropdownToggle();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(true);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(true);
element.find('ul li a').click();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(true);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(true);
$document.click();
- expect(element.hasClass(dropdownConfig.openClass)).toBe(false);
+ expect(element.hasClass(uibDropdownConfig.openClass)).toBe(false);
});
it('should work with dropdown-append-to-body', function() {
element = $compile('