Compare commits

...

15 Commits

Author SHA1 Message Date
Wesley Cho a4d7076c8e chore(dropdown): fix accidental deletion 2016-11-29 23:25:21 -08:00
Wesley Cho c824731ae8 revert(dropdown): undo addition of unbind 2016-11-29 23:19:43 -08:00
Wesley Cho 1653afa210 refactor(dropdown): use container for class toggle detection 2016-11-29 01:47:23 -08:00
Wesley Cho 7e2f2c1bad chore(datepickerPopup): change to on/off 2016-11-29 00:19:19 -08:00
Wesley Cho 61f365abfd chore(timepicker): change to on/off 2016-11-29 00:18:36 -08:00
Wesley Cho ec2d9ad605 chore(typeahead): change to on/off 2016-11-29 00:17:13 -08:00
Wesley Cho 08b50ccb1c chore(dropdown): change to on/off 2016-11-29 00:14:33 -08:00
patricksmms 44ab0a8106 fix(dropDown): remove dropdown on close
- Unbind keybind on close

Closes #6326
Fixes #6314
2016-11-29 00:09:08 -08:00
Wesley Cho 1962485504 fix(datepickerPopup): change to toTimezone
- Change conversion to use `toTimezone`, as the date is going from UI date with timezone applied to timezone set by `ngModelOptions`

Fixes #6235
2016-11-27 02:26:52 -08:00
Jonathan Perez 8a4f625ef6 fix(modal): revert focus behavior on open
- On open, do not focus the first focusable element

Closes #6295
2016-11-27 02:18:17 -08:00
Wesley Cho 7be665399f chore(release): starting 2.3.1 2016-11-26 07:43:50 -08:00
Wesley Cho 2edb5d38cb chore(release): update changelog 2016-11-26 07:41:20 -08:00
Wesley Cho 4e06553f7c chore(release): 2.3.0 2016-11-26 07:38:21 -08:00
Janne 25ff206767 feat(dateparser): add LLLL support
- Add LLLL support. This requires Angular 1.5+ to take advantage of this.

Closes #6281
2016-11-26 07:35:37 -08:00
Wesley Cho 997813f0eb chore(release): starting 2.2.1 2016-10-10 08:30:03 -07:00
18 changed files with 215 additions and 89 deletions
+10
View File
@@ -1,3 +1,13 @@
<a name="2.3.0"></a>
# [2.3.0](https://github.com/angular-ui/bootstrap/compare/2.2.0...v2.3.0) (2016-11-26)
### Features
* **dateparser:** add LLLL support([25ff206](https://github.com/angular-ui/bootstrap/commit/25ff206)), closes [#6281](https://github.com/angular-ui/bootstrap/issues/6281)
<a name="2.2.0"></a>
# [2.2.0](https://github.com/angular-ui/bootstrap/compare/2.1.4...v2.2.0) (2016-10-10)
+1 -1
View File
@@ -1,7 +1,7 @@
{
"author": "https://github.com/angular-ui/bootstrap/graphs/contributors",
"name": "angular-ui-bootstrap",
"version": "2.2.0",
"version": "2.3.1-SNAPSHOT",
"homepage": "http://angular-ui.github.io/bootstrap/",
"dependencies": {},
"directories": {
+6
View File
@@ -81,6 +81,12 @@ angular.module('ui.bootstrap.dateparser', [])
apply: function(value) { this.month = value - 1; },
formatter: function(date) { return dateFilter(date, 'M'); }
},
{
key: 'LLLL',
regex: $locale.DATETIME_FORMATS.STANDALONEMONTH.join('|'),
apply: function(value) { this.month = $locale.DATETIME_FORMATS.STANDALONEMONTH.indexOf(value); },
formatter: function(date) { return dateFilter(date, 'LLLL'); }
},
{
key: 'd!',
regex: '[0-2]?[0-9]{1}|3[0-1]{1}',
+3
View File
@@ -58,6 +58,9 @@ Certain format codes support i18n. Check this [guide](https://docs.angularjs.org
_(Example: `3` or `03`)_ -
Parses a numeric month, but allowing an optional leading zero
* `LLLL`
_(Example: `February`, i18n support)_ - Stand-alone month in year (January-December). Requires Angular version 1.5.1 or higher.
* `dd`
_(Example: `05`, Leading 0)_ -
Parses a numeric day.
+10
View File
@@ -84,6 +84,16 @@ describe('date parser', function() {
expectFilter(new Date(2011, 4, 2, 0), 'dd-M!-yy', '02-05-11');
expectFilter(oldDate, 'yyyy/M!/dd', '0001/03/06');
});
it('should work correctly for `LLLL`', function() {
expectFilter(new Date(2013, 7, 24, 0), 'LLLL/dd/yyyy', 'August/24/2013');
expectFilter(new Date(2004, 10, 7, 0), 'dd.LLLL.yy', '07.November.04');
expectFilter(new Date(2011, 4, 18, 0), 'dd-LLLL-yy', '18-May-11');
expectFilter(new Date(1980, 1, 5, 0), 'LLLL/dd/yyyy', 'February/05/1980');
expectFilter(new Date(1955, 2, 5, 0), 'yyyy/LLLL/dd', '1955/March/05');
expectFilter(new Date(2011, 5, 2, 0), 'dd-LLLL-yy', '02-June-11');
expectFilter(oldDate, 'yyyy/LLLL/dd', '0001/March/06');
});
it('should work correctly for `d`', function() {
expectFilter(new Date(2013, 10, 17, 0), 'd.MMMM.yy', '17.November.13');
+1 -1
View File
@@ -333,7 +333,7 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
if (angular.isString(viewValue)) {
var date = parseDateString(viewValue);
if (!isNaN(date)) {
return dateParser.fromTimezone(date, ngModelOptions.timezone);
return dateParser.toTimezone(date, ngModelOptions.timezone);
}
}
+2 -2
View File
@@ -397,7 +397,7 @@ describe('datepicker popup', function() {
it('stops the ESC key from propagating if the dropdown is open, but not when closed', function() {
var documentKey = -1;
var getKey = function(evt) { documentKey = evt.which; };
$document.bind('keydown', getKey);
$document.on('keydown', getKey);
triggerKeyDown(inputEl, 'esc');
expect(documentKey).toBe(-1);
@@ -405,7 +405,7 @@ describe('datepicker popup', function() {
triggerKeyDown(inputEl, 'esc');
expect(documentKey).toBe(27);
$document.unbind('keydown', getKey);
$document.off('keydown', getKey);
});
});
+74 -10
View File
@@ -1,14 +1,33 @@
angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.multiMap', 'ui.bootstrap.position'])
.constant('uibDropdownConfig', {
appendToOpenClass: 'uib-dropdown-open',
openClass: 'open'
})
.service('uibDropdownService', ['$document', '$rootScope', function($document, $rootScope) {
.service('uibDropdownService', ['$document', '$rootScope', '$$multiMap', function($document, $rootScope, $$multiMap) {
var openScope = null;
var openedContainers = $$multiMap.createNew();
this.open = function(dropdownScope, element) {
this.isOnlyOpen = function(dropdownScope, appendTo) {
var openedDropdowns = openedContainers.get(appendTo);
if (openedDropdowns) {
var openDropdown = openedDropdowns.reduce(function(toClose, dropdown) {
if (dropdown.scope === dropdownScope) {
return dropdown;
}
return toClose;
}, {});
if (openDropdown) {
return openedDropdowns.length === 1;
}
}
return false;
};
this.open = function(dropdownScope, element, appendTo) {
if (!openScope) {
$document.on('click', closeDropdown);
}
@@ -18,14 +37,52 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
}
openScope = dropdownScope;
if (!appendTo) {
return;
}
var openedDropdowns = openedContainers.get(appendTo);
if (openedDropdowns) {
var openedScopes = openedDropdowns.map(function(dropdown) {
return dropdown.scope;
});
if (openedScopes.indexOf(dropdownScope) === -1) {
openedContainers.put(appendTo, {
scope: dropdownScope
});
}
} else {
openedContainers.put(appendTo, {
scope: dropdownScope
});
}
};
this.close = function(dropdownScope, element) {
this.close = function(dropdownScope, element, appendTo) {
if (openScope === dropdownScope) {
$document.off('click', closeDropdown);
$document.off('keydown', this.keybindFilter);
openScope = null;
}
if (!appendTo) {
return;
}
var openedDropdowns = openedContainers.get(appendTo);
if (openedDropdowns) {
var dropdownToClose = openedDropdowns.reduce(function(toClose, dropdown) {
if (dropdown.scope === dropdownScope) {
return dropdown;
}
return toClose;
}, {});
if (dropdownToClose) {
openedContainers.remove(appendTo, dropdownToClose);
}
}
};
var closeDropdown = function(evt) {
@@ -244,10 +301,18 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
}
var openContainer = appendTo ? appendTo : $element;
var hasOpenClass = openContainer.hasClass(appendTo ? appendToOpenClass : openClass);
var dropdownOpenClass = appendTo ? appendToOpenClass : openClass;
var hasOpenClass = openContainer.hasClass(dropdownOpenClass);
var isOnlyOpen = uibDropdownService.isOnlyOpen($scope, appendTo);
if (hasOpenClass === !isOpen) {
$animate[isOpen ? 'addClass' : 'removeClass'](openContainer, appendTo ? appendToOpenClass : openClass).then(function() {
var toggleClass;
if (appendTo) {
toggleClass = !isOnlyOpen ? 'addClass' : 'removeClass';
} else {
toggleClass = isOpen ? 'addClass' : 'removeClass';
}
$animate[toggleClass](openContainer, dropdownOpenClass).then(function() {
if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
toggleInvoker($scope, { open: !!isOpen });
}
@@ -270,9 +335,8 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
}
scope.focusToggleElement();
uibDropdownService.open(scope, $element);
uibDropdownService.open(scope, $element, appendTo);
} else {
uibDropdownService.close(scope, $element);
if (self.dropdownMenuTemplateUrl) {
if (templateScope) {
templateScope.$destroy();
@@ -345,7 +409,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
}
};
element.bind('click', toggleDropdown);
element.on('click', toggleDropdown);
// WAI-ARIA
element.attr({ 'aria-haspopup': true, 'aria-expanded': false });
@@ -354,7 +418,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
});
scope.$on('$destroy', function() {
element.unbind('click', toggleDropdown);
element.off('click', toggleDropdown);
});
}
};
+1
View File
@@ -1,3 +1,4 @@
require('../multiMap');
require('../position/index-nocss.js');
require('./dropdown');
+33 -2
View File
@@ -99,11 +99,11 @@ describe('uib-dropdown', function() {
expect(elm1).not.toHaveClass(dropdownConfig.openClass);
expect(elm2).not.toHaveClass(dropdownConfig.openClass);
clickDropdownToggle( elm1 );
clickDropdownToggle(elm1);
expect(elm1).toHaveClass(dropdownConfig.openClass);
expect(elm2).not.toHaveClass(dropdownConfig.openClass);
clickDropdownToggle( elm2 );
clickDropdownToggle(elm2);
expect(elm1).not.toHaveClass(dropdownConfig.openClass);
expect(elm2).toHaveClass(dropdownConfig.openClass);
});
@@ -303,6 +303,37 @@ describe('uib-dropdown', function() {
});
});
describe('using dropdown-append-to with two dropdowns', function() {
function dropdown() {
return $compile('<div><div class="dropdown1" uib-dropdown dropdown-append-to="appendTo" on-toggle="log(1, open)"><a href uib-dropdown-toggle></a><ul class="dropdown-menu" uib-dropdown-menu id="dropdown-menu"><li><a href>Hello On Container</a></li></ul></div><div class="dropdown2" uib-dropdown dropdown-append-to="appendTo" on-toggle="log(2, open)"><a href uib-dropdown-toggle></a><ul class="dropdown-menu" uib-dropdown-menu id="dropdown-menu"><li><a href>Hello On Container</a></li></ul></div></div>')($rootScope);
}
beforeEach(function() {
$document.find('body').append(angular.element('<div id="dropdown-container"></div>'));
$rootScope.appendTo = $document.find('#dropdown-container');
$rootScope.log = jasmine.createSpy('log');
element = dropdown();
$document.find('body').append(element);
});
afterEach(function() {
// Cleanup the extra elements we appended
$document.find('#dropdown-container').remove();
});
it('should keep the class when toggling from one dropdown to another with the same container', function() {
var container = $document.find('#dropdown-container');
expect(container).not.toHaveClass('uib-dropdown-open');
element.find('.dropdown1 [uib-dropdown-toggle]').click();
expect(container).toHaveClass('uib-dropdown-open');
element.find('.dropdown2 [uib-dropdown-toggle]').click();
expect(container).toHaveClass('uib-dropdown-open');
});
});
describe('using is-open', function() {
describe('with uib-dropdown-toggle', function() {
beforeEach(function() {
+1
View File
@@ -1,3 +1,4 @@
require('../multiMap');
require('../position/index-nocss.js');
require('../stackedMap');
require('../../template/modal/window.html.js');
+3 -59
View File
@@ -1,59 +1,4 @@
angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.position'])
/**
* A helper, internal data structure that stores all references attached to key
*/
.factory('$$multiMap', function() {
return {
createNew: function() {
var map = {};
return {
entries: function() {
return Object.keys(map).map(function(key) {
return {
key: key,
value: map[key]
};
});
},
get: function(key) {
return map[key];
},
hasKey: function(key) {
return !!map[key];
},
keys: function() {
return Object.keys(map);
},
put: function(key, value) {
if (!map[key]) {
map[key] = [];
}
map[key].push(value);
},
remove: function(key, value) {
var values = map[key];
if (!values) {
return;
}
var idx = values.indexOf(value);
if (idx !== -1) {
values.splice(idx, 1);
}
if (!values.length) {
delete map[key];
}
}
};
}
};
})
angular.module('ui.bootstrap.modal', ['ui.bootstrap.multiMap', 'ui.bootstrap.stackedMap', 'ui.bootstrap.position'])
/**
* Pluggable resolve mechanism for the modal resolve resolution
* Supports UI Router's $resolve service
@@ -569,7 +514,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
ariaHiddenCount = parseInt(sibling.getAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME), 10);
if (!ariaHiddenCount) {
ariaHiddenCount = elemIsAlreadyHidden ? 1 : 0;
ariaHiddenCount = elemIsAlreadyHidden ? 1 : 0;
}
sibling.setAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME, ariaHiddenCount + 1);
@@ -607,7 +552,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
}
);
}
$modalStack.close = function(modalInstance, result) {
var modalWindow = openedWindows.get(modalInstance);
unhideBackgroundElements();
@@ -646,7 +591,6 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
$modalStack.modalRendered = function(modalInstance) {
var modalWindow = openedWindows.get(modalInstance);
$modalStack.focusFirstFocusableElement($modalStack.loadFocusElementList(modalWindow));
if (modalWindow) {
modalWindow.value.renderDeferred.resolve();
}
+2 -2
View File
@@ -526,7 +526,7 @@ describe('$uibModal', function() {
var modal = open({template: '<div>Content<button>inside modal</button></div>'});
$rootScope.$digest();
expect(document.activeElement.tagName).toBe('BUTTON');
expect(document.activeElement.className.split(' ')).toContain('modal');
expect($document).toHaveModalsOpen(1);
triggerKeyDown($document, 27);
@@ -698,7 +698,7 @@ describe('$uibModal', function() {
$rootScope.$digest();
$animate.flush();
expect(document.activeElement.tagName).toBe('INPUT');
expect(document.activeElement.className.split(' ')).toContain('modal');
close(modal, 'closed ok');
+1
View File
@@ -0,0 +1 @@
require('./multiMap.js');
+55
View File
@@ -0,0 +1,55 @@
angular.module('ui.bootstrap.multiMap', [])
/**
* A helper, internal data structure that stores all references attached to key
*/
.factory('$$multiMap', function() {
return {
createNew: function() {
var map = {};
return {
entries: function() {
return Object.keys(map).map(function(key) {
return {
key: key,
value: map[key]
};
});
},
get: function(key) {
return map[key];
},
hasKey: function(key) {
return !!map[key];
},
keys: function() {
return Object.keys(map);
},
put: function(key, value) {
if (!map[key]) {
map[key] = [];
}
map[key].push(value);
},
remove: function(key, value) {
var values = map[key];
if (!values) {
return;
}
var idx = values.indexOf(value);
if (idx !== -1) {
values.splice(idx, 1);
}
if (!values.length) {
delete map[key];
}
}
};
}
};
});
@@ -1,7 +1,7 @@
describe('multi map', function() {
var multiMap;
beforeEach(module('ui.bootstrap.modal'));
beforeEach(module('ui.bootstrap.multiMap'));
beforeEach(inject(function($$multiMap) {
multiMap = $$multiMap.createNew();
}));
+9 -9
View File
@@ -220,21 +220,21 @@ angular.module('ui.bootstrap.timepicker', [])
return e.detail || delta > 0;
};
hoursInputEl.bind('mousewheel wheel', function(e) {
hoursInputEl.on('mousewheel wheel', function(e) {
if (!disabled) {
$scope.$apply(isScrollingUp(e) ? $scope.incrementHours() : $scope.decrementHours());
}
e.preventDefault();
});
minutesInputEl.bind('mousewheel wheel', function(e) {
minutesInputEl.on('mousewheel wheel', function(e) {
if (!disabled) {
$scope.$apply(isScrollingUp(e) ? $scope.incrementMinutes() : $scope.decrementMinutes());
}
e.preventDefault();
});
secondsInputEl.bind('mousewheel wheel', function(e) {
secondsInputEl.on('mousewheel wheel', function(e) {
if (!disabled) {
$scope.$apply(isScrollingUp(e) ? $scope.incrementSeconds() : $scope.decrementSeconds());
}
@@ -244,7 +244,7 @@ angular.module('ui.bootstrap.timepicker', [])
// Respond on up/down arrowkeys
this.setupArrowkeyEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
hoursInputEl.bind('keydown', function(e) {
hoursInputEl.on('keydown', function(e) {
if (!disabled) {
if (e.which === 38) { // up
e.preventDefault();
@@ -258,7 +258,7 @@ angular.module('ui.bootstrap.timepicker', [])
}
});
minutesInputEl.bind('keydown', function(e) {
minutesInputEl.on('keydown', function(e) {
if (!disabled) {
if (e.which === 38) { // up
e.preventDefault();
@@ -272,7 +272,7 @@ angular.module('ui.bootstrap.timepicker', [])
}
});
secondsInputEl.bind('keydown', function(e) {
secondsInputEl.on('keydown', function(e) {
if (!disabled) {
if (e.which === 38) { // up
e.preventDefault();
@@ -339,7 +339,7 @@ angular.module('ui.bootstrap.timepicker', [])
}
};
hoursInputEl.bind('blur', function(e) {
hoursInputEl.on('blur', function(e) {
ngModelCtrl.$setTouched();
if (modelIsEmpty()) {
makeValid();
@@ -371,7 +371,7 @@ angular.module('ui.bootstrap.timepicker', [])
}
};
minutesInputEl.bind('blur', function(e) {
minutesInputEl.on('blur', function(e) {
ngModelCtrl.$setTouched();
if (modelIsEmpty()) {
makeValid();
@@ -397,7 +397,7 @@ angular.module('ui.bootstrap.timepicker', [])
}
};
secondsInputEl.bind('blur', function(e) {
secondsInputEl.on('blur', function(e) {
if (modelIsEmpty()) {
makeValid();
} else if (!$scope.invalidSeconds && $scope.seconds < 10) {
+2 -2
View File
@@ -430,7 +430,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
}
});
element.bind('focus', function (evt) {
element.on('focus', function (evt) {
hasFocus = true;
if (minLength === 0 && !modelCtrl.$viewValue) {
$timeout(function() {
@@ -439,7 +439,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
}
});
element.bind('blur', function(evt) {
element.on('blur', function(evt) {
if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) {
selected = true;
scope.$apply(function() {