Compare commits

..

26 Commits

Author SHA1 Message Date
Wesley Cho 876e72a0df chore(release): 2.1.2 2016-08-22 10:01:02 -07:00
Wesley Cho 6734908108 test(modal): fix custom matchers
- Trigger a test failure if resolved on rejected promise and rejected on resolved promise
- Trigger a test failure if neither resolved or rejected when expected resolved/rejected promise

Closes #6195
2016-08-22 09:57:20 -07:00
Wesley Cho 515bcf2933 fix(collapse): revert change to transition css
- Revert change to `getScrollFromElement` function

Closes #6196
Fixes #6194
2016-08-22 09:54:30 -07:00
Wesley Cho 148371fb6d docs(tooltip): add info on Safari bug
- Add information on Safari bug, affecting all versions up to 9 at the least

Closes #5445
2016-08-22 09:53:11 -07:00
Axel Navarro 8047c06258 docs(modal): added a simple component modal demo
Closes #6193
2016-08-22 08:03:06 -07:00
Axel Navarro 3e8ecfffe0 fix(modal): close and dismiss bindings on component
- Correctly bind close and dismiss handlers on component usage

Closes #6192
Fixes #6191
2016-08-21 12:12:44 -07:00
Georgii Dolzhykov ddcacb7a83 fix(datepicker): fix accidental global
- Fix accidental global usage

Closes #6188
2016-08-21 04:42:13 -07:00
Wesley Cho 915eda0540 chore(release): starting 2.1.2 2016-08-20 22:54:15 -07:00
Wesley Cho f9eede7555 chore(release): 2.1.1 2016-08-20 22:49:30 -07:00
Axel Navarro fb5fabf580 fix(modal): switch to .append
- Switch to `append` since `html` is jQuery specific

Closes #6187
Fixes #6186
2016-08-20 22:46:35 -07:00
Wesley Cho aef24cde4b fix(collapse): default to css
- Default to value set from CSS for height/width

Closes #6182
Fixes #6045
2016-08-20 05:58:59 -07:00
Wesley Cho b893a93f82 docs(collapse): fix height on horizontal
- Fix height on horizontal collapse usage

Closes #6050
Closes #6183
2016-08-20 05:58:03 -07:00
Wesley Cho 6352f13e85 chore(release): starting 2.1.1 2016-08-19 14:34:37 -07:00
Wesley Cho 76cd65ef1a chore(release): 2.1.0 2016-08-19 14:31:32 -07:00
Wesley Cho 84cc2cf5cc fix(collapse): set overflow to hidden on transition
- When collapse is transitioning, set `overflow: hidden` to avoid possibility of scrollbars changing size

Closes #6180
Fixes #5474
2016-08-19 10:37:06 -07:00
Wesley Cho 2ade0545a9 feat(modal): add component support
- Add support for `component` option

Closes #5683
Closes #6179
2016-08-19 09:54:05 -07:00
Stuart Forster f5ff12c0a4 fix(tooltip): close tooltip on esc
- On keypress of escape, close tooltip/popover

Closes #6177
Fixes #6108
2016-08-18 14:10:17 -07:00
Wesley Cho f147d22f5c fix(datepickerPopup): apply timezone conversion
- Apply timezone conversion to selecting today

Closes #6173
Fixes #6147
2016-08-18 13:21:15 -07:00
Wesley Cho f798a47d48 chore: bump angular version 2016-08-18 10:12:21 -07:00
Wesley Cho 43a1f75e7c chore(demo): bump versions 2016-08-18 10:11:55 -07:00
Nick Heiner 4a5e6a7418 fix(modal): improve ARIA support
- Add aria-labelledby and aria-described by support

Closes #4772
2016-08-17 22:09:30 -07:00
Wesley Cho af6c2aaca0 chore(release): starting 2.1.0 2016-08-17 22:09:18 -07:00
Kirkland 61415e1968 Adding roles to the datepicker templates to resolve an ADA violation. (#6170)
Closes #6170
2016-08-17 17:45:34 -07:00
Wesley Cho 5123e38a49 docs(tooltip): add ngSanitize to Plunker
- Add ngSanitize to Plunkers for uib-tooltip-html and uib-popover-html Plunkers

Closes #6165
Fixes #6157
2016-08-16 08:36:00 -07:00
Wesley Cho ca20be4667 fix(collapse): remove unnecessary inherit
- Remove unnecessary `inherit` property, which causes issues when custom widths are used, including percentage based widths

Closes #6164
Fixes #6163
2016-08-16 08:35:09 -07:00
Wesley Cho d9dd5803ae chore(release): starting 2.0.3 2016-08-15 13:45:53 -07:00
20 changed files with 419 additions and 113 deletions
+41
View File
@@ -1,3 +1,44 @@
<a name="2.1.2"></a>
## [2.1.2](https://github.com/angular-ui/bootstrap/compare/2.1.1...v2.1.2) (2016-08-22)
### Bug Fixes
* **collapse:** revert change to transition css ([515bcf2](https://github.com/angular-ui/bootstrap/commit/515bcf2)), closes [#6196](https://github.com/angular-ui/bootstrap/issues/6196) [#6194](https://github.com/angular-ui/bootstrap/issues/6194)
* **datepicker:** fix accidental global ([ddcacb7](https://github.com/angular-ui/bootstrap/commit/ddcacb7)), closes [#6188](https://github.com/angular-ui/bootstrap/issues/6188)
* **modal:** close and dismiss bindings on component ([3e8ecff](https://github.com/angular-ui/bootstrap/commit/3e8ecff)), closes [#6192](https://github.com/angular-ui/bootstrap/issues/6192) [#6191](https://github.com/angular-ui/bootstrap/issues/6191)
<a name="2.1.1"></a>
## [2.1.1](https://github.com/angular-ui/bootstrap/compare/2.1.0...v2.1.1) (2016-08-21)
### Bug Fixes
* **collapse:** default to css([aef24cd](https://github.com/angular-ui/bootstrap/commit/aef24cd)), closes [#6182](https://github.com/angular-ui/bootstrap/issues/6182) [#6045](https://github.com/angular-ui/bootstrap/issues/6045)
* **modal:** switch to .append([fb5fabf](https://github.com/angular-ui/bootstrap/commit/fb5fabf)), closes [#6187](https://github.com/angular-ui/bootstrap/issues/6187) [#6186](https://github.com/angular-ui/bootstrap/issues/6186)
<a name="2.1.0"></a>
# [2.1.0](https://github.com/angular-ui/bootstrap/compare/2.0.2...v2.1.0) (2016-08-19)
### Bug Fixes
* **collapse:** remove unnecessary inherit ([ca20be4](https://github.com/angular-ui/bootstrap/commit/ca20be4)), closes [#6164](https://github.com/angular-ui/bootstrap/issues/6164) [#6163](https://github.com/angular-ui/bootstrap/issues/6163)
* **collapse:** set overflow to hidden on transition ([84cc2cf](https://github.com/angular-ui/bootstrap/commit/84cc2cf)), closes [#6180](https://github.com/angular-ui/bootstrap/issues/6180) [#5474](https://github.com/angular-ui/bootstrap/issues/5474)
* **datepickerPopup:** apply timezone conversion ([f147d22](https://github.com/angular-ui/bootstrap/commit/f147d22)), closes [#6173](https://github.com/angular-ui/bootstrap/issues/6173) [#6147](https://github.com/angular-ui/bootstrap/issues/6147)
* **modal:** improve ARIA support ([4a5e6a7](https://github.com/angular-ui/bootstrap/commit/4a5e6a7)), closes [#4772](https://github.com/angular-ui/bootstrap/issues/4772)
* **tooltip:** close tooltip on esc ([f5ff12c](https://github.com/angular-ui/bootstrap/commit/f5ff12c)), closes [#6177](https://github.com/angular-ui/bootstrap/issues/6177) [#6108](https://github.com/angular-ui/bootstrap/issues/6108)
### Features
* **modal:** add component support ([2ade054](https://github.com/angular-ui/bootstrap/commit/2ade054)), closes [#5683](https://github.com/angular-ui/bootstrap/issues/5683) [#6179](https://github.com/angular-ui/bootstrap/issues/6179)
<a name="2.0.2"></a>
## [2.0.2](https://github.com/angular-ui/bootstrap/compare/2.0.1...v2.0.2) (2016-08-15)
+2 -2
View File
@@ -9,8 +9,8 @@ module.exports = function(grunt) {
grunt.util.linefeed = '\n';
grunt.initConfig({
ngversion: '1.5.5',
bsversion: '3.3.6',
ngversion: '1.5.8',
bsversion: '3.3.7',
modules: [],//to be filled in by build task
pkg: grunt.file.readJSON('package.json'),
dist: 'dist',
+2 -1
View File
@@ -17,6 +17,7 @@ angular.module('plunker', [])
' <head>\n' +
' <script src="//ajax.googleapis.com/ajax/libs/angularjs/'+ngVersion+'/angular.js"></script>\n' +
' <script src="//ajax.googleapis.com/ajax/libs/angularjs/'+ngVersion+'/angular-animate.js"></script>\n' +
' <script src="//ajax.googleapis.com/ajax/libs/angularjs/'+ngVersion+'/angular-sanitize.js"></script>\n' +
' <script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-'+version+'.js"></script>\n' +
' <script src="example.js"></script>\n' +
' <link href="//netdna.bootstrapcdn.com/bootstrap/'+bsVersion+'/css/bootstrap.min.css" rel="stylesheet">\n' +
@@ -28,7 +29,7 @@ angular.module('plunker', [])
};
var scriptContent = function(content) {
return "angular.module('ui.bootstrap.demo', ['ngAnimate', 'ui.bootstrap']);" + "\n" + content;
return "angular.module('ui.bootstrap.demo', ['ngAnimate', 'ngSanitize', 'ui.bootstrap']);" + "\n" + content;
};
addField('description', 'http://angular-ui.github.io/bootstrap/');
+4 -4
View File
@@ -1,7 +1,7 @@
{
"author": "https://github.com/angular-ui/bootstrap/graphs/contributors",
"name": "angular-ui-bootstrap",
"version": "2.0.2",
"version": "2.1.2",
"homepage": "http://angular-ui.github.io/bootstrap/",
"dependencies": {},
"directories": {
@@ -22,9 +22,9 @@
"url": "https://github.com/angular-ui/bootstrap.git"
},
"devDependencies": {
"angular": "1.5.5",
"angular-mocks": "1.5.5",
"angular-sanitize": "1.5.5",
"angular": "1.5.8",
"angular-mocks": "1.5.8",
"angular-sanitize": "1.5.8",
"grunt": "^0.4.5",
"grunt-contrib-concat": "^1.0.0",
"grunt-contrib-copy": "^1.0.0",
+8 -4
View File
@@ -18,14 +18,12 @@ angular.module('ui.bootstrap.collapse', [])
horizontal = !!('horizontal' in attrs);
if (horizontal) {
css = {
width: 'auto',
height: 'inherit'
width: ''
};
cssTo = {width: '0'};
} else {
css = {
width: 'inherit',
height: 'auto'
height: ''
};
cssTo = {height: '0'};
}
@@ -61,10 +59,16 @@ angular.module('ui.bootstrap.collapse', [])
$animateCss(element, {
addClass: 'in',
easing: 'ease',
css: {
overflow: 'hidden'
},
to: getScrollFromElement(element[0])
}).start()['finally'](expandDone);
} else {
$animate.addClass(element, 'in', {
css: {
overflow: 'hidden'
},
to: getScrollFromElement(element[0])
}).then(expandDone);
}
+6 -1
View File
@@ -1,3 +1,8 @@
<style>
.horizontal-collapse {
height: 70px;
}
</style>
<div ng-controller="CollapseDemoCtrl">
<button type="button" class="btn btn-default" ng-click="isCollapsed = !isCollapsed">Toggle collapse Vertically</button>
<hr>
@@ -7,7 +12,7 @@
<button type="button" class="btn btn-default" ng-click="isCollapsedHorizontal = !isCollapsedHorizontal">Toggle collapse Horizontally</button>
<hr>
<div uib-collapse="isCollapsedHorizontal" horizontal>
<div class="horizontal-collapse" uib-collapse="isCollapsedHorizontal" horizontal>
<div class="well well-lg">Some content</div>
</div>
</div>
+4 -1
View File
@@ -27,8 +27,11 @@
<i class="glyphicon glyphicon-eye-open"></i>
_(Default: `false`)_ -
Whether the element should be collapsed or not.
* `horizontal`
<small class="badge">$</small> -
An optional attribute that permit to collapse horizontally.
### Known Issues
When using the `horizontal` attribute with this directive, keep in mind that due to how CSS can reflow as the collapse element goes from 0px to its desired end width, this cause result in issues with a changing height. This can cause animations to not appear to run. The best way around this is to set a fixed height via CSS on the horizontal collapse element so that this situation does not occur, and so the animation can run as expected.
+1 -1
View File
@@ -126,7 +126,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
case 'minMode':
if ($scope.datepickerOptions[key]) {
$scope.$watch(function() { return $scope.datepickerOptions[key]; }, function(value) {
self[key] = $scope[key] = angular.isDefined(value) ? value : datepickerOptions[key];
self[key] = $scope[key] = angular.isDefined(value) ? value : $scope.datepickerOptions[key];
if (key === 'minMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) < self.modes.indexOf(self[key]) ||
key === 'maxMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) > self.modes.indexOf(self[key])) {
$scope.datepickerMode = self[key];
+2 -1
View File
@@ -242,7 +242,8 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
date = new Date($scope.date);
date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());
} else {
date = new Date(today.setHours(0, 0, 0, 0));
date = dateParser.fromTimezone(today, ngModelOptions.timezone);
date.setHours(0, 0, 0, 0);
}
}
$scope.dateSelection(date);
+15 -14
View File
@@ -1,25 +1,26 @@
<div ng-controller="ModalDemoCtrl">
<div ng-controller="ModalDemoCtrl as $ctrl">
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-header">
<h3 class="modal-title">I'm a modal!</h3>
<h3 class="modal-title" id="modal-title">I'm a modal!</h3>
</div>
<div class="modal-body">
<div class="modal-body" id="modal-body">
<ul>
<li ng-repeat="item in items">
<a href="#" ng-click="$event.preventDefault(); selected.item = item">{{ item }}</a>
<li ng-repeat="item in $ctrl.items">
<a href="#" ng-click="$event.preventDefault(); $ctrl.selected.item = item">{{ item }}</a>
</li>
</ul>
Selected: <b>{{ selected.item }}</b>
Selected: <b>{{ $ctrl.selected.item }}</b>
</div>
<div class="modal-footer">
<button class="btn btn-primary" type="button" ng-click="ok()">OK</button>
<button class="btn btn-warning" type="button" ng-click="cancel()">Cancel</button>
<button class="btn btn-primary" type="button" ng-click="$ctrl.ok()">OK</button>
<button class="btn btn-warning" type="button" ng-click="$ctrl.cancel()">Cancel</button>
</div>
</script>
<button type="button" class="btn btn-default" ng-click="open()">Open me!</button>
<button type="button" class="btn btn-default" ng-click="open('lg')">Large modal</button>
<button type="button" class="btn btn-default" ng-click="open('sm')">Small modal</button>
<button type="button" class="btn btn-default" ng-click="toggleAnimation()">Toggle Animation ({{ animationsEnabled }})</button>
<div ng-show="selected">Selection from a modal: {{ selected }}</div>
</div>
<button type="button" class="btn btn-default" ng-click="$ctrl.open()">Open me!</button>
<button type="button" class="btn btn-default" ng-click="$ctrl.open('lg')">Large modal</button>
<button type="button" class="btn btn-default" ng-click="$ctrl.open('sm')">Small modal</button>
<button type="button" class="btn btn-default" ng-click="$ctrl.toggleAnimation()">Toggle Animation ({{ $ctrl.animationsEnabled }})</button>
<button type="button" class="btn btn-default" ng-click="$ctrl.openComponentModal()">Open a component modal!</button>
<div ng-show="$ctrl.selected">Selection from a modal: {{ $ctrl.selected }}</div>
</div>
+67 -19
View File
@@ -1,51 +1,99 @@
angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl', function ($scope, $uibModal, $log) {
angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl', function ($uibModal, $log) {
var $ctrl = this;
$ctrl.items = ['item1', 'item2', 'item3'];
$scope.items = ['item1', 'item2', 'item3'];
$scope.animationsEnabled = true;
$scope.open = function (size) {
$ctrl.animationsEnabled = true;
$ctrl.open = function (size) {
var modalInstance = $uibModal.open({
animation: $scope.animationsEnabled,
animation: $ctrl.animationsEnabled,
ariaLabelledBy: 'modal-title',
ariaDescribedBy: 'modal-body',
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
controllerAs: '$ctrl',
size: size,
resolve: {
items: function () {
return $scope.items;
return $ctrl.items;
}
}
});
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem;
$ctrl.selected = selectedItem;
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
$scope.toggleAnimation = function () {
$scope.animationsEnabled = !$scope.animationsEnabled;
$ctrl.openComponentModal = function () {
var modalInstance = $uibModal.open({
animation: $ctrl.animationsEnabled,
component: 'modalComponent',
resolve: {
items: function () {
return $ctrl.items;
}
}
});
modalInstance.result.then(function (selectedItem) {
$ctrl.selected = selectedItem;
}, function () {
$log.info('modal-component dismissed at: ' + new Date());
});
};
$ctrl.toggleAnimation = function () {
$ctrl.animationsEnabled = !$ctrl.animationsEnabled;
};
});
// Please note that $uibModalInstance represents a modal window (instance) dependency.
// It is not the same as the $uibModal service used above.
angular.module('ui.bootstrap.demo').controller('ModalInstanceCtrl', function ($scope, $uibModalInstance, items) {
$scope.items = items;
$scope.selected = {
item: $scope.items[0]
angular.module('ui.bootstrap.demo').controller('ModalInstanceCtrl', function ($uibModalInstance, items) {
var $ctrl = this;
$ctrl.items = items;
$ctrl.selected = {
item: $ctrl.items[0]
};
$scope.ok = function () {
$uibModalInstance.close($scope.selected.item);
$ctrl.ok = function () {
$uibModalInstance.close($ctrl.selected.item);
};
$scope.cancel = function () {
$ctrl.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
});
// Please note that the close and dismiss bindings are from $uibModalInstance.
angular.module('ui.bootstrap.demo').component('modalComponent', {
templateUrl: 'myModalContent.html',
bindings: {
resolve: '<',
close: '&',
dismiss: '&'
},
controller: function () {
var $ctrl = this;
$ctrl.$onInit = function () {
$ctrl.items = $ctrl.resolve.items;
$ctrl.selected = {
item: $ctrl.items[0]
};
};
$ctrl.ok = function () {
$ctrl.close({$value: $ctrl.selected.item});
};
$ctrl.cancel = function () {
$ctrl.dismiss({$value: 'cancel'});
};
}
});
+34 -12
View File
@@ -10,15 +10,23 @@ The `$uibModal` service has only one method: `open(options)`.
* `animation`
_(Type: `boolean`, Default: `true`)_ -
Set to false to disable animations on new modal/backdrop. Does not toggle animations for modals/backdrops that are already displayed.
* `appendTo`
* `appendTo`
_(Type: `angular.element`, Default: `body`: Example: `$document.find('aside').eq(0)`)_ -
Appends the modal to a specific element.
* `ariaDescribedBy`
_(Type: `string`, `my-modal-description`)_ -
Sets the [`aria-describedby`](https://www.w3.org/TR/wai-aria/states_and_properties#aria-describedby) property on the modal. The value should be an id (without the leading `#`) pointing to the element that describes your modal. Typically, this will be the text on your modal, but does not include something the user would interact with, like buttons or a form. Omitting this option will not impact sighted users but will weaken your accessibility support.
* `ariaLabelledBy`
_(Type: `string`, `my-modal-title`)_ -
Sets the [`aria-labelledby`](https://www.w3.org/TR/wai-aria/states_and_properties#aria-labelledby) property on the modal. The value should be an id (without the leading `#`) pointing to the element that labels your modal. Typically, this will be a header element. Omitting this option will not impact sighted users but will weaken your accessibility support.
* `backdrop`
_(Type: `boolean|string`, Default: `true`)_ -
Controls presence of a backdrop. Allowed values: `true` (default), `false` (no backdrop), `'static'` (disables modal closing by click on the backdrop).
* `backdropClass`
_(Type: `string`)_ -
Additional CSS class(es) to be added to a modal backdrop template.
@@ -27,15 +35,29 @@ The `$uibModal` service has only one method: `open(options)`.
_(Type: `boolean`, Default: `false`)_ -
When used with `controllerAs` & set to `true`, it will bind the $scope properties onto the controller.
* `component`
_(Type: `string`, Example: `myComponent`)_ -
A string reference to the component to be rendered that is registered with Angular's compiler. If using a directive, the directive must have `restrict: 'E'` and a template or templateUrl set.
It supports these bindings:
* `close` - A method that can be used to close a modal, passing a result. The result must be passed in this format: `{$value: myResult}`
* `dismiss` - A method that can be used to dismiss a modal, passing a result. The result must be passed in this format: `{$value: myRejectedResult}`
* `modalInstance` - The modal instance. This is the same `$uibModalInstance` injectable found when using `controller`.
* `resolve` - An object of the modal resolve values. See [UI Router resolves](#ui-router-resolves) for details.
* `controller`
_(Type: `function|string|array`, Example: `MyModalController`)_ -
A controller for the modal instance, either a controller name as a string, or an inline controller function, optionally wrapped in array notation for dependency injection. Allows the controller-as syntax. Has a special `$uibModalInstance` injectable to access the modal instance.
* `controllerAs`
_(Type: `string`, Example: `ctrl`)_ -
_(Type: `string`, Example: `ctrl`)_ -
An alternative to the controller-as syntax. Requires the `controller` option to be provided as well.
* `keyboard` -
* `keyboard` -
_(Type: `boolean`, Default: `true`)_ -
Indicates whether the dialog should be closable by hitting the ESC key.
@@ -76,7 +98,7 @@ The `$uibModal` service has only one method: `open(options)`.
CSS class(es) to be added to the top modal window.
Global defaults may be set for `$uibModal` via `$uibModalProvider.options`.
#### return
The `open` method returns a modal instance, an object with the following properties:
@@ -103,8 +125,8 @@ The `open` method returns a modal instance, an object with the following propert
* `rendered`
_(Type: `promise`)_ -
Is resolved when a modal is rendered.
Is resolved when a modal is rendered.
---
The scope associated with modal's content is augmented with:
@@ -125,9 +147,9 @@ Also, when using `bindToController`, you can define an `$onInit` method in the c
Events fired:
* `$uibUnscheduledDestruction` -
* `$uibUnscheduledDestruction` -
This event is fired if the $scope is destroyed via unexpected mechanism, such as it being passed in the modal options and a $route/$state transition occurs. The modal will also be dismissed.
* `modal.closing` -
This event is broadcast to the modal scope before the modal closes. If the listener calls preventDefault() on the event, then the modal will remain open.
Also, the `$close` and `$dismiss` methods returns true if the event was executed. This event also includes a parameter for the result/reason and a boolean that indicates whether the modal is being closed (true) or dismissed.
@@ -136,4 +158,4 @@ Events fired:
If one wants to have the modal resolve using [UI Router's](https://github.com/angular-ui/ui-router) pre-1.0 resolve mechanism, one can call `$uibResolve.setResolver('$resolve')` in the configuration phase of the application. One can also provide a custom resolver as well, as long as the signature conforms to UI Router's [$resolve](http://angular-ui.github.io/ui-router/site/#/api/ui.router.util.$resolve).
When the modal is opened with a controller, a `$resolve` object is exposed on the template with the resolved values from the resolve object.
When the modal is opened with a controller, a `$resolve` object is exposed on the template with the resolved values from the resolve object. If using the component option, see details on how to access this object in component section of the modal documentation.
+85 -31
View File
@@ -260,6 +260,15 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
'button:not([disabled]):not([tabindex=\'-1\']),select:not([disabled]):not([tabindex=\'-1\']), textarea:not([disabled]):not([tabindex=\'-1\']), ' +
'iframe, object, embed, *[tabindex]:not([tabindex=\'-1\']), *[contenteditable=true]';
var scrollbarPadding;
var SNAKE_CASE_REGEXP = /[A-Z]/g;
// TODO: extract into common dependency with tooltip
function snake_case(name) {
var separator = '-';
return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
return (pos ? separator : '') + letter.toLowerCase();
});
}
function isVisible(element) {
return !!(element.offsetWidth ||
@@ -496,6 +505,21 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
}
}
var content;
if (modal.component) {
content = document.createElement(snake_case(modal.component.name));
content = angular.element(content);
content.attr({
resolve: '$resolve',
'modal-instance': '$uibModalInstance',
close: '$close($value)',
dismiss: '$dismiss($value)'
});
content = $compile(content)(modal.scope);
} else {
content = modal.content;
}
// Set the top modal index based on the index of the previous top modal
topModalIndex = previousTopOpenedModal ? parseInt(previousTopOpenedModal.value.modalDomEl.attr('index'), 10) + 1 : 0;
var angularDomEl = angular.element('<div uib-modal-window="modal-window"></div>');
@@ -504,6 +528,8 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
'template-url': modal.windowTemplateUrl,
'window-top-class': modal.windowTopClass,
'role': 'dialog',
'aria-labelledby': modal.ariaLabelledBy,
'aria-describedby': modal.ariaDescribedBy,
'size': modal.size,
'index': topModalIndex,
'animate': 'animate',
@@ -511,7 +537,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
'tabindex': -1,
'uib-modal-animation-class': 'fade',
'modal-in-class': 'in'
}).html(modal.content);
}).append(content);
if (modal.windowClass) {
angularDomEl.addClass(modal.windowClass);
}
@@ -680,12 +706,17 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
modalOptions.appendTo = modalOptions.appendTo || $document.find('body').eq(0);
//verify options
if (!modalOptions.template && !modalOptions.templateUrl) {
throw new Error('One of template or templateUrl options is required.');
if (!modalOptions.component && !modalOptions.template && !modalOptions.templateUrl) {
throw new Error('One of component or template or templateUrl options is required.');
}
var templateAndResolvePromise =
$q.all([getTemplatePromise(modalOptions), $uibResolve.resolve(modalOptions.resolve, {}, null, null)]);
var templateAndResolvePromise;
if (modalOptions.component) {
templateAndResolvePromise = $q.when($uibResolve.resolve(modalOptions.resolve, {}, null, null));
} else {
templateAndResolvePromise =
$q.all([getTemplatePromise(modalOptions), $uibResolve.resolve(modalOptions.resolve, {}, null, null)]);
}
function resolveWithTemplate() {
return templateAndResolvePromise;
@@ -711,17 +742,34 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
}
});
var modal = {
scope: modalScope,
deferred: modalResultDeferred,
renderDeferred: modalRenderDeferred,
closedDeferred: modalClosedDeferred,
animation: modalOptions.animation,
backdrop: modalOptions.backdrop,
keyboard: modalOptions.keyboard,
backdropClass: modalOptions.backdropClass,
windowTopClass: modalOptions.windowTopClass,
windowClass: modalOptions.windowClass,
windowTemplateUrl: modalOptions.windowTemplateUrl,
ariaLabelledBy: modalOptions.ariaLabelledBy,
ariaDescribedBy: modalOptions.ariaDescribedBy,
size: modalOptions.size,
openedClass: modalOptions.openedClass,
appendTo: modalOptions.appendTo
};
var component = {};
var ctrlInstance, ctrlInstantiate, ctrlLocals = {};
//controllers
if (modalOptions.controller) {
ctrlLocals.$scope = modalScope;
ctrlLocals.$scope.$resolve = {};
ctrlLocals.$uibModalInstance = modalInstance;
angular.forEach(tplAndVars[1], function(value, key) {
ctrlLocals[key] = value;
ctrlLocals.$scope.$resolve[key] = value;
});
if (modalOptions.component) {
constructLocals(component, false, true, false);
component.name = modalOptions.component;
modal.component = component;
} else if (modalOptions.controller) {
constructLocals(ctrlLocals, true, false, true);
// the third param will make the controller instantiate later,private api
// @see https://github.com/angular/angular.js/blob/master/src/ng/controller.js#L126
@@ -742,25 +790,31 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
}
}
$modalStack.open(modalInstance, {
scope: modalScope,
deferred: modalResultDeferred,
renderDeferred: modalRenderDeferred,
closedDeferred: modalClosedDeferred,
content: tplAndVars[0],
animation: modalOptions.animation,
backdrop: modalOptions.backdrop,
keyboard: modalOptions.keyboard,
backdropClass: modalOptions.backdropClass,
windowTopClass: modalOptions.windowTopClass,
windowClass: modalOptions.windowClass,
windowTemplateUrl: modalOptions.windowTemplateUrl,
size: modalOptions.size,
openedClass: modalOptions.openedClass,
appendTo: modalOptions.appendTo
});
if (!modalOptions.component) {
modal.content = tplAndVars[0];
}
$modalStack.open(modalInstance, modal);
modalOpenedDeferred.resolve(true);
function constructLocals(obj, template, instanceOnScope, injectable) {
obj.$scope = modalScope;
obj.$scope.$resolve = {};
if (instanceOnScope) {
obj.$scope.$uibModalInstance = modalInstance;
} else {
obj.$uibModalInstance = modalInstance;
}
var resolves = template ? tplAndVars[1] : tplAndVars;
angular.forEach(resolves, function(value, key) {
if (injectable) {
obj[key] = value;
}
obj.$scope.$resolve[key] = value;
});
}
}, function resolveError(reason) {
modalOpenedDeferred.reject(reason);
modalResultDeferred.reject(reason);
+128 -7
View File
@@ -131,6 +131,16 @@ describe('$uibModal', function() {
elem.focus();
}
};
}).component('fooBar', {
bindings: {
resolve: '<',
modalInstance: '<',
close: '&',
dismiss: '&'
},
controller: angular.noop,
controllerAs: 'foobar',
template: '<div>Foo Bar</div>'
});
}));
@@ -151,6 +161,7 @@ describe('$uibModal', function() {
toBeResolvedWith: function(util, customEqualityTesters) {
return {
compare: function(promise, expected) {
var called = false;
promise.then(function(result) {
expect(result).toEqual(expected);
@@ -159,10 +170,18 @@ describe('$uibModal', function() {
} else {
result.message = 'Expected "' + angular.mock.dump(result) + '" to be resolved with "' + expected + '".';
}
}, function(result) {
fail('Expected "' + angular.mock.dump(result) + '" to be resolved with "' + expected + '".');
})['finally'](function() {
called = true;
});
$rootScope.$digest();
if (!called) {
fail('Expected "' + angular.mock.dump(result) + '" to be resolved with "' + expected + '".');
}
return {pass: true};
}
};
@@ -171,9 +190,10 @@ describe('$uibModal', function() {
return {
compare: function(promise, expected) {
var result = {};
var called = false;
promise.then(function() {
promise.then(function(result) {
fail('Expected "' + angular.mock.dump(result) + '" to be rejected with "' + expected + '".');
}, function(result) {
expect(result).toEqual(expected);
@@ -182,10 +202,16 @@ describe('$uibModal', function() {
} else {
result.message = 'Expected "' + angular.mock.dump(result) + '" to be rejected with "' + expected + '".';
}
})['finally'](function() {
called = true;
});
$rootScope.$digest();
if (!called) {
fail('Expected "' + angular.mock.dump(result) + '" to be rejected with "' + expected + '".');
}
return {pass: true};
}
};
@@ -930,16 +956,89 @@ describe('$uibModal', function() {
});
});
describe('option by option', function () {
describe('template and templateUrl', function () {
it('should throw an error if none of template and templateUrl are provided', function() {
describe('option by option', function() {
describe('component', function() {
function getModalComponent($document) {
return $document.find('body > div.modal > div.modal-dialog > div.modal-content foo-bar');
}
it('should use as modal content', function() {
open({
component: 'fooBar'
});
var component = getModalComponent($document);
expect(component.html()).toBe('<div>Foo Bar</div>');
});
it('should bind expected values', function() {
var modal = open({
component: 'fooBar',
resolve: {
foo: function() {
return 'bar';
}
}
});
var component = getModalComponent($document);
var componentScope = component.isolateScope();
expect(componentScope.foobar.resolve.foo).toBe('bar');
expect(componentScope.foobar.modalInstance).toBe(modal);
expect(componentScope.foobar.close).toEqual(jasmine.any(Function));
expect(componentScope.foobar.dismiss).toEqual(jasmine.any(Function));
});
it('should close the modal', function() {
var modal = open({
component: 'fooBar',
resolve: {
foo: function() {
return 'bar';
}
}
});
var component = getModalComponent($document);
var componentScope = component.isolateScope();
componentScope.foobar.close({
$value: 'baz'
});
expect(modal.result).toBeResolvedWith('baz');
});
it('should dismiss the modal', function() {
var modal = open({
component: 'fooBar',
resolve: {
foo: function() {
return 'bar';
}
}
});
var component = getModalComponent($document);
var componentScope = component.isolateScope();
componentScope.foobar.dismiss({
$value: 'baz'
});
expect(modal.result).toBeRejectedWith('baz');
});
});
describe('template and templateUrl', function() {
it('should throw an error if none of component, template and templateUrl are provided', function() {
expect(function(){
var modal = open({});
}).toThrow(new Error('One of template or templateUrl options is required.'));
}).toThrow(new Error('One of component or template or templateUrl options is required.'));
});
it('should not fail if a templateUrl contains leading / trailing white spaces', function() {
$templateCache.put('whitespace.html', ' <div>Whitespaces</div> ');
open({templateUrl: 'whitespace.html'});
expect($document).toHaveModalOpenWithContent('Whitespaces', 'div');
@@ -1465,6 +1564,28 @@ describe('$uibModal', function() {
expect(body).not.toHaveClass('modal-open');
});
});
describe('ariaLabelledBy', function() {
it('should add the aria-labelledby property to the modal', function() {
open({
template: '<div><h3 id="modal-label">Modal Label</h3><p id="modal-description">Modal description</p></div>',
ariaLabelledBy: 'modal-label'
});
expect($document.find('.modal').attr('aria-labelledby')).toEqual('modal-label');
});
});
describe('ariaDescribedBy', function() {
it('should add the aria-describedby property to the modal', function() {
open({
template: '<div><h3 id="modal-label">Modal Label</h3><p id="modal-description">Modal description</p></div>',
ariaDescribedBy: 'modal-description'
});
expect($document.find('.modal').attr('aria-describedby')).toEqual('modal-description');
});
});
});
describe('modal window', function() {
+2
View File
@@ -118,3 +118,5 @@ For Safari 7+ support, if you want to use the **focus** `tooltip-trigger`, you n
Click Me
</a>
```
For Safari (potentially all versions up to 9), there is an issue with the hover CSS selector when using multiple elements grouped close to each other that are using the tooltip - it is possible for multiple elements to gain the hover state when mousing between the elements quickly and exiting the container at the right time. See [issue #5445](https://github.com/angular-ui/bootstrap/issues/5445) for more details.
+6 -5
View File
@@ -27,7 +27,7 @@ describe('tooltip', function() {
}));
afterEach(function() {
$document.off('keypress');
$document.off('keyup');
});
function trigger(element, evt) {
@@ -295,10 +295,9 @@ describe('tooltip', function() {
trigger(elm, 'mouseenter');
expect(tooltipScope.isOpen).toBe(false);
$timeout.flush(500);
expect(tooltipScope.isOpen).toBe(false);
elmScope.disabled = true;
elmScope.$digest();
$timeout.flush(500);
expect(tooltipScope.isOpen).toBe(false);
});
@@ -343,22 +342,24 @@ describe('tooltip', function() {
expect(tooltipScope.isOpen).toBe(true);
expect(tooltipScope2.isOpen).toBe(true);
var evt = $.Event('keypress');
var evt = $.Event('keyup');
evt.which = 27;
$document.trigger(evt);
tooltipScope.$digest();
tooltipScope2.$digest();
$timeout.flush();
expect(tooltipScope.isOpen).toBe(true);
expect(tooltipScope2.isOpen).toBe(false);
var evt2 = $.Event('keypress');
var evt2 = $.Event('keyup');
evt2.which = 27;
$document.trigger(evt2);
tooltipScope.$digest();
tooltipScope2.$digest();
$timeout.flush(500);
expect(tooltipScope.isOpen).toBe(false);
expect(tooltipScope2.isOpen).toBe(false);
+9 -7
View File
@@ -71,10 +71,10 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
*/
this.$get = ['$window', '$compile', '$timeout', '$document', '$uibPosition', '$interpolate', '$rootScope', '$parse', '$$stackedMap', function($window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse, $$stackedMap) {
var openedTooltips = $$stackedMap.createNew();
$document.on('keypress', keypressListener);
$document.on('keyup', keypressListener);
$rootScope.$on('$destroy', function() {
$document.off('keypress', keypressListener);
$document.off('keyup', keypressListener);
});
function keypressListener(e) {
@@ -82,7 +82,6 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
var last = openedTooltips.top();
if (last) {
last.value.close();
openedTooltips.removeTop();
last = null;
}
}
@@ -207,9 +206,6 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
// By default, the tooltip is not open.
// TODO add ability to start tooltip opened
ttScope.isOpen = false;
openedTooltips.add(ttScope, {
close: hide
});
function toggleTooltipBind() {
if (!ttScope.isOpen) {
@@ -336,6 +332,10 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
}
});
openedTooltips.add(ttScope, {
close: hide
});
prepObservers();
}
@@ -348,6 +348,9 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
tooltip.remove();
tooltip = null;
}
openedTooltips.remove(ttScope);
if (tooltipLinkedScope) {
tooltipLinkedScope.$destroy();
tooltipLinkedScope = null;
@@ -563,7 +566,6 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
scope.$on('$destroy', function onDestroyTooltip() {
unregisterTriggers();
removeTooltip();
openedTooltips.remove(ttScope);
ttScope = null;
});
};
+1 -1
View File
@@ -11,7 +11,7 @@
</tr>
</thead>
<tbody>
<tr class="uib-weeks" ng-repeat="row in rows track by $index">
<tr class="uib-weeks" ng-repeat="row in rows track by $index" role="row">
<td ng-if="showWeeks" class="text-center h6"><em>{{ weekNumbers[$index] }}</em></td>
<td ng-repeat="dt in row" class="uib-day text-center" role="gridcell"
id="{{::dt.uid}}"
+1 -1
View File
@@ -7,7 +7,7 @@
</tr>
</thead>
<tbody>
<tr class="uib-months" ng-repeat="row in rows track by $index">
<tr class="uib-months" ng-repeat="row in rows track by $index" role="row">
<td ng-repeat="dt in row" class="uib-month text-center" role="gridcell"
id="{{::dt.uid}}"
ng-class="::dt.customClass">
+1 -1
View File
@@ -7,7 +7,7 @@
</tr>
</thead>
<tbody>
<tr class="uib-years" ng-repeat="row in rows track by $index">
<tr class="uib-years" ng-repeat="row in rows track by $index" role="row">
<td ng-repeat="dt in row" class="uib-year text-center" role="gridcell"
id="{{::dt.uid}}"
ng-class="::dt.customClass">