Compare commits

...

73 Commits

Author SHA1 Message Date
Wesley Cho 5173779f84 chore(release): 2.0.0 2016-07-19 23:59:40 -07:00
Wesley Cho 1616e9720d feat(tooltip): remove replace usage
- Remove `replace: true` usage

BREAKING CHANGE: The template structure changed slightly due to the removal of `replace: true` - see documentation examples in action to see differences in practice.

Closes #5994
2016-07-19 13:40:38 -07:00
Ashton b77f02cdce docs(readme): change to https
- Change from http to https

Closes #6064
2016-06-30 08:45:49 -07:00
craig 39d9b9886d feat(datepicker): add monthColumns
- Add `monthColumns` option

Closes #5515
Closes #5935
2016-06-27 09:45:30 -07:00
Dolev Dori 2d831bcc2a fix(dropdown): align position correctly with scrollbar
- Fix horizontal alignment independent of the presence of a vertical scrollbar

Closes #6008
Fixes #5942
2016-06-27 09:32:13 -07:00
Wesley Cho d846e2d788 fix(dateparser): correctly format with literals
- Fix formatting of dates with literal usage

Closes #6055
Fixes #5620
Fixes #5802
2016-06-27 09:18:25 -07:00
Anthony Cleaver 409b7aa3b3 fix(modal): remove window class after animation
- Wait until animations are complete before removing window class

BREAKING CHANGE: This introduces a minor behavior change in when the class is removed

Closes #6056
Fixes #6051
2016-06-27 08:42:20 -07:00
Manuel Siggen ed3400bb68 fix(typeahead): clear validity in $digest
- Clear within $digest cycle

Closes #6033
Fixes #6032
2016-06-26 17:17:46 -07:00
Anthony Cleaver 1cbd73d227 feat(modal): append using $animate
- Use $animate.enter to append transcluded content

Closes #6023
Closes #6029
2016-06-26 17:07:01 -07:00
robjacobs 433e536e6b fix(modal): check for overflow hidden
Check if appendToElement has scrolling disabled before adding
the scrollbar padding.

Fixes #6037
Closes #6041
2016-06-21 15:35:26 -04:00
squelix 1ec099767f feat(collapse): add horizontal support
- Add support for horizontal collapsing

Closes #3593
Closes #6010
2016-06-16 10:52:31 -07:00
Wesley Cho 4e0e34f47a revert(dropdown): change back to .constant() 2016-06-15 10:12:14 -07:00
Wei Wang 7457fb0e63 feat(dropdown): use .value() for uibDropdownConfig
Closes #6004
Closes #6006
2016-06-15 06:44:55 -07:00
Wei Wang 23ef372a17 docs(README): add modal to index-nocss list
Closes #6005
2016-06-14 14:04:25 -07:00
Andrij Fedyk 164811ab35 fix(tooltip): missed dependency for cjs
Closes #5999
2016-06-14 14:03:19 -07:00
Andrij Fedyk 1de58a3e5d fix(modal): remove unused template from modal 2016-06-14 14:03:17 -07:00
dirk 7e320e0e92 fix(datepickerPopup): use value instead of viewValue
Closes #6007
2016-06-14 14:02:22 -07:00
Wesley Cho d6fe9c7e7a feat(rating): remove replace usage
- Remove `replace: true` usage

BREAKING CHANGE: Due to the removal of `replace: true`, this results in a slight change to the HTML structure - see the documentation examples to see it in action.

Closes #5998
2016-06-13 20:17:38 -07:00
Wesley Cho 75188211aa feat(timepicker): remove replace usage
- Remove `replace: true` usage

BREAKING CHANGE: This removes `replace: true`, which changes the HTML structure slightly - see the documentation examples to see it in action.

Closes #5997
2016-06-13 20:17:03 -07:00
johanneskrtek 4e68778561 feat(timepicker): avoid allowing to tab to controls
- Prevent the ability to tab to timepicker controls, leaving incrementing/decrementing to keyboard actions on the individual inputs

Closes #5930
2016-06-13 20:15:34 -07:00
Wesley Cho a68cc20dd4 chore(dropdown): add guard when closing 2016-06-13 12:54:15 -07:00
Wesley Cho ba113a050e chore(pager): add tabindex module dep 2016-06-13 10:35:53 -07:00
Wesley Cho 0d8cec6e4f feat(pager): toggle tabindex when disabled
- Add `tabindex="-1"` when disabled and remove it when not for accessibility

Closes #5996
2016-06-13 10:34:52 -07:00
Wesley Cho a47bcedad4 feat(datepickerPopup): remove replace usage
- Remove `replace: true` usage

BREAKING CHANGE: Due to the nature of `replace: true`, this has a slight structural HTML change in the popup as a result - see documentation examples for the change in action.

Closes #5993
2016-06-13 10:18:37 -07:00
Wesley Cho 387c6e7bbc feat(pagination): remove replace usage
- Remove `replace: true` usage

BREAKING CHANGE: Due to the removal of `replace: true`, this affects the HTML structure slightly - see documentation examples to see new usage.

Closes #5992
2016-06-13 10:17:53 -07:00
Wesley Cho 9b24e1dfed feat(pager): remove replace usage
- Remove `replace: true usage

BREAKING CHANGE: This removes `replace: true` usage from the pager, which causes a slight usage change - see documentation examples for new usage.

Closes #5991
2016-06-13 10:08:01 -07:00
Wesley Cho 5f4eedd0e7 feat(accordion): add appropriate tabindex on disabled
- Add `tabindex="-1"` to accordion group header when disabled and remove when not to assist with accessibility

Closes #4067
Closes #5990
2016-06-13 10:07:24 -07:00
Wesley Cho 96d52ce150 feat(modal): remove replace usage
- Remove `replace: true` usage

BREAKING CHANGE: This removes `replace: true` usage, causing some structural changes to the HTML - the major part here is that there is no more backdrop template, and the top level elements for the window/backdrop elements lose a little flexibility. See documentation examples for new structure.

Closes #5989
2016-06-13 10:06:48 -07:00
renhao 55cf26bf28 chore(datepicker): remove dead code
Closes #5995
2016-06-13 10:05:31 -07:00
Wesley Cho 5fdcbd6b90 chore(dropdown): add guard 2016-06-13 00:21:39 -07:00
Wesley Cho e92fb7fa7c feat(datepicker): remove replace: true usage
- Remove `replace: true` usage

BREAKING CHANGE: As a result of removal of `replace: true`, there is the potential that this may break some CSS layout due to the slightly different HTML. Refer to the documentation examples to see the new structure.

Closes #5988
2016-06-12 23:53:11 -07:00
Wesley Cho 1a870a3bb1 feat(pagination): disable tabbing when disabled
- Add directive for toggling `tabindex="-1"` when ngDisabled is true, and removing tabindex when false

Closes #5984
2016-06-12 23:52:20 -07:00
Wesley Cho 6038403179 fix(dropdown): bind event listener on dropdown menu
- Bind keypress listener to dropdown menu for handling keyboard navigation

Closes #5982
2016-06-12 23:51:25 -07:00
Wesley Cho fb38d0db2e chore(position): add missing , 2016-06-12 15:18:32 -07:00
Wesley Cho 5046bd4e56 feat(carousel): remove replace: true usage
- Remove `replace: true` usage from the carousel and the slide

BREAKING CHANGE: Due to the removal of `replace: true`, this causes a slight HTML structure change to the carousel and the slide elements - see documentation demos to see how it changes. This also caused removal of the ngTouch built in support - if one is using ng-touch, one needs to add the `ng-swipe-left` and `ng-swipe-right` directives to the carousel element with relevant logic.

Closes #5987
2016-06-12 15:13:26 -07:00
Wesley Cho 2458c2863d feat(alert): remove replace: true usage
- Remove `replace: true` usage

BREAKING CHANGE: This removes the `replace: true` usage, so this has an effect on how one uses the directive in the template - see documentation for reference

Closes #5986
2016-06-12 15:12:25 -07:00
Wesley Cho 3819bbe8fa feat(accordion): remove replace: true usage
- Remove usage of `replace: true`
- Remove unnecessary `panel-class` feature

BREAKING CHANGE: This removes usage of `replace: true` in the accordion group, which results in a template change where the template no longer needs to contain the panel itself, but its contents. The accordion group will add the `panel` class by default, so the user just needs to add the appropriate classes to the accordion group element. This allows the user to use ng-class as well to fully control the panel related classes, so `panel-class` now is unnecessary

Closes #5985
2016-06-12 15:11:10 -07:00
Wesley Cho e2016fd2eb refactor(position): abstract out common logic
- Abstracting out conditional to position service adjustTop method to increase readability and to prepare it for use in dropdown/typeahead

Closes #5983
2016-06-12 15:10:15 -07:00
Wesley Cho 34a1443a85 fix(tooltip): reposition for placement top
- Reposition the tooltip when the placement option contains top in order to account for resizing of tooltip from right side

Closes #5959
2016-06-11 15:47:04 -07:00
atrinh 35ced04445 fix(modal): filter out non-tabbable elements
- Ensure non-tabbable elements with taxindex of -1 are not tabbed to

Closes #5963
Fixes #5955
2016-06-09 14:25:22 -07:00
Wesley Cho 90fa2f682a docs(readme): add note about versioning policy
- Add information on versioning policy

Closes #5800
Closes #5958
2016-06-03 21:40:23 -07:00
cherry-geqi 4fef0377bb fix(datepickerPopup): specify dependency on datepicker
- Specify dependency on datepicker module

Closes #5954
2016-06-02 23:27:38 -07:00
rcholic b0466d9e6e fix(datepickerPopup): clear date when button is clicked
- Fix clearing the date when the clear button is clicked

Closes #5945
Fixes #5906
2016-05-31 08:50:12 -07:00
Wesley Cho 4b912228be feat(tooltip): add expression support
- Add expression support for tooltip-trigger
- Add support for object event bindings for showing/hiding tooltips

BREAKING CHANGE: This removes support for plain strings and interpolations in tooltip-trigger and popover-trigger - please change these appropriately. See test changes in this commit for reference

Closes #5221
Closes #5938
2016-05-28 22:14:05 -07:00
Wesley Cho 9a606dca7c chore(typeahead): add matcher class 2016-05-28 18:21:36 -07:00
Adam Gordon 6d5b84a0ba fix(typeahead): remove duplicate id attribute
* remove any duplicate `id` attribute on the typeahead hint element if used on
  the original input element.

BREAKING CHANGE: This change removes the `id` attribute on the first `<input>`
element placed into the DOM when the `typeahead-show-hint` attribute is used
and there is an `id` attribute present on the original `uib-typeahead` element.
This could affect selectors if they are being used.

Closes #5936
Fixes #5926
2016-05-28 17:27:20 -07:00
Wesley Cho cce00970f1 feat(dropdown): focus toggle on close from click
- Focus the toggle element when the click causes the dropdown menu to close

BREAKING CHANGE: This changes the focus behavior of the dropdown slightly, and potentially may break code built around current usage

Closes #5934
Fixes #5782
2016-05-28 17:18:39 -07:00
Wesley Cho 23b91d951e feat(datepicker): add ngModelOptions support
- Adds support for ngModelOptions in the `datepicker-options` object

BREAKING CHANGE: This modifies the current behavior around the datepicker & popup's ngModelOptions, which may affect custom components that are built around both

Closes #5933
Fixes #5825
2016-05-28 09:09:18 -07:00
Wesley Cho 053035234c chore: bump angular to 1.5.5
Closes #5920
2016-05-28 07:27:45 -07:00
deplay a075824087 feat(stackedMap): improve perf of removeTop
- Change `removeTop` from `slice` to `pop`

Closes #5925
Closes #5932
2016-05-28 07:24:25 -07:00
Wesley Cho 13c14af9a2 fix(typeahead): change to select class
- Change to select `.uib-typeahead-match` class instead of the `li` tag

BREAKING CHANGE: This changes the selector used so that it doesn't select for the `li` tag specifically, but the elements with the class `uib-typeahead-match` - if using a custom template, this class needs to be added to the `li` element in the typeahead popup template.

Closes #5922
Fixes #5848
2016-05-24 11:31:34 -07:00
Wesley Cho 280ddaf47e chore(release): starting 2.0.0 2016-05-22 20:09:55 -07:00
Wesley Cho dc85f67b2d chore(release): 1.3.3 2016-05-22 20:05:01 -07:00
Christopher Rued 2ffb86f201 feat(typeahead): Add support for should-select
By default behaves as before. If specified, allows a custom function
to be defined to determine whether the keydown event should trigger
selection based on the `typeahead-should-select` attribute.

Closes #5671
Closes #5794
2016-05-21 22:33:00 -07:00
Adam Gordon aec5304ef5 chore(button): add known issue for tooltip/popover
* add known issue to button docs indicating a CSS issue when using a tooltip or
  a popover.

Closes #5890
2016-05-16 08:47:26 -07:00
robjacobs eb02778297 chore(tab): code cleanup
Removing unnecessary code added under PR #5489

Closes #5891
Fixes #5888
2016-05-10 14:09:39 -04:00
robjacobs 26d399557e feat(tab): allow strings for index
- Allows the use of strings for the tab index and active value
on the tabset.

Closes #5713
Closes #5827
2016-05-09 16:09:47 -04:00
Adam Gordon 9436b9e7bd docs: add note for datepicker/datepicker-popup
* update the datepicker and datepicker popup docs to let user know
  about using the `uibDateParser` service to convert a date string
  into a `Date` object.

Closes #5880
2016-05-08 07:57:28 -07:00
kwiateusz b2984b0416 docs(tabs): mention that index needs to be number
Closes #5876
2016-05-07 07:53:22 -06:00
Guillaume Royer 5188463f69 fix(datepickerPopup): convert numbers to date before parsing
Closes #5873
Fixes #5871
2016-05-04 09:04:55 -06:00
Wesley Cho a417ec2ab9 fix(datepicker): add intermediary valid date check
- Add intermediary check for whether the date is valid due to an IE quirk of turning a valid date into an invalid date when using the date constructor

Closes #5872
Fixes #5865
2016-05-03 13:15:34 -07:00
Wesley Cho d923df9100 chore(dateparser): port logic from Angular
- Port logic from timezone converter from Angular

Closes #5870
2016-05-03 09:47:07 -07:00
Wesley Cho dd09148893 feat(modal): add resolve values to template
- Expose resolve in template as $resolve for those modals opened with a controller

BREAKING CHANGE: Since this adds support for $resolve being exposed on $scope, it could potentially overwrite any pre-existing usage of it - this is an unlikely scenario, but marked as a breaking change in case this key is being used

Closes #5808
Closes #5857
2016-04-29 12:02:34 -07:00
ruiann 5741be9d5b chore(modal): use $controller to apply controllerAs
- Change to use `$controller` to apply the `controllerAs` feature

Closes #5861
2016-04-29 08:58:49 -07:00
Scotty Waggoner 1611248daa docs(progressbar): fix danger reference
Closes #5854
2016-04-28 08:35:25 -07:00
Bohdan L e51373d84b chore(readme): fix link
Closes #5852
2016-04-28 07:40:43 -07:00
Perry Hoffman 4c40d9db7f feat(accordion): add alternative attribute support
- Add support for alternative attributes

Closes #5834
Closes #5839
2016-04-27 21:24:56 -07:00
crhistian26 f88067a04f docs(accordion): fix typo
Closes #5838
2016-04-24 21:32:27 -07:00
Wesley Cho 0137179ee6 docs(accordion): add example with custom header
- Add an example with custom header template

Closes #5831
Fixes #5829
2016-04-21 09:53:53 -07:00
Dolev Dori 2b48259a35 fix(dropdown): align position with vertical scrollbar
- Correctly aligns the dropdown when a vertical scrollbar is present

Closes #5830
Fixes #4317
2016-04-20 07:38:04 -07:00
Karl Sieburg 241fea8355 feat(tabs): pass the selected index to onDeselect
-  Add the index of the tab the user attempted to open to the onDeselect call
Closes #5820
Closes #5821
2016-04-19 13:24:01 -07:00
bryanbak f0e661c7c0 docs(tabs): get rid of 'active' option on uib-tab directive
Update demo to remove deprecated active attribute

Closes #5809
2016-04-14 13:31:27 -07:00
Wesley Cho 78db2cefe7 chore(release): starting 1.3.3 2016-04-14 08:32:07 -07:00
99 changed files with 1658 additions and 849 deletions
+26
View File
@@ -1,3 +1,29 @@
<a name="1.3.3"></a>
## [1.3.3](https://github.com/angular-ui/bootstrap/compare/1.3.2...v1.3.3) (2016-05-23)
### Bug Fixes
* **datepicker:** add intermediary valid date check([a417ec2](https://github.com/angular-ui/bootstrap/commit/a417ec2)), closes [#5872](https://github.com/angular-ui/bootstrap/issues/5872) [#5865](https://github.com/angular-ui/bootstrap/issues/5865)
* **datepickerPopup:** convert numbers to date before parsing([5188463](https://github.com/angular-ui/bootstrap/commit/5188463)), closes [#5873](https://github.com/angular-ui/bootstrap/issues/5873) [#5871](https://github.com/angular-ui/bootstrap/issues/5871)
* **dropdown:** align position with vertical scrollbar([2b48259](https://github.com/angular-ui/bootstrap/commit/2b48259)), closes [#5830](https://github.com/angular-ui/bootstrap/issues/5830) [#4317](https://github.com/angular-ui/bootstrap/issues/4317)
### Features
* **accordion:** add alternative attribute support([4c40d9d](https://github.com/angular-ui/bootstrap/commit/4c40d9d)), closes [#5834](https://github.com/angular-ui/bootstrap/issues/5834) [#5839](https://github.com/angular-ui/bootstrap/issues/5839)
* **modal:** add resolve values to template([dd09148](https://github.com/angular-ui/bootstrap/commit/dd09148)), closes [#5808](https://github.com/angular-ui/bootstrap/issues/5808) [#5857](https://github.com/angular-ui/bootstrap/issues/5857)
* **tab:** allow strings for index([26d3995](https://github.com/angular-ui/bootstrap/commit/26d3995)), closes [#5713](https://github.com/angular-ui/bootstrap/issues/5713) [#5827](https://github.com/angular-ui/bootstrap/issues/5827)
* **tabs:** pass the selected index to onDeselect([241fea8](https://github.com/angular-ui/bootstrap/commit/241fea8)), closes [#5820](https://github.com/angular-ui/bootstrap/issues/5820) [#5821](https://github.com/angular-ui/bootstrap/issues/5821)
* **typeahead:** Add support for `should-select`([2ffb86f](https://github.com/angular-ui/bootstrap/commit/2ffb86f)), closes [#5671](https://github.com/angular-ui/bootstrap/issues/5671) [#5794](https://github.com/angular-ui/bootstrap/issues/5794)
### BREAKING CHANGES
* modal: Since this adds support for $resolve being exposed on $scope, it could potentially overwrite any pre-existing usage of it - this is an unlikely scenario, but marked as a breaking change in case this key is being used
<a name="1.3.2"></a>
## [1.3.2](https://github.com/angular-ui/bootstrap/compare/1.3.1...v1.3.2) (2016-04-14)
+1 -1
View File
@@ -9,7 +9,7 @@ module.exports = function(grunt) {
grunt.util.linefeed = '\n';
grunt.initConfig({
ngversion: '1.5.3',
ngversion: '1.5.5',
bsversion: '3.3.6',
modules: [],//to be filled in by build task
pkg: grunt.file.readJSON('package.json'),
+8 -3
View File
@@ -13,7 +13,7 @@
- [NuGet](#install-with-nuget)
- [Custom](#custom-build)
- [Manual](#manual-download)
- [Webpack](#webpack)
- [Webpack / JSPM](#webpack--jspm)
- [Support](#support)
- [FAQ](#faq)
- [Code of Conduct](#code-of-conduct)
@@ -27,7 +27,7 @@
# Demo
Do you want to see directives in action? Visit http://angular-ui.github.io/bootstrap/!
Do you want to see directives in action? Visit https://angular-ui.github.io/bootstrap/!
# Angular 2
@@ -73,7 +73,7 @@ PM> Install-Package Angular.UI.Bootstrap
#### Custom build
Head over to http://angular-ui.github.io/bootstrap/ and hit the *Custom build* button to create your own custom UI Bootstrap build, just the way you like it.
Head over to https://angular-ui.github.io/bootstrap/ and hit the *Custom build* button to create your own custom UI Bootstrap build, just the way you like it.
#### Manual download
@@ -118,6 +118,7 @@ If you would prefer not to load your css through your JavaScript file loader/bun
* datepicker
* datepickerPopup
* dropdown
* modal
* popover
* position
* timepicker
@@ -133,6 +134,10 @@ import typeahead from 'angular-ui-bootstrap/src/typeahead/index-nocss.js';
angular.module('myModule', [accordion, datepicker]);
```
# Versioning
Pre-2.0.0 does not follow a particular versioning system. 2.0.0 and onwards follows [semantic versioning](http://semver.org/). All release changes can be viewed on our [changelog](CHANGELOG.md).
# Support
## FAQ
+4 -4
View File
@@ -1,7 +1,7 @@
{
"author": "https://github.com/angular-ui/bootstrap/graphs/contributors",
"name": "angular-ui-bootstrap",
"version": "1.3.2",
"version": "2.0.0",
"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.3",
"angular-mocks": "1.5.3",
"angular-sanitize": "1.5.3",
"angular": "1.5.5",
"angular-mocks": "1.5.5",
"angular-sanitize": "1.5.5",
"grunt": "^0.4.5",
"grunt-contrib-concat": "^1.0.0",
"grunt-contrib-copy": "^1.0.0",
+14 -3
View File
@@ -1,4 +1,4 @@
angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse', 'ui.bootstrap.tabindex'])
.constant('uibAccordionConfig', {
closeOthers: true
@@ -58,7 +58,7 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
return {
require: '^uibAccordion', // We need this directive to be inside an accordion
transclude: true, // It transcludes the contents of the directive into the template
replace: true, // The element containing the directive will be replaced with the template
restrict: 'A',
templateUrl: function(element, attrs) {
return attrs.templateUrl || 'uib/template/accordion/accordion-group.html';
},
@@ -74,6 +74,7 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
};
},
link: function(scope, element, attrs, accordionCtrl) {
element.addClass('panel');
accordionCtrl.addGroup(scope);
scope.openClass = attrs.openClass || 'panel-open';
@@ -124,11 +125,21 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
link: function(scope, element, attrs, controller) {
scope.$watch(function() { return controller[attrs.uibAccordionTransclude]; }, function(heading) {
if (heading) {
var elem = angular.element(element[0].querySelector('[uib-accordion-header]'));
var elem = angular.element(element[0].querySelector(getHeaderSelectors()));
elem.html('');
elem.append(heading);
}
});
}
};
function getHeaderSelectors() {
return 'uib-accordion-header,' +
'data-uib-accordion-header,' +
'x-uib-accordion-header,' +
'uib\\:accordion-header,' +
'[uib-accordion-header],' +
'[data-uib-accordion-header],' +
'[x-uib-accordion-header]';
}
});
+29 -22
View File
@@ -1,15 +1,16 @@
<div ng-controller="AccordionDemoCtrl">
<script type="text/ng-template" id="group-template.html">
<div class="panel {{panelClass || 'panel-default'}}">
<div class="panel-heading">
<h4 class="panel-title" style="color:#fa39c3">
<a href tabindex="0" class="accordion-toggle" ng-click="toggleOpen()" uib-accordion-transclude="heading"><span
ng-class="{'text-muted': isDisabled}">{{heading}}</span></a>
</h4>
</div>
<div class="panel-collapse collapse" uib-collapse="!isOpen">
<div class="panel-body" style="text-align: right" ng-transclude></div>
</div>
<div class="panel-heading">
<h4 class="panel-title" style="color:#fa39c3">
<a href tabindex="0" class="accordion-toggle" ng-click="toggleOpen()" uib-accordion-transclude="heading">
<span uib-accordion-header ng-class="{'text-muted': isDisabled}">
{{heading}}
</span>
</a>
</h4>
</div>
<div class="panel-collapse collapse" uib-collapse="!isOpen">
<div class="panel-body" style="text-align: right" ng-transclude></div>
</div>
</script>
@@ -25,29 +26,35 @@
</label>
</div>
<uib-accordion close-others="oneAtATime">
<uib-accordion-group heading="Static Header, initially expanded" is-open="status.isFirstOpen" is-disabled="status.isFirstDisabled">
<div uib-accordion-group class="panel-default" heading="Static Header, initially expanded" is-open="status.isFirstOpen" is-disabled="status.isFirstDisabled">
This content is straight in the template.
</uib-accordion-group>
<uib-accordion-group heading="{{group.title}}" ng-repeat="group in groups">
</div>
<div uib-accordion-group class="panel-default" heading="{{group.title}}" ng-repeat="group in groups">
{{group.content}}
</uib-accordion-group>
<uib-accordion-group heading="Dynamic Body Content">
</div>
<div uib-accordion-group class="panel-default" heading="Dynamic Body Content">
<p>The body of the uib-accordion group grows to fit the contents</p>
<button type="button" class="btn btn-default btn-sm" ng-click="addItem()">Add Item</button>
<div ng-repeat="item in items">{{item}}</div>
</uib-accordion-group>
<uib-accordion-group heading="Custom template" template-url="group-template.html">
</div>
<div uib-accordion-group class="panel-default" heading="Custom template" template-url="group-template.html">
Hello
</uib-accordion-group>
<uib-accordion-group heading="Delete account" panel-class="panel-danger">
</div>
<div uib-accordion-group class="panel-default" is-open="status.isCustomHeaderOpen" template-url="group-template.html">
<uib-accordion-heading>
Custom template with custom header template <i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': status.isCustomHeaderOpen, 'glyphicon-chevron-right': !status.isCustomHeaderOpen}"></i>
</uib-accordion-heading>
World
</div>
<div uib-accordion-group class="panel-danger" heading="Delete account">
<p>Please, to delete your account, click the button below</p>
<button class="btn btn-danger">Delete</button>
</uib-accordion-group>
<uib-accordion-group is-open="status.open">
</div>
<div uib-accordion-group class="panel-default" is-open="status.open">
<uib-accordion-heading>
I can have markup, too! <i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': status.open, 'glyphicon-chevron-right': !status.open}"></i>
</uib-accordion-heading>
This is just some content to illustrate fancy headings.
</uib-accordion-group>
</div>
</uib-accordion>
</div>
+1
View File
@@ -20,6 +20,7 @@ angular.module('ui.bootstrap.demo').controller('AccordionDemoCtrl', function ($s
};
$scope.status = {
isCustomHeaderOpen: false,
isFirstOpen: true,
isFirstDisabled: false
};
+1 -6
View File
@@ -32,11 +32,6 @@ The body of each accordion group is transcluded into the body of the collapsible
_(Default: `false`)_ -
Whether accordion group is open or closed.
* `panel-class`
<i class="glyphicon glyphicon-eye-open"></i>
_(Default: `panel-default`)_ -
Add ability to use Bootstrap's contextual panel classes (panel-primary, panel-success, panel-info, etc...) or your own. This must be a string.
* `template-url`
_(Default: `uib/template/accordion/accordion-group.html`)_ -
Add the ability to override the template used on the component.
@@ -49,6 +44,6 @@ If you're using a custom template for the `uib-accordion-group`, you'll need to
### Known issues
To use clickable elements within the accordion, you have override the accordion-group template to use div elements instead of anchor elements, and add `cursor: pointer` in your CSS. This is due to browsers interpreting anchor elements as the target of any click event, which triggers routing when certain elements such as buttons are nested inside the anchor element.
To use clickable elements within the accordion, you have to override the accordion-group template to use div elements instead of anchor elements, and add `cursor: pointer` in your CSS. This is due to browsers interpreting anchor elements as the target of any click event, which triggers routing when certain elements such as buttons are nested inside the anchor element.
If custom classes on the accordion-group element are desired, one needs to either modify the template to remove the `ng-class` usage in the accordion-group template and use ng-class on the accordion-group element (not recommended), or use an interpolated expression in the class attribute, i.e. `<uib-accordion-group class="{{customClass()}}"></uib-accordion-group>`.
+1
View File
@@ -1,4 +1,5 @@
require('../collapse');
require('../tabindex');
require('../../template/accordion/accordion-group.html.js');
require('../../template/accordion/accordion.html.js');
require('./accordion');
+30 -54
View File
@@ -176,12 +176,12 @@ describe('uib-accordion', function() {
var tpl =
'<uib-accordion>' +
'<uib-accordion-group heading="title 1" template-url="foo/bar.html"></uib-accordion-group>' +
'<div uib-accordion-group heading="title 1" template-url="foo/bar.html"></div>' +
'</uib-accordion>';
element = $compile(tpl)(scope);
scope.$digest();
expect(element.find('[template-url]').html()).toBe('baz');
expect(element.find('[template-url]').html()).toBe('<div>baz</div>');
}));
describe('with static panels', function() {
@@ -189,8 +189,8 @@ describe('uib-accordion', function() {
spyOn(Math, 'random').and.returnValue(0.1);
var tpl =
'<uib-accordion>' +
'<uib-accordion-group heading="title 1">Content 1</uib-accordion-group>' +
'<uib-accordion-group heading="title 2">Content 2</uib-accordion-group>' +
'<div uib-accordion-group heading="title 1">Content 1</div>' +
'<div uib-accordion-group heading="title 2">Content 2</div>' +
'</uib-accordion>';
element = angular.element(tpl);
$compile(element)(scope);
@@ -288,8 +288,8 @@ describe('uib-accordion', function() {
beforeEach(function() {
var tpl =
'<uib-accordion>' +
'<uib-accordion-group heading="title 1" open-class="custom-open-class">Content 1</uib-accordion-group>' +
'<uib-accordion-group heading="title 2" open-class="custom-open-class">Content 2</uib-accordion-group>' +
'<div uib-accordion-group heading="title 1" open-class="custom-open-class">Content 1</div>' +
'<div uib-accordion-group heading="title 2" open-class="custom-open-class">Content 2</div>' +
'</uib-accordion>';
element = angular.element(tpl);
$compile(element)(scope);
@@ -318,7 +318,7 @@ describe('uib-accordion', function() {
beforeEach(function() {
var tpl =
'<uib-accordion>' +
'<uib-accordion-group ng-repeat="group in groups" heading="{{group.name}}">{{group.content}}</uib-accordion-group>' +
'<div uib-accordion-group ng-repeat="group in groups" heading="{{group.name}}">{{group.content}}</div>' +
'</uib-accordion>';
element = angular.element(tpl);
model = [
@@ -363,8 +363,8 @@ describe('uib-accordion', function() {
beforeEach(function() {
var tpl =
'<uib-accordion>' +
'<uib-accordion-group heading="title 1" is-open="open.first">Content 1</uib-accordion-group>' +
'<uib-accordion-group heading="title 2" is-open="open.second">Content 2</uib-accordion-group>' +
'<div uib-accordion-group heading="title 1" is-open="open.first">Content 1</div>' +
'<div uib-accordion-group heading="title 2" is-open="open.second">Content 2</div>' +
'</uib-accordion>';
element = angular.element(tpl);
scope.open = { first: false, second: true };
@@ -393,8 +393,8 @@ describe('uib-accordion', function() {
beforeEach(function() {
var tpl =
'<uib-accordion>' +
'<uib-accordion-group heading="title 1" is-open="open1"><div ng-repeat="item in items">{{item}}</div></uib-accordion-group>' +
'<uib-accordion-group heading="title 2" is-open="open2">Static content</uib-accordion-group>' +
'<div uib-accordion-group heading="title 1" is-open="open1"><div ng-repeat="item in items">{{item}}</div></div>' +
'<div uib-accordion-group heading="title 2" is-open="open2">Static content</div>' +
'</uib-accordion>';
element = angular.element(tpl);
scope.items = ['Item 1', 'Item 2', 'Item 3'];
@@ -421,7 +421,7 @@ describe('uib-accordion', function() {
beforeEach(function() {
var tpl =
'<uib-accordion>' +
'<uib-accordion-group ng-repeat="group in groups" heading="{{group.name}}" is-open="group.open">{{group.content}}</uib-accordion-group>' +
'<div uib-accordion-group ng-repeat="group in groups" heading="{{group.name}}" is-open="group.open">{{group.content}}</div>' +
'</uib-accordion>';
element = angular.element(tpl);
scope.groups = [
@@ -456,7 +456,7 @@ describe('uib-accordion', function() {
beforeEach(function() {
var tpl =
'<uib-accordion>' +
'<uib-accordion-group ng-repeat="group in groups" heading="{{group.name}}" is-open="group.open" class="testClass">{{group.content}}</uib-accordion-group>' +
'<div uib-accordion-group ng-repeat="group in groups" heading="{{group.name}}" is-open="group.open" class="testClass">{{group.content}}</div>' +
'</uib-accordion>';
element = angular.element(tpl);
scope.groups = [
@@ -480,7 +480,7 @@ describe('uib-accordion', function() {
beforeEach(function() {
var tpl =
'<uib-accordion>' +
'<uib-accordion-group heading="title 1" is-disabled="disabled">Content 1</uib-accordion-group>' +
'<div uib-accordion-group heading="title 1" is-disabled="disabled">Content 1</div>' +
'</uib-accordion>';
element = angular.element(tpl);
scope.disabled = true;
@@ -519,10 +519,10 @@ describe('uib-accordion', function() {
function isDisabledStyleCheck() {
var tpl =
'<uib-accordion ng-init="a = [1,2,3]">' +
'<uib-accordion-group heading="I get overridden" is-disabled="true">' +
'<div uib-accordion-group heading="I get overridden" is-disabled="true">' +
'<uib-accordion-heading>Heading Element <span ng-repeat="x in a">{{x}}</span> </uib-accordion-heading>' +
'Body' +
'</uib-accordion-group>' +
'</div>' +
'</uib-accordion>';
scope.disabled = true;
element = $compile(tpl)(scope);
@@ -536,10 +536,10 @@ describe('uib-accordion', function() {
beforeEach(function() {
var tpl =
'<uib-accordion ng-init="a = [1,2,3]">' +
'<uib-accordion-group heading="I get overridden">' +
'<div uib-accordion-group heading="I get overridden">' +
'<uib-accordion-heading>Heading Element <span ng-repeat="x in a">{{x}}</span> </uib-accordion-heading>' +
'Body' +
'</uib-accordion-group>' +
'</div>' +
'</uib-accordion>';
element = $compile(tpl)(scope);
scope.$digest();
@@ -565,10 +565,10 @@ describe('uib-accordion', function() {
beforeEach(function() {
var tpl =
'<uib-accordion ng-init="a = [1,2,3]">' +
'<uib-accordion-group heading="I get overridden">' +
'<div uib-accordion-group heading="I get overridden">' +
'<div uib-accordion-heading>Heading Element <span ng-repeat="x in a">{{x}}</span> </div>' +
'Body' +
'</uib-accordion-group>' +
'</div>' +
'</uib-accordion>';
element = $compile(tpl)(scope);
scope.$digest();
@@ -588,7 +588,7 @@ describe('uib-accordion', function() {
describe('uib-accordion-heading, with repeating uib-accordion-groups', function() {
it('should clone the uib-accordion-heading for each group', function() {
element = $compile('<uib-accordion><uib-accordion-group ng-repeat="x in [1,2,3]"><uib-accordion-heading>{{x}}</uib-accordion-heading></uib-accordion-group></uib-accordion>')(scope);
element = $compile('<uib-accordion><div uib-accordion-group ng-repeat="x in [1,2,3]"><uib-accordion-heading>{{x}}</uib-accordion-heading></div></uib-accordion>')(scope);
scope.$digest();
groups = element.find('.panel');
expect(groups.length).toBe(3);
@@ -600,7 +600,7 @@ describe('uib-accordion', function() {
describe('uib-accordion-heading attribute, with repeating uib-accordion-groups', function() {
it('should clone the uib-accordion-heading for each group', function() {
element = $compile('<uib-accordion><uib-accordion-group ng-repeat="x in [1,2,3]"><div uib-accordion-heading>{{x}}</div></uib-accordion-group></uib-accordion>')(scope);
element = $compile('<uib-accordion><div uib-accordion-group ng-repeat="x in [1,2,3]"><div uib-accordion-heading>{{x}}</div></div></uib-accordion>')(scope);
scope.$digest();
groups = element.find('.panel');
expect(groups.length).toBe(3);
@@ -610,39 +610,15 @@ describe('uib-accordion', function() {
});
});
describe('uib-accordion group panel class', function() {
it('should use the default value when panel class is falsy - #3968', function() {
element = $compile('<uib-accordion><uib-accordion-group heading="Heading">Content</uib-accordion-group></uib-accordion>')(scope);
scope.$digest();
groups = element.find('.panel');
expect(groups.eq(0)).toHaveClass('panel-default');
describe('uib-accordion-heading attribute, with custom template', function() {
it('should transclude heading to a template using data-uib-accordion-header', inject(function($templateCache) {
$templateCache.put('foo/bar.html', '<div class="panel"><a uib-accordion-transclude="heading" class="accordion-toggle"><span data-uib-accordion-header></span></a><div ng-transclude></div></div>');
element = $compile('<uib-accordion><uib-accordion-group heading="Heading" panel-class="">Content</uib-accordion-group></uib-accordion>')(scope);
scope.$digest();
groups = element.find('.panel');
expect(groups.eq(0)).toHaveClass('panel-default');
});
it('should use the specified value when not falsy - #3968', function() {
element = $compile('<uib-accordion><uib-accordion-group heading="Heading" panel-class="custom-class">Content</uib-accordion-group></uib-accordion>')(scope);
scope.$digest();
groups = element.find('.panel');
expect(groups.eq(0)).toHaveClass('custom-class');
expect(groups.eq(0)).not.toHaveClass('panel-default');
});
it('should change class if panel-class is changed', function() {
element = $compile('<uib-accordion><uib-accordion-group heading="Heading" panel-class="{{panelClass}}">Content</uib-accordion-group></uib-accordion>')(scope);
scope.panelClass = 'custom-class';
scope.$digest();
groups = element.find('.panel');
expect(groups.eq(0)).toHaveClass('custom-class');
scope.panelClass = 'different-class';
scope.$digest();
expect(groups.eq(0)).toHaveClass('different-class');
expect(groups.eq(0)).not.toHaveClass('custom-class');
});
element = $compile('<uib-accordion><div uib-accordion-group template-url="foo/bar.html"><uib-accordion-heading>baz</uib-accordion-heading></div></uib-accordion>')(scope);
scope.$digest();
groups = element.find('.panel');
expect(findGroupLink(0).text()).toBe('baz');
}));
});
});
});
+7 -3
View File
@@ -1,7 +1,12 @@
angular.module('ui.bootstrap.alert', [])
.controller('UibAlertController', ['$scope', '$attrs', '$interpolate', '$timeout', function($scope, $attrs, $interpolate, $timeout) {
.controller('UibAlertController', ['$scope', '$element', '$attrs', '$interpolate', '$timeout', function($scope, $element, $attrs, $interpolate, $timeout) {
$scope.closeable = !!$attrs.close;
$element.addClass('alert');
$attrs.$set('role', 'alert');
if ($scope.closeable) {
$element.addClass('alert-dismissible');
}
var dismissOnTimeout = angular.isDefined($attrs.dismissOnTimeout) ?
$interpolate($attrs.dismissOnTimeout)($scope.$parent) : null;
@@ -17,13 +22,12 @@ angular.module('ui.bootstrap.alert', [])
return {
controller: 'UibAlertController',
controllerAs: 'alert',
restrict: 'A',
templateUrl: function(element, attrs) {
return attrs.templateUrl || 'uib/template/alert/alert.html';
},
transclude: true,
replace: true,
scope: {
type: '@',
close: '&'
}
};
+3 -5
View File
@@ -1,11 +1,9 @@
<div ng-controller="AlertDemoCtrl">
<script type="text/ng-template" id="alert.html">
<div class="alert" style="background-color:#fa39c3;color:white" role="alert">
<div ng-transclude></div>
</div>
<div ng-transclude></div>
</script>
<uib-alert ng-repeat="alert in alerts" type="{{alert.type}}" close="closeAlert($index)">{{alert.msg}}</uib-alert>
<uib-alert template-url="alert.html">A happy alert!</uib-alert>
<div uib-alert ng-repeat="alert in alerts" ng-class="'alert-' + (alert.type || 'warning')" close="closeAlert($index)">{{alert.msg}}</div>
<div uib-alert template-url="alert.html" style="background-color:#fa39c3;color:white">A happy alert!</div>
<button type="button" class='btn btn-default' ng-click="addAlert()">Add Alert</button>
</div>
+2 -6
View File
@@ -5,15 +5,11 @@ This directive can be used both to generate alerts from static and dynamic model
* `close()`
<small class="badge">$</small> -
A callback function that gets fired when an `alert` is closed. If the attribute exists, a close button is displayed as well.
* `dismiss-on-timeout`
_(Default: `none`)_ -
Takes the number of milliseconds that specify the timeout duration, after which the alert will be closed. This attribute requires the presence of the `close` attribute.
* `template-url`
_(Default: `uib/template/alert/alert.html`)_ -
Add the ability to override the template used in the component.
* `type`
_(Default: `warning`)_ -
Defines the type of the alert. Go to [bootstrap page](http://getbootstrap.com/components/#alerts) to see the type of alerts available.
+10 -33
View File
@@ -12,9 +12,10 @@ describe('uib-alert', function() {
element = angular.element(
'<div>' +
'<uib-alert ng-repeat="alert in alerts" type="{{alert.type}}"' +
'<div uib-alert ng-repeat="alert in alerts" ' +
'ng-class="\'alert-\' + (alert.type || \'warning\')" ' +
'close="removeAlert($index)">{{alert.msg}}' +
'</uib-alert>' +
'</div>' +
'</div>');
scope.alerts = [
@@ -41,7 +42,7 @@ describe('uib-alert', function() {
it('should expose the controller to the view', function() {
$templateCache.put('uib/template/alert/alert.html', '<div>{{alert.text}}</div>');
element = $compile('<uib-alert></uib-alert>')(scope);
element = $compile('<div uib-alert></div>')(scope);
scope.$digest();
var ctrl = element.controller('uib-alert');
@@ -50,16 +51,16 @@ describe('uib-alert', function() {
ctrl.text = 'foo';
scope.$digest();
expect(element.html()).toBe('foo');
expect(element.html()).toBe('<div class="ng-binding">foo</div>');
});
it('should support custom templates', function() {
$templateCache.put('foo/bar.html', '<div>baz</div>');
element = $compile('<uib-alert template-url="foo/bar.html"></uib-alert>')(scope);
element = $compile('<div uib-alert template-url="foo/bar.html"></div>')(scope);
scope.$digest();
expect(element.html()).toBe('baz');
expect(element.html()).toBe('<div>baz</div>');
});
it('should generate alerts using ng-repeat', function() {
@@ -67,23 +68,6 @@ describe('uib-alert', function() {
expect(alerts.length).toEqual(3);
});
it('should use correct classes for different alert types', function() {
var alerts = createAlerts();
expect(alerts.eq(0)).toHaveClass('alert-success');
expect(alerts.eq(1)).toHaveClass('alert-error');
expect(alerts.eq(2)).toHaveClass('alert-warning');
});
it('should respect alert type binding', function() {
var alerts = createAlerts();
expect(alerts.eq(0)).toHaveClass('alert-success');
scope.alerts[0].type = 'error';
scope.$digest();
expect(alerts.eq(0)).toHaveClass('alert-error');
});
it('should show the alert content', function() {
var alerts = createAlerts();
@@ -115,22 +99,15 @@ describe('uib-alert', function() {
});
it('should not show close button and have the dismissible class if no close callback specified', function() {
element = $compile('<uib-alert>No close</uib-alert>')(scope);
element = $compile('<div uib-alert>No close</div>')(scope);
scope.$digest();
expect(findCloseButton(0)).toBeHidden();
expect(element).not.toHaveClass('alert-dismissible');
});
it('should be possible to add additional classes for alert', function() {
var element = $compile('<uib-alert class="alert-block" type="info">Default alert!</uib-alert>')(scope);
scope.$digest();
expect(element).toHaveClass('alert-block');
expect(element).toHaveClass('alert-info');
});
it('should close automatically if dismiss-on-timeout is defined on the element', function() {
scope.removeAlert = jasmine.createSpy();
$compile('<uib-alert close="removeAlert()" dismiss-on-timeout="500">Default alert!</uib-alert>')(scope);
$compile('<div uib-alert close="removeAlert()" dismiss-on-timeout="500">Default alert!</div>')(scope);
scope.$digest();
$timeout.flush();
@@ -140,7 +117,7 @@ describe('uib-alert', function() {
it('should not close immediately with a dynamic dismiss-on-timeout', function() {
scope.removeAlert = jasmine.createSpy();
scope.dismissTime = 500;
$compile('<uib-alert close="removeAlert()" dismiss-on-timeout="{{dismissTime}}">Default alert!</uib-alert>')(scope);
$compile('<div uib-alert close="removeAlert()" dismiss-on-timeout="{{dismissTime}}">Default alert!</div>')(scope);
scope.$digest();
$timeout.flush(100);
+4
View File
@@ -44,3 +44,7 @@ With the buttons directive, we can make a group of buttons behave like a set of
* `toggleEvent`
_(Default: `click`)_ -
Event used to toggle the buttons.
### Known issues
To use tooltips or popovers on elements within a `btn-group`, set the tooltip/popover `appendToBody` option to `true`. This is due to Bootstrap CSS styling. See [here](http://getbootstrap.com/components/#btn-groups) for more information.
+13 -4
View File
@@ -8,6 +8,7 @@ angular.module('ui.bootstrap.carousel', [])
currentInterval, isPlaying, bufferedTransitions = [];
var destroyed = false;
$element.addClass('carousel');
self.addSlide = function(slide, element) {
slides.push({
@@ -145,6 +146,9 @@ angular.module('ui.bootstrap.carousel', [])
}
};
$element.on('mouseenter', $scope.pause);
$element.on('mouseleave', $scope.play);
$scope.$on('$destroy', function() {
destroyed = true;
resetTimer();
@@ -280,9 +284,9 @@ angular.module('ui.bootstrap.carousel', [])
.directive('uibCarousel', function() {
return {
transclude: true,
replace: true,
controller: 'UibCarouselController',
controllerAs: 'carousel',
restrict: 'A',
templateUrl: function(element, attrs) {
return attrs.templateUrl || 'uib/template/carousel/carousel.html';
},
@@ -296,11 +300,11 @@ angular.module('ui.bootstrap.carousel', [])
};
})
.directive('uibSlide', function() {
.directive('uibSlide', ['$animate', function($animate) {
return {
require: '^uibCarousel',
restrict: 'A',
transclude: true,
replace: true,
templateUrl: function(element, attrs) {
return attrs.templateUrl || 'uib/template/carousel/slide.html';
},
@@ -309,14 +313,19 @@ angular.module('ui.bootstrap.carousel', [])
index: '=?'
},
link: function (scope, element, attrs, carouselCtrl) {
element.addClass('item');
carouselCtrl.addSlide(scope, element);
//when the scope is destroyed then remove the slide from the current slides array
scope.$on('$destroy', function() {
carouselCtrl.removeSlide(scope);
});
scope.$watch('active', function(active) {
$animate[active ? 'addClass' : 'removeClass'](element, 'active');
});
}
};
})
}])
.animation('.item', ['$animateCss',
function($animateCss) {
+4 -4
View File
@@ -1,14 +1,14 @@
<div ng-controller="CarouselDemoCtrl">
<div style="height: 305px">
<uib-carousel active="active" interval="myInterval" no-wrap="noWrapSlides">
<uib-slide ng-repeat="slide in slides track by slide.id" index="slide.id">
<div uib-carousel active="active" interval="myInterval" no-wrap="noWrapSlides">
<div uib-slide ng-repeat="slide in slides track by slide.id" index="slide.id">
<img ng-src="{{slide.image}}" style="margin:auto;">
<div class="carousel-caption">
<h4>Slide {{slide.id}}</h4>
<p>{{slide.text}}</p>
</div>
</uib-slide>
</uib-carousel>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
+37 -63
View File
@@ -1,17 +1,5 @@
describe('carousel', function() {
beforeEach(module('ui.bootstrap.carousel', function($compileProvider, $provide) {
angular.forEach(['ngSwipeLeft', 'ngSwipeRight'], makeMock);
function makeMock(name) {
$provide.value(name + 'Directive', []); //remove existing directive if it exists
$compileProvider.directive(name, function() {
return function(scope, element, attr) {
element.on(name, function() {
scope.$apply(attr[name]);
});
};
});
}
}));
beforeEach(module('ui.bootstrap.carousel'));
beforeEach(module('ngAnimateMock'));
beforeEach(module('uib/template/carousel/carousel.html', 'uib/template/carousel/slide.html'));
@@ -36,11 +24,11 @@ describe('carousel', function() {
{content: 'three', index: 2}
];
elm = $compile(
'<uib-carousel active="active" interval="interval" no-transition="true" no-pause="nopause">' +
'<uib-slide ng-repeat="slide in slides track by slide.index" index="slide.index">' +
'<div uib-carousel active="active" interval="interval" no-transition="true" no-pause="nopause">' +
'<div uib-slide ng-repeat="slide in slides track by slide.index" index="slide.index">' +
'{{slide.content}}' +
'</uib-slide>' +
'</uib-carousel>'
'</div>' +
'</div>'
)(scope);
scope.interval = 5000;
scope.nopause = undefined;
@@ -60,19 +48,19 @@ describe('carousel', function() {
it('should allow overriding of the carousel template', function() {
$templateCache.put('foo/bar.html', '<div>foo</div>');
elm = $compile('<uib-carousel template-url="foo/bar.html"></uib-carousel>')(scope);
elm = $compile('<div uib-carousel template-url="foo/bar.html"></div>')(scope);
$rootScope.$digest();
expect(elm.html()).toBe('foo');
expect(elm.html()).toBe('<div>foo</div>');
});
it('should allow overriding of the slide template', function() {
$templateCache.put('foo/bar.html', '<div class="slide">bar</div>');
elm = $compile(
'<uib-carousel interval="interval" no-transition="true" no-pause="nopause">' +
'<uib-slide template-url="foo/bar.html"></uib-slide>' +
'</uib-carousel>'
'<div uib-carousel interval="interval" no-transition="true" no-pause="nopause">' +
'<div uib-slide template-url="foo/bar.html"></div>' +
'</div>'
)(scope);
$rootScope.$digest();
@@ -101,11 +89,11 @@ describe('carousel', function() {
it('should stop cycling slides forward when noWrap is truthy', function () {
elm = $compile(
'<uib-carousel active="active" interval="interval" no-wrap="noWrap">' +
'<uib-slide ng-repeat="slide in slides track by slide.index" index="slide.index">' +
'<div uib-carousel active="active" interval="interval" no-wrap="noWrap">' +
'<div uib-slide ng-repeat="slide in slides track by slide.index" index="slide.index">' +
'{{slide.content}}' +
'</uib-slide>' +
'</uib-carousel>'
'</div>' +
'</div>'
)(scope);
scope.noWrap = true;
@@ -124,11 +112,11 @@ describe('carousel', function() {
it('should stop cycling slides backward when noWrap is truthy', function () {
elm = $compile(
'<uib-carousel active="active" interval="interval" no-wrap="noWrap">' +
'<uib-slide ng-repeat="slide in slides track by slide.index" index="slide.index">' +
'<div uib-carousel active="active" interval="interval" no-wrap="noWrap">' +
'<div uib-slide ng-repeat="slide in slides track by slide.index" index="slide.index">' +
'{{slide.content}}' +
'</uib-slide>' +
'</uib-carousel>'
'</div>' +
'</div>'
)(scope);
scope.noWrap = true;
@@ -147,11 +135,11 @@ describe('carousel', function() {
scope.slides = [{active:false,content:'one'}];
scope.$apply();
elm = $compile(
'<uib-carousel active="active" interval="interval" no-transition="true">' +
'<uib-slide ng-repeat="slide in slides" index="$index">' +
'<div uib-carousel active="active" interval="interval" no-transition="true">' +
'<div uib-slide ng-repeat="slide in slides" index="$index">' +
'{{slide.content}}' +
'</uib-slide>' +
'</uib-carousel>'
'</div>' +
'</div>'
)(scope);
var indicators = elm.find('ol.carousel-indicators > li');
expect(indicators.length).toBe(0);
@@ -228,20 +216,6 @@ describe('carousel', function() {
testSlideActive(0);
});
describe('swiping', function() {
it('should go next on swipeLeft', function() {
testSlideActive(0);
elm.triggerHandler('ngSwipeLeft');
testSlideActive(1);
});
it('should go prev on swipeRight', function() {
testSlideActive(0);
elm.triggerHandler('ngSwipeRight');
testSlideActive(2);
});
});
it('should select a slide when clicking on slide indicators', function () {
var indicators = elm.find('ol.carousel-indicators > li');
indicators.eq(1).click();
@@ -269,7 +243,7 @@ describe('carousel', function() {
});
it('should bind the content to slides', function() {
var contents = elm.find('div.item');
var contents = elm.find('div.item [ng-transclude]');
expect(contents.length).toBe(3);
expect(contents.eq(0).text()).toBe('one');
@@ -343,7 +317,7 @@ describe('carousel', function() {
{content:'new3', index: 6}
];
scope.$apply();
var contents = elm.find('div.item');
var contents = elm.find('div.item [ng-transclude]');
expect(contents.length).toBe(3);
expect(contents.eq(0).text()).toBe('new1');
expect(contents.eq(1).text()).toBe('new2');
@@ -441,11 +415,11 @@ describe('carousel', function() {
{content: 'three', id: 2}
];
elm = $compile(
'<uib-carousel active="active" interval="interval" no-transition="true" no-pause="nopause">' +
'<uib-slide ng-repeat="slide in slides | orderBy: \'id\' track by slide.id" index="slide.id">' +
'<div uib-carousel active="active" interval="interval" no-transition="true" no-pause="nopause">' +
'<div uib-slide ng-repeat="slide in slides | orderBy: \'id\' track by slide.id" index="slide.id">' +
'{{slide.content}}' +
'</uib-slide>' +
'</uib-carousel>'
'</div>' +
'</div>'
)(scope);
scope.$apply();
});
@@ -465,7 +439,7 @@ describe('carousel', function() {
scope.slides[1].id = 2;
scope.slides[2].id = 1;
scope.$apply();
var contents = elm.find('div.item');
var contents = elm.find('div.item [ng-transclude]');
expect(contents.length).toBe(3);
expect(contents.eq(0).text()).toBe('three');
expect(contents.eq(1).text()).toBe('two');
@@ -491,7 +465,7 @@ describe('carousel', function() {
scope.slides[2].id = 4;
scope.slides.push({content:'four', id: 5});
scope.$apply();
var contents = elm.find('div.item');
var contents = elm.find('div.item [ng-transclude]');
expect(contents.length).toBe(4);
expect(contents.eq(0).text()).toBe('two');
expect(contents.eq(1).text()).toBe('one');
@@ -503,7 +477,7 @@ describe('carousel', function() {
testSlideActive(1);
scope.slides.splice(1, 1);
scope.$apply();
var contents = elm.find('div.item');
var contents = elm.find('div.item [ng-transclude]');
expect(contents.length).toBe(2);
expect(contents.eq(0).text()).toBe('three');
expect(contents.eq(1).text()).toBe('one');
@@ -583,7 +557,7 @@ describe('carousel', function() {
$templateCache.put('uib/template/carousel/carousel.html', '<div>{{carousel.text}}</div>');
var scope = $rootScope.$new();
var elm = $compile('<uib-carousel interval="bar" no-transition="false" no-pause="true"></uib-carousel>')(scope);
var elm = $compile('<div uib-carousel interval="bar" no-transition="false" no-pause="true"></div>')(scope);
$rootScope.$digest();
var ctrl = elm.controller('uibCarousel');
@@ -593,7 +567,7 @@ describe('carousel', function() {
ctrl.text = 'foo';
$rootScope.$digest();
expect(elm.html()).toBe('foo');
expect(elm.html()).toBe('<div class="ng-binding">foo</div>');
}));
});
@@ -605,11 +579,11 @@ describe('carousel', function() {
{active:false,content:'three'}
];
var elm = $compile(
'<uib-carousel active="active" interval="interval" no-transition="true" no-pause="nopause">' +
'<uib-slide ng-repeat="slide in slides" index="$index" actual="slide">' +
'<div uib-carousel active="active" interval="interval" no-transition="true" no-pause="nopause">' +
'<div uib-slide ng-repeat="slide in slides" index="$index" actual="slide">' +
'{{slide.content}}' +
'</uib-slide>' +
'</uib-carousel>'
'</div>' +
'</div>'
)(scope);
$rootScope.$digest();
+47 -19
View File
@@ -5,16 +5,44 @@ angular.module('ui.bootstrap.collapse', [])
return {
link: function(scope, element, attrs) {
var expandingExpr = $parse(attrs.expanding),
expandedExpr = $parse(attrs.expanded),
collapsingExpr = $parse(attrs.collapsing),
collapsedExpr = $parse(attrs.collapsed);
expandedExpr = $parse(attrs.expanded),
collapsingExpr = $parse(attrs.collapsing),
collapsedExpr = $parse(attrs.collapsed),
horizontal = false,
css = {},
cssTo = {};
if (!scope.$eval(attrs.uibCollapse)) {
element.addClass('in')
.addClass('collapse')
.attr('aria-expanded', true)
.attr('aria-hidden', false)
.css({height: 'auto'});
init();
function init() {
horizontal = !!('horizontal' in attrs);
if (horizontal) {
css = {
width: 'auto',
height: 'inherit'
};
cssTo = {width: '0'};
} else {
css = {
width: 'inherit',
height: 'auto'
};
cssTo = {height: '0'};
}
if (!scope.$eval(attrs.uibCollapse)) {
element.addClass('in')
.addClass('collapse')
.attr('aria-expanded', true)
.attr('aria-hidden', false)
.css(css);
}
}
function getScrollFromElement(element) {
if (horizontal) {
return {width: element.scrollWidth + 'px'};
}
return {height: element.scrollHeight + 'px'};
}
function expand() {
@@ -33,11 +61,11 @@ angular.module('ui.bootstrap.collapse', [])
$animateCss(element, {
addClass: 'in',
easing: 'ease',
to: { height: element[0].scrollHeight + 'px' }
to: getScrollFromElement(element[0])
}).start()['finally'](expandDone);
} else {
$animate.addClass(element, 'in', {
to: { height: element[0].scrollHeight + 'px' }
to: getScrollFromElement(element[0])
}).then(expandDone);
}
});
@@ -46,7 +74,7 @@ angular.module('ui.bootstrap.collapse', [])
function expandDone() {
element.removeClass('collapsing')
.addClass('collapse')
.css({height: 'auto'});
.css(css);
expandedExpr(scope);
}
@@ -58,10 +86,10 @@ angular.module('ui.bootstrap.collapse', [])
$q.resolve(collapsingExpr(scope))
.then(function() {
element
// IMPORTANT: The height must be set before adding "collapsing" class.
// Otherwise, the browser attempts to animate from height 0 (in
// collapsing class) to the given height here.
.css({height: element[0].scrollHeight + 'px'})
// IMPORTANT: The width must be set before adding "collapsing" class.
// Otherwise, the browser attempts to animate from width 0 (in
// collapsing class) to the given width here.
.css(getScrollFromElement(element[0]))
// initially all panel collapse have the collapse class, this removal
// prevents the animation from jumping to collapsed state
.removeClass('collapse')
@@ -72,18 +100,18 @@ angular.module('ui.bootstrap.collapse', [])
if ($animateCss) {
$animateCss(element, {
removeClass: 'in',
to: {height: '0'}
to: cssTo
}).start()['finally'](collapseDone);
} else {
$animate.removeClass(element, 'in', {
to: {height: '0'}
to: cssTo
}).then(collapseDone);
}
});
}
function collapseDone() {
element.css({height: '0'}); // Required so that collapse works when animation is disabled
element.css(cssTo); // Required so that collapse works when animation is disabled
element.removeClass('collapsing')
.addClass('collapse');
collapsedExpr(scope);
+7 -1
View File
@@ -1,7 +1,13 @@
<div ng-controller="CollapseDemoCtrl">
<button type="button" class="btn btn-default" ng-click="isCollapsed = !isCollapsed">Toggle collapse</button>
<button type="button" class="btn btn-default" ng-click="isCollapsed = !isCollapsed">Toggle collapse Vertically</button>
<hr>
<div uib-collapse="isCollapsed">
<div class="well well-lg">Some content</div>
</div>
<button type="button" class="btn btn-default" ng-click="isCollapsedHorizontal = !isCollapsedHorizontal">Toggle collapse Horizontally</button>
<hr>
<div uib-collapse="isCollapsedHorizontal" horizontal>
<div class="well well-lg">Some content</div>
</div>
</div>
+1
View File
@@ -1,3 +1,4 @@
angular.module('ui.bootstrap.demo').controller('CollapseDemoCtrl', function ($scope) {
$scope.isCollapsed = false;
$scope.isCollapsedHorizontal = false;
});
+4
View File
@@ -27,4 +27,8 @@
<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.
@@ -0,0 +1,255 @@
describe('collapse directive', function() {
var elementH, compileFnH, scope, $compile, $animate, $q;
beforeEach(module('ui.bootstrap.collapse'));
beforeEach(module('ngAnimateMock'));
beforeEach(inject(function(_$rootScope_, _$compile_, _$animate_, _$q_) {
scope = _$rootScope_;
$compile = _$compile_;
$animate = _$animate_;
$q = _$q_;
}));
beforeEach(function() {
elementH = angular.element(
'<div uib-collapse="isCollapsed" '
+ 'expanding="expanding()" '
+ 'expanded="expanded()" '
+ 'collapsing="collapsing()" '
+ 'collapsed="collapsed()" '
+ 'horizontal>'
+ 'Some Content</div>');
compileFnH = $compile(elementH);
angular.element(document.body).append(elementH);
});
afterEach(function() {
elementH.remove();
});
function initCallbacks() {
scope.collapsing = jasmine.createSpy('scope.collapsing');
scope.collapsed = jasmine.createSpy('scope.collapsed');
scope.expanding = jasmine.createSpy('scope.expanding');
scope.expanded = jasmine.createSpy('scope.expanded');
}
function assertCallbacks(expected) {
['collapsing', 'collapsed', 'expanding', 'expanded'].forEach(function(cbName) {
if (expected[cbName]) {
expect(scope[cbName]).toHaveBeenCalled();
} else {
expect(scope[cbName]).not.toHaveBeenCalled();
}
});
}
it('should be hidden on initialization if isCollapsed = true', function() {
initCallbacks();
scope.isCollapsed = true;
compileFnH(scope);
scope.$digest();
expect(elementH.width()).toBe(0);
assertCallbacks({ collapsed: true });
});
it('should not trigger any animation on initialization if isCollapsed = true', function() {
var wrapperFn = function() {
$animate.flush();
};
scope.isCollapsed = true;
compileFnH(scope);
scope.$digest();
expect(wrapperFn).toThrowError(/No pending animations ready to be closed or flushed/);
});
it('should collapse if isCollapsed = true on subsequent use', function() {
scope.isCollapsed = false;
compileFnH(scope);
scope.$digest();
initCallbacks();
scope.isCollapsed = true;
scope.$digest();
$animate.flush();
expect(elementH.width()).toBe(0);
assertCallbacks({ collapsing: true, collapsed: true });
});
it('should show after toggled from collapsed', function() {
initCallbacks();
scope.isCollapsed = true;
compileFnH(scope);
scope.$digest();
expect(elementH.width()).toBe(0);
assertCallbacks({ collapsed: true });
scope.collapsed.calls.reset();
scope.isCollapsed = false;
scope.$digest();
$animate.flush();
expect(elementH.width()).not.toBe(0);
assertCallbacks({ expanding: true, expanded: true });
});
it('should not trigger any animation on initialization if isCollapsed = false', function() {
var wrapperFn = function() {
$animate.flush();
};
scope.isCollapsed = false;
compileFnH(scope);
scope.$digest();
expect(wrapperFn).toThrowError(/No pending animations ready to be closed or flushed/);
});
it('should expand if isCollapsed = false on subsequent use', function() {
scope.isCollapsed = false;
compileFnH(scope);
scope.$digest();
scope.isCollapsed = true;
scope.$digest();
$animate.flush();
initCallbacks();
scope.isCollapsed = false;
scope.$digest();
$animate.flush();
expect(elementH.width()).not.toBe(0);
assertCallbacks({ expanding: true, expanded: true });
});
it('should collapse if isCollapsed = true on subsequent uses', function() {
scope.isCollapsed = false;
compileFnH(scope);
scope.$digest();
scope.isCollapsed = true;
scope.$digest();
$animate.flush();
scope.isCollapsed = false;
scope.$digest();
$animate.flush();
initCallbacks();
scope.isCollapsed = true;
scope.$digest();
$animate.flush();
expect(elementH.width()).toBe(0);
assertCallbacks({ collapsing: true, collapsed: true });
});
it('should change aria-expanded attribute', function() {
scope.isCollapsed = false;
compileFnH(scope);
scope.$digest();
expect(elementH.attr('aria-expanded')).toBe('true');
scope.isCollapsed = true;
scope.$digest();
$animate.flush();
expect(elementH.attr('aria-expanded')).toBe('false');
});
it('should change aria-hidden attribute', function() {
scope.isCollapsed = false;
compileFnH(scope);
scope.$digest();
expect(elementH.attr('aria-hidden')).toBe('false');
scope.isCollapsed = true;
scope.$digest();
$animate.flush();
expect(elementH.attr('aria-hidden')).toBe('true');
});
describe('expanding callback returning a promise', function() {
var defer, collapsedWidth;
beforeEach(function() {
defer = $q.defer();
scope.isCollapsed = true;
scope.expanding = function() {
return defer.promise;
};
compileFnH(scope);
scope.$digest();
collapsedWidth = elementH.width();
// set flag to expand ...
scope.isCollapsed = false;
scope.$digest();
// ... shouldn't expand yet ...
expect(elementH.attr('aria-expanded')).not.toBe('true');
expect(elementH.width()).toBe(collapsedWidth);
});
it('should wait for it to resolve before animating', function() {
defer.resolve();
// should now expand
scope.$digest();
$animate.flush();
expect(elementH.attr('aria-expanded')).toBe('true');
expect(elementH.width()).toBeGreaterThan(collapsedWidth);
});
it('should not animate if it rejects', function() {
defer.reject();
// should NOT expand
scope.$digest();
expect(elementH.attr('aria-expanded')).not.toBe('true');
expect(elementH.width()).toBe(collapsedWidth);
});
});
describe('collapsing callback returning a promise', function() {
var defer, expandedWidth;
beforeEach(function() {
defer = $q.defer();
scope.isCollapsed = false;
scope.collapsing = function() {
return defer.promise;
};
compileFnH(scope);
scope.$digest();
expandedWidth = elementH.width();
// set flag to collapse ...
scope.isCollapsed = true;
scope.$digest();
// ... but it shouldn't collapse yet ...
expect(elementH.attr('aria-expanded')).not.toBe('false');
expect(elementH.width()).toBe(expandedWidth);
});
it('should wait for it to resolve before animating', function() {
defer.resolve();
// should now collapse
scope.$digest();
$animate.flush();
expect(elementH.attr('aria-expanded')).toBe('false');
expect(elementH.width()).toBeLessThan(expandedWidth);
});
it('should not animate if it rejects', function() {
defer.reject();
// should NOT collapse
scope.$digest();
expect(elementH.attr('aria-expanded')).not.toBe('false');
expect(elementH.width()).toBe(expandedWidth);
});
});
});
+75 -24
View File
@@ -234,7 +234,7 @@ angular.module('ui.bootstrap.dateparser', [])
this.init();
function createParser(format, func) {
function createParser(format) {
var map = [], regex = format.split('');
// check for literal values
@@ -283,7 +283,7 @@ angular.module('ui.bootstrap.dateparser', [])
map.push({
index: index,
key: data.key,
apply: data[func],
apply: data.apply,
matcher: data.regex
});
}
@@ -295,6 +295,70 @@ angular.module('ui.bootstrap.dateparser', [])
};
}
function createFormatter(format) {
var formatters = [];
var i = 0;
var formatter, literalIdx;
while (i < format.length) {
if (angular.isNumber(literalIdx)) {
if (format.charAt(i) === '\'') {
if (i + 1 >= format.length || format.charAt(i + 1) !== '\'') {
formatters.push(constructLiteralFormatter(format, literalIdx, i));
literalIdx = null;
}
} else if (i === format.length) {
while (literalIdx < format.length) {
formatter = constructFormatterFromIdx(format, literalIdx);
formatters.push(formatter);
literalIdx = formatter.endIdx;
}
}
i++;
continue;
}
if (format.charAt(i) === '\'') {
literalIdx = i;
i++;
continue;
}
formatter = constructFormatterFromIdx(format, i);
formatters.push(formatter.parser);
i = formatter.endIdx;
}
return formatters;
}
function constructLiteralFormatter(format, literalIdx, endIdx) {
return function() {
return format.substr(literalIdx + 1, endIdx - literalIdx - 1);
};
}
function constructFormatterFromIdx(format, i) {
var currentPosStr = format.substr(i);
for (var j = 0; j < formatCodeToRegex.length; j++) {
if (new RegExp('^' + formatCodeToRegex[j].key).test(currentPosStr)) {
var data = formatCodeToRegex[j];
return {
endIdx: i + data.key.length,
parser: data.formatter
};
}
}
return {
endIdx: i + 1,
parser: function() {
return currentPosStr.charAt(0);
}
};
}
this.filter = function(date, format) {
if (!angular.isDate(date) || isNaN(date) || !format) {
return '';
@@ -307,28 +371,13 @@ angular.module('ui.bootstrap.dateparser', [])
}
if (!this.formatters[format]) {
this.formatters[format] = createParser(format, 'formatter');
this.formatters[format] = createFormatter(format);
}
var parser = this.formatters[format],
map = parser.map;
var formatters = this.formatters[format];
var _format = format;
return map.reduce(function(str, mapper, i) {
var match = _format.match(new RegExp('(.*)' + mapper.key));
if (match && angular.isString(match[1])) {
str += match[1];
_format = _format.replace(match[1] + mapper.key, '');
}
var endStr = i === map.length - 1 ? _format : '';
if (mapper.apply) {
return str + mapper.apply.call(null, date) + endStr;
}
return str + endStr;
return formatters.reduce(function(str, formatter) {
return str + formatter(date);
}, '');
};
@@ -442,8 +491,9 @@ angular.module('ui.bootstrap.dateparser', [])
return date && timezone ? convertTimezoneToLocal(date, timezone, true) : date;
}
//https://github.com/angular/angular.js/blob/4daafd3dbe6a80d578f5a31df1bb99c77559543e/src/Angular.js#L1207
//https://github.com/angular/angular.js/blob/622c42169699ec07fc6daaa19fe6d224e5d2f70e/src/Angular.js#L1207
function timezoneToOffset(timezone, fallback) {
timezone = timezone.replace(/:/g, '');
var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
}
@@ -456,7 +506,8 @@ angular.module('ui.bootstrap.dateparser', [])
function convertTimezoneToLocal(date, timezone, reverse) {
reverse = reverse ? -1 : 1;
var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
var dateTimezoneOffset = date.getTimezoneOffset();
var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
}
}]);
+21 -21
View File
@@ -565,27 +565,27 @@ describe('date parser', function() {
});
describe('with value literals', function() {
// describe('filter', function() {
// it('should work with multiple literals', function() {
// expect(dateParser.filter(new Date(2013, 0, 29), 'd \'de\' MMMM \'de\' y')).toEqual('29 de January de 2013');
// });
//
// it('should work with escaped single quote', function() {
// expect(dateParser.filter(new Date(2015, 2, 22, 12), 'd.MMMM.yy h \'o\'\'clock\'')).toEqual('22.March.15 12 o\'clock');
// });
//
// it('should work with only a single quote', function() {
// expect(dateParser.filter(new Date(2015, 2, 22), 'd.MMMM.yy \'\'\'')).toEqual('22.March.15 \'');
// });
//
// it('should work with trailing literal', function() {
// expect(dateParser.filter(new Date(2013, 0, 1), '\'year\' y')).toEqual('year 2013');
// });
//
// it('should work without whitespace', function() {
// expect(dateParser.filter(new Date(2013, 0, 1), '\'year:\'y')).toEqual('year:2013');
// });
// });
describe('filter', function() {
it('should work with multiple literals', function() {
expect(dateParser.filter(new Date(2013, 0, 29), 'd \'de\' MMMM \'de\' y')).toEqual('29 de January de 2013');
});
it('should work with escaped single quote', function() {
expect(dateParser.filter(new Date(2015, 2, 22, 12), 'd.MMMM.yy h \'o\'\'clock\'')).toEqual('22.March.15 12 o\'clock');
});
it('should work with only a single quote', function() {
expect(dateParser.filter(new Date(2015, 2, 22), 'd.MMMM.yy \'\'\'')).toEqual('22.March.15 \'');
});
it('should work with trailing literal', function() {
expect(dateParser.filter(new Date(2013, 0, 1), '\'year\' y')).toEqual('year 2013');
});
it('should work without whitespace', function() {
expect(dateParser.filter(new Date(2013, 0, 1), '\'year:\'y')).toEqual('year:2013');
});
});
describe('parse', function() {
it('should work with multiple literals', function() {
+29 -14
View File
@@ -16,6 +16,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
maxMode: 'year',
minDate: null,
minMode: 'day',
monthColumns: 3,
ngModelOptions: {},
shortcutPropagation: false,
showWeeks: true,
@@ -23,13 +24,15 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
yearRows: 4
})
.controller('UibDatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$locale', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerLiteralWarning', '$datepickerSuppressError', 'uibDateParser',
function($scope, $attrs, $parse, $interpolate, $locale, $log, dateFilter, datepickerConfig, $datepickerLiteralWarning, $datepickerSuppressError, dateParser) {
.controller('UibDatepickerController', ['$scope', '$element', '$attrs', '$parse', '$interpolate', '$locale', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerLiteralWarning', '$datepickerSuppressError', 'uibDateParser',
function($scope, $element, $attrs, $parse, $interpolate, $locale, $log, dateFilter, datepickerConfig, $datepickerLiteralWarning, $datepickerSuppressError, dateParser) {
var self = this,
ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl;
ngModelOptions = {},
watchListeners = [],
optionsUsed = !!$attrs.datepickerOptions;
watchListeners = [];
$element.addClass('uib-datepicker');
$attrs.$set('role', 'application');
if (!$scope.datepickerOptions) {
$scope.datepickerOptions = {};
@@ -52,6 +55,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
'maxMode',
'minDate',
'minMode',
'monthColumns',
'showWeeks',
'shortcutPropagation',
'startingDay',
@@ -77,6 +81,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
$interpolate($scope.datepickerOptions[key])($scope.$parent) :
datepickerConfig[key];
break;
case 'monthColumns':
case 'showWeeks':
case 'shortcutPropagation':
case 'yearColumns':
@@ -156,7 +161,9 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
this.init = function(ngModelCtrl_) {
ngModelCtrl = ngModelCtrl_;
ngModelOptions = ngModelCtrl_.$options || datepickerConfig.ngModelOptions;
ngModelOptions = ngModelCtrl_.$options ||
$scope.datepickerOptions.ngModelOptions ||
datepickerConfig.ngModelOptions;
if ($scope.datepickerOptions.initDate) {
self.activeDate = dateParser.fromTimezone($scope.datepickerOptions.initDate, ngModelOptions.timezone) || new Date();
$scope.$watch('datepickerOptions.initDate', function(initDate) {
@@ -169,8 +176,9 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
self.activeDate = new Date();
}
this.activeDate = ngModelCtrl.$modelValue ?
dateParser.fromTimezone(new Date(ngModelCtrl.$modelValue), ngModelOptions.timezone) :
var date = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : new Date();
this.activeDate = !isNaN(date) ?
dateParser.fromTimezone(date, ngModelOptions.timezone) :
dateParser.fromTimezone(new Date(), ngModelOptions.timezone);
ngModelCtrl.$render = function() {
@@ -327,6 +335,12 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
}
};
$element.on('keydown', function(evt) {
$scope.$apply(function() {
$scope.keydown(evt);
});
});
$scope.$on('$destroy', function() {
//Clear all watch listeners on destroy
while (watchListeners.length) {
@@ -477,7 +491,8 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
}
scope.title = dateFilter(this.activeDate, this.formatMonthTitle);
scope.rows = this.split(months, 3);
scope.rows = this.split(months, this.monthColumns);
scope.yearHeaderColspan = this.monthColumns > 3 ? this.monthColumns - 2 : 1;
};
this.compare = function(date1, date2) {
@@ -494,11 +509,11 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
if (key === 'left') {
date = date - 1;
} else if (key === 'up') {
date = date - 3;
date = date - this.monthColumns;
} else if (key === 'right') {
date = date + 1;
} else if (key === 'down') {
date = date + 3;
date = date + this.monthColumns;
} else if (key === 'pageup' || key === 'pagedown') {
var year = this.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1);
this.activeDate.setFullYear(year);
@@ -569,7 +584,6 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
.directive('uibDatepicker', function() {
return {
replace: true,
templateUrl: function(element, attrs) {
return attrs.templateUrl || 'uib/template/datepicker/datepicker.html';
},
@@ -577,6 +591,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
datepickerOptions: '=?'
},
require: ['uibDatepicker', '^ngModel'],
restrict: 'A',
controller: 'UibDatepickerController',
controllerAs: 'datepicker',
link: function(scope, element, attrs, ctrls) {
@@ -589,11 +604,11 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
.directive('uibDaypicker', function() {
return {
replace: true,
templateUrl: function(element, attrs) {
return attrs.templateUrl || 'uib/template/datepicker/day.html';
},
require: ['^uibDatepicker', 'uibDaypicker'],
restrict: 'A',
controller: 'UibDaypickerController',
link: function(scope, element, attrs, ctrls) {
var datepickerCtrl = ctrls[0],
@@ -606,11 +621,11 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
.directive('uibMonthpicker', function() {
return {
replace: true,
templateUrl: function(element, attrs) {
return attrs.templateUrl || 'uib/template/datepicker/month.html';
},
require: ['^uibDatepicker', 'uibMonthpicker'],
restrict: 'A',
controller: 'UibMonthpickerController',
link: function(scope, element, attrs, ctrls) {
var datepickerCtrl = ctrls[0],
@@ -623,11 +638,11 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
.directive('uibYearpicker', function() {
return {
replace: true,
templateUrl: function(element, attrs) {
return attrs.templateUrl || 'uib/template/datepicker/year.html';
},
require: ['^uibDatepicker', 'uibYearpicker'],
restrict: 'A',
controller: 'UibYearpickerController',
link: function(scope, element, attrs, ctrls) {
var ctrl = ctrls[0];
+1 -1
View File
@@ -15,7 +15,7 @@
<h4>Inline</h4>
<div style="display:inline-block; min-height:290px;">
<uib-datepicker ng-model="dt" class="well well-sm" datepicker-options="options"></uib-datepicker>
<div uib-datepicker ng-model="dt" class="well well-sm" datepicker-options="options"></div>
</div>
<hr />
+12 -2
View File
@@ -13,7 +13,7 @@ The datepicker has 3 modes:
* `ng-model`
<small class="badge">$</small>
<i class="glyphicon glyphicon-eye-open"></i> -
The date object. Needs to be a Javascript Date object.
The date object. Must be a Javascript `Date` object. You may use the `uibDateParser` service to assist in string-to-object conversion.
* `ng-model-options`
<small class="badge">$</small>
@@ -104,6 +104,16 @@ Apart from the previous settings, to configure the uib-datepicker you need to cr
_(Default: `day`)_ -
Sets a lower limit for mode.
* `monthColumns`
<small class="badge">C</small>
_(Default: `3`)_ -
Number of columns displayed in month selection.
* `ngModelOptions`
<small class="badge">C</small>
_(Default: `null`)_ -
Sets `ngModelOptions` for datepicker. This can be overridden through attribute usage
* `shortcutPropagation`
<small class="badge">C</small>
_(Default: `false`)_ -
@@ -118,7 +128,7 @@ Apart from the previous settings, to configure the uib-datepicker you need to cr
<small class="badge">C</small>
*(Default: `$locale.DATETIME_FORMATS.FIRSTDAYOFWEEK`)* -
Starting day of the week from 0-6 (0=Sunday, ..., 6=Saturday).
* `yearRows`
<small class="badge">C</small>
_(Default: `4`)_ -
+79 -53
View File
@@ -26,8 +26,12 @@ describe('datepicker', function() {
});
}));
function getTitleCell() {
return element.find('th').eq(1);
}
function getTitleButton() {
return element.find('th').eq(1).find('button').first();
return getTitleCell().find('button').first();
}
function getTitle() {
@@ -155,7 +159,7 @@ describe('datepicker', function() {
$scope.options = {
minDate: '1984-01-01'
};
element = $compile('<uib-datepicker ng-model="locals.date" datepicker-options="options"></uib-datepicker>')($scope);
element = $compile('<div uib-datepicker ng-model="locals.date" datepicker-options="options"></div>')($scope);
$scope.$digest();
expect($log.warn).toHaveBeenCalledWith('Literal date support has been deprecated, please switch to date object usage');
@@ -175,7 +179,7 @@ describe('datepicker', function() {
$scope.options = {
minDate: '1984-01-01'
};
element = $compile('<uib-datepicker ng-model="locals.date" datepicker-options="options"></uib-datepicker>')($scope);
element = $compile('<div uib-datepicker ng-model="locals.date" datepicker-options="options"></div>')($scope);
$scope.$digest();
expect($log.warn).not.toHaveBeenCalled();
@@ -192,7 +196,7 @@ describe('datepicker', function() {
$scope.options = {
maxDate: '1984-01-01'
};
element = $compile('<uib-datepicker ng-model="locals.date" datepicker-options="options"></uib-datepicker>')($scope);
element = $compile('<div uib-datepicker ng-model="locals.date" datepicker-options="options"></div>')($scope);
$scope.$digest();
expect($log.warn).toHaveBeenCalledWith('Literal date support has been deprecated, please switch to date object usage');
@@ -212,7 +216,7 @@ describe('datepicker', function() {
$scope.options = {
maxDate: '1984-01-01'
};
element = $compile('<uib-datepicker ng-model="locals.date" datepicker-options="options"></uib-datepicker>')($scope);
element = $compile('<div uib-datepicker ng-model="locals.date" datepicker-options="options"></div>')($scope);
$scope.$digest();
expect($log.warn).not.toHaveBeenCalled();
@@ -232,7 +236,7 @@ describe('datepicker', function() {
});
spyOn($log, 'error');
element = $compile('<uib-datepicker ng-model="locals.date"></uib-datepicker>')($scope);
element = $compile('<div uib-datepicker ng-model="locals.date"></div>')($scope);
$scope.locals = {
date: 'lalala'
@@ -253,7 +257,7 @@ describe('datepicker', function() {
});
spyOn($log, 'error');
element = $compile('<uib-datepicker ng-model="locals.date"></uib-datepicker>')($scope);
element = $compile('<div uib-datepicker ng-model="locals.date"></div>')($scope);
$scope.locals = {
date: 'lalala'
@@ -274,7 +278,7 @@ describe('datepicker', function() {
});
spyOn($log, 'error');
element = $compile('<uib-datepicker ng-model="locals.date"></uib-datepicker>')($scope);
element = $compile('<div uib-datepicker ng-model="locals.date"></div>')($scope);
$scope.locals = {
date: 'lalala'
@@ -305,7 +309,7 @@ describe('datepicker', function() {
var baseTime = new Date(2015, 2, 23);
jasmine.clock().mockDate(baseTime);
element = $compile('<uib-datepicker ng-model="fooDate"></uib-datepicker')($rootScope);
element = $compile('<div uib-datepicker ng-model="fooDate"></div')($rootScope);
$rootScope.$digest();
expect(element.controller('uibDatepicker').activeDate.getTime()).toEqual(baseTime.getTime());
@@ -315,10 +319,10 @@ describe('datepicker', function() {
it('should support custom templates', function() {
$templateCache.put('foo/bar.html', '<div>baz</div>');
element = $compile('<uib-datepicker ng-model="date" template-url="foo/bar.html"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" template-url="foo/bar.html"></div>')($rootScope);
$rootScope.$digest();
expect(element.html()).toBe('baz');
expect(element.html()).toBe('<div>baz</div>');
});
it('should support custom day, month and year templates', function() {
@@ -327,15 +331,15 @@ describe('datepicker', function() {
$templateCache.put('foo/year.html', '<div>year</div>');
$templateCache.put('foo/bar.html', '<div>' +
'<uib-daypicker template-url="foo/day.html"></uib-daypicker>' +
'<uib-monthpicker template-url="foo/month.html"></uib-monthpicker>' +
'<uib-yearpicker template-url="foo/year.html"></uib-yearpicker>' +
'<div uib-daypicker template-url="foo/day.html"></div>' +
'<div uib-monthpicker template-url="foo/month.html"></div>' +
'<div uib-yearpicker template-url="foo/year.html"></div>' +
'</div>');
element = $compile('<uib-datepicker ng-model="date" template-url="foo/bar.html"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" template-url="foo/bar.html"></div>')($rootScope);
$rootScope.$digest();
var expectedHtml = '<div template-url="foo/day.html">day</div><div template-url="foo/month.html">month</div><div template-url="foo/year.html">year</div>';
var expectedHtml = '<div><div uib-daypicker="" template-url="foo/day.html"><div>day</div></div><div uib-monthpicker="" template-url="foo/month.html"><div>month</div></div><div uib-yearpicker="" template-url="foo/year.html"><div>year</div></div></div>';
expect(element.html()).toBe(expectedHtml);
});
@@ -343,22 +347,22 @@ describe('datepicker', function() {
it('should expose the controller in the template', function() {
$templateCache.put('uib/template/datepicker/datepicker.html', '<div>{{datepicker.text}}</div>');
element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date"></div>')($rootScope);
$rootScope.$digest();
var ctrl = element.controller('uib-datepicker');
expect(ctrl).toBeDefined();
expect(element.html()).toBe('');
expect(element.html()).toBe('<div class="ng-binding"></div>');
ctrl.text = 'baz';
$rootScope.$digest();
expect(element.html()).toBe('baz');
expect(element.html()).toBe('<div class="ng-binding">baz</div>');
});
describe('basic functionality', function() {
beforeEach(function() {
element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date"></div>')($rootScope);
$rootScope.$digest();
});
@@ -953,12 +957,29 @@ describe('datepicker', function() {
});
describe('attribute `datepicker-options`', function() {
describe('ngModelOptions', function() {
beforeEach(inject(function() {
$rootScope.date = new Date('2005-11-07T10:00:00.000Z');
$rootScope.options = {
ngModelOptions: {
timezone: '+600'
}
};
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
$rootScope.$digest();
}));
it('supports ngModelOptions from options object and sets date to appropriate date', function() {
expectSelectedElement(8);
});
});
describe('startingDay', function() {
beforeEach(function() {
$rootScope.options = {
startingDay: 1
};
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
$rootScope.$digest();
});
@@ -987,7 +1008,7 @@ describe('datepicker', function() {
$rootScope.options = {
showWeeks: false
};
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
$rootScope.$digest();
});
@@ -1004,7 +1025,7 @@ describe('datepicker', function() {
beforeEach(function() {
$rootScope.options = {};
$rootScope.date = new Date('September 10, 2010');
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
$rootScope.$digest();
});
@@ -1029,7 +1050,7 @@ describe('datepicker', function() {
$rootScope.options = {
minDate: new Date('September 12, 2010')
};
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
$rootScope.$digest();
});
@@ -1114,7 +1135,7 @@ describe('datepicker', function() {
it('accepts literals, \'yyyy-MM-dd\' case', function() {
$rootScope.options.minDate = '2010-09-05';
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
$rootScope.$digest();
var buttons = getAllOptionsEl();
angular.forEach(buttons, function(button, index) {
@@ -1127,7 +1148,7 @@ describe('datepicker', function() {
beforeEach(function() {
$rootScope.options = {};
$rootScope.date = new Date('September 10, 2010');
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
$rootScope.$digest();
});
@@ -1152,7 +1173,7 @@ describe('datepicker', function() {
$rootScope.options = {
maxDate: new Date('September 25, 2010')
};
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
$rootScope.$digest();
});
@@ -1244,8 +1265,8 @@ describe('datepicker', function() {
yearColumns: 4,
yearRows: 3
};
element = $compile('<uib-datepicker ng-model="date"' +
'datepicker-options="options"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date"' +
'datepicker-options="options"></div>')($rootScope);
$rootScope.$digest();
});
@@ -1308,8 +1329,9 @@ describe('datepicker', function() {
uibDatepickerConfig.yearRows = 2;
uibDatepickerConfig.yearColumns = 5;
uibDatepickerConfig.startingDay = 6;
uibDatepickerConfig.monthColumns = 4;
element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date"></div>')($rootScope);
$rootScope.$digest();
}));
afterEach(inject(function(uibDatepickerConfig) {
@@ -1329,13 +1351,17 @@ describe('datepicker', function() {
expect(getTitle()).toBe('10');
expect(getOptions()).toEqual([
['Jan', 'Feb', 'Mar'],
['Apr', 'May', 'Jun'],
['Jul', 'Aug', 'Sep'],
['Oct', 'Nov', 'Dec']
['Jan', 'Feb', 'Mar', 'Apr'],
['May', 'Jun', 'Jul', 'Aug'],
['Sep', 'Oct', 'Nov', 'Dec']
]);
});
it('shows title year button to expand to fill width in `month` mode', function() {
clickTitleButton();
expect(getTitleCell().attr('colspan')).toBe('2');
});
it('changes the title, year format & range in `year` mode', function() {
clickTitleButton();
clickTitleButton();
@@ -1370,7 +1396,7 @@ describe('datepicker', function() {
describe('disabled', function() {
beforeEach(function() {
element = $compile('<uib-datepicker ng-model="date" disabled></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" disabled></div>')($rootScope);
$rootScope.$digest();
});
@@ -1384,7 +1410,7 @@ describe('datepicker', function() {
describe('ng-disabled', function() {
beforeEach(function() {
$rootScope.disabled = false;
element = $compile('<uib-datepicker ng-model="date" ng-disabled="disabled"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" ng-disabled="disabled"></div>')($rootScope);
$rootScope.$digest();
});
@@ -1425,7 +1451,7 @@ describe('datepicker', function() {
describe('basics', function() {
beforeEach(function() {
element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date"></div>')($rootScope);
$rootScope.$digest();
});
@@ -1444,7 +1470,7 @@ describe('datepicker', function() {
initDate: new Date('2006-01-01T00:00:00.000Z')
};
$rootScope.date = null;
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"><uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
$rootScope.$digest();
expect(getTitle()).toEqual('January 2006');
@@ -1454,7 +1480,7 @@ describe('datepicker', function() {
$rootScope.options = {
minDate: new Date('2010-10-01T00:00:00.000Z')
};
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"><uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
$rootScope.$digest();
expect(getSelectedElement().prop('disabled')).toBe(true);
@@ -1467,7 +1493,7 @@ describe('datepicker', function() {
beforeEach(inject(function() {
$rootScope.date = new Date('2005-11-07T10:00:00.000Z');
$rootScope.ngModelOptions = { timezone: '+600'};
element = $compile('<uib-datepicker ng-model="date" ng-model-options="ngModelOptions"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" ng-model-options="ngModelOptions"></div>')($rootScope);
$rootScope.$digest();
}));
@@ -1485,7 +1511,7 @@ describe('datepicker', function() {
describe('with empty initial state', function() {
beforeEach(inject(function() {
$rootScope.date = null;
element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date"></div>')($rootScope);
$rootScope.$digest();
}));
@@ -1514,7 +1540,7 @@ describe('datepicker', function() {
$rootScope.options = {
initDate: new Date('November 9, 1980')
};
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
$rootScope.$digest();
}));
@@ -1533,7 +1559,7 @@ describe('datepicker', function() {
$rootScope.options = {
datepickerMode: 'month'
};
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
$rootScope.$digest();
}));
@@ -1554,7 +1580,7 @@ describe('datepicker', function() {
minMode: 'month',
datepickerMode: 'month'
};
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
$rootScope.$digest();
}));
@@ -1584,7 +1610,7 @@ describe('datepicker', function() {
$rootScope.options = {
maxMode: 'month'
};
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
$rootScope.$digest();
}));
@@ -1632,7 +1658,7 @@ describe('datepicker', function() {
// Use dateModel directive to add formatters and parsers to the
// ngModelController that translate the custom date object.
element = $compile('<uib-datepicker ng-model="date" date-model></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" date-model></div>')($rootScope);
$rootScope.$digest();
}));
@@ -1657,7 +1683,7 @@ describe('datepicker', function() {
}));
it('with the default starting day (sunday)', function() {
element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date"></div>')($rootScope);
$rootScope.$digest();
expect(getWeeks()).toEqual(['23', '24', '25', '26', '27', '28']);
@@ -1668,7 +1694,7 @@ describe('datepicker', function() {
$rootScope.options = {
startingDay: 1
};
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
$rootScope.$digest();
expect(getWeeks()).toEqual(['22', '23', '24', '25', '26', '27']);
@@ -1678,7 +1704,7 @@ describe('datepicker', function() {
$rootScope.options = {
startingDay: 4
};
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
$rootScope.$digest();
expect(getWeeks()).toEqual(['22', '23', '24', '25', '26', '27']);
@@ -1688,7 +1714,7 @@ describe('datepicker', function() {
$rootScope.options = {
startingDay: 6
};
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
$rootScope.$digest();
expect(getWeeks()).toEqual(['23', '24', '25', '26', '27', '28']);
@@ -1698,7 +1724,7 @@ describe('datepicker', function() {
describe('first week in january', function() {
it('in current year', function() {
$rootScope.date = new Date('January 07, 2014');
element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date"></div>')($rootScope);
$rootScope.$digest();
expect(getWeeks()).toEqual(['1', '2', '3', '4', '5', '6']);
@@ -1706,7 +1732,7 @@ describe('datepicker', function() {
it('in last year', function() {
$rootScope.date = new Date('January 07, 2010');
element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date"></div>')($rootScope);
$rootScope.$digest();
expect(getWeeks()).toEqual(['53', '1', '2', '3', '4', '5']);
@@ -1719,7 +1745,7 @@ describe('datepicker', function() {
}));
it('in next year', function() {
element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
element = $compile('<div uib-datepicker ng-model="date"></div>')($rootScope);
$rootScope.$digest();
expect(getWeeks()).toEqual(['49', '50', '51', '52', '1', '2']);
+5
View File
@@ -57,6 +57,11 @@ Add the ability to override the template used on the component (inner uib-datepi
_(Default: `false`)_ -
Whether or not to show the datepicker.
* `ng-model`
<small class="badge">$</small>
<i class="glyphicon glyphicon-eye-open"></i> -
The date object. Must be a Javascript `Date` object. You may use the `uibDateParser` service to assist in string-to-object conversion.
* `on-open-focus`
<small class="badge">$</small>
<small class="badge">C</small>
+1 -1
View File
@@ -5,6 +5,6 @@ require('./popup.js');
var MODULE_NAME = 'ui.bootstrap.module.datepickerPopup';
angular.module(MODULE_NAME, ['ui.bootstrap.datepickerPopup', 'uib/template/datepickerPopup/popup.html']);
angular.module(MODULE_NAME, ['ui.bootstrap.datepickerPopup', 'uib/template/datepickerPopup/popup.html', 'ui.bootstrap.module.datepicker']);
module.exports = MODULE_NAME;
+12 -28
View File
@@ -28,8 +28,7 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
isHtml5DateInput = false;
var dateFormat, closeOnDateSelection, appendToBody, onOpenFocus,
datepickerPopupTemplateUrl, datepickerTemplateUrl, popupEl, datepickerEl, scrollParentEl,
ngModel, ngModelOptions, $popup, altInputFormats, watchListeners = [],
timezone;
ngModel, ngModelOptions, $popup, altInputFormats, watchListeners = [];
this.init = function(_ngModel_) {
ngModel = _ngModel_;
@@ -85,19 +84,6 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
// popup element used to display calendar
popupEl = angular.element('<div uib-datepicker-popup-wrap><div uib-datepicker></div></div>');
if (ngModelOptions) {
timezone = ngModelOptions.timezone;
$scope.ngModelOptions = angular.copy(ngModelOptions);
$scope.ngModelOptions.timezone = null;
if ($scope.ngModelOptions.updateOnDefault === true) {
$scope.ngModelOptions.updateOn = $scope.ngModelOptions.updateOn ?
$scope.ngModelOptions.updateOn + ' default' : 'default';
}
popupEl.attr('ng-model-options', 'ngModelOptions');
} else {
timezone = null;
}
popupEl.attr({
'ng-model': 'date',
@@ -133,17 +119,17 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
return value;
}
$scope.date = dateParser.fromTimezone(value, timezone);
if (angular.isNumber($scope.date)) {
$scope.date = new Date($scope.date);
if (angular.isNumber(value)) {
value = new Date(value);
}
$scope.date = value;
return dateParser.filter($scope.date, dateFormat);
});
} else {
ngModel.$formatters.push(function(value) {
$scope.date = dateParser.fromTimezone(value, timezone);
$scope.date = value;
return value;
});
}
@@ -195,7 +181,7 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
$scope.isDisabled = function(date) {
if (date === 'today') {
date = dateParser.fromTimezone(new Date(), timezone);
date = new Date();
}
var dates = {};
@@ -203,7 +189,7 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
if (!$scope.datepickerOptions[key]) {
dates[key] = null;
} else if (angular.isDate($scope.datepickerOptions[key])) {
dates[key] = dateParser.fromTimezone(new Date($scope.datepickerOptions[key]), timezone);
dates[key] = new Date($scope.datepickerOptions[key]);
} else {
if ($datepickerPopupLiteralWarning) {
$log.warn('Literal date support has been deprecated, please switch to date object usage');
@@ -224,9 +210,7 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
// Inner change
$scope.dateSelection = function(dt) {
if (angular.isDefined(dt)) {
$scope.date = dt;
}
$scope.date = dt;
var date = $scope.date ? dateParser.filter($scope.date, dateFormat) : null; // Setting to NULL is necessary for form validators to function
$element.val(date);
ngModel.$setViewValue(date);
@@ -344,7 +328,7 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
if (angular.isString(viewValue)) {
var date = parseDateString(viewValue);
if (!isNaN(date)) {
return dateParser.toTimezone(date, timezone);
return date;
}
}
@@ -371,7 +355,7 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
}
if (angular.isString(value)) {
return !isNaN(parseDateString(viewValue));
return !isNaN(parseDateString(value));
}
return false;
@@ -450,7 +434,7 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
.directive('uibDatepickerPopupWrap', function() {
return {
replace: true,
restrict: 'A',
transclude: true,
templateUrl: function(element, attrs) {
return attrs.templateUrl || 'uib/template/datepickerPopup/popup.html';
+31 -9
View File
@@ -175,8 +175,10 @@ describe('datepicker popup', function() {
$sniffer = _$sniffer_;
$rootScope.date = new Date('September 30, 2010 15:30:00');
$rootScope.modelOptions = {allowInvalid: true};
element = $compile('<div><input ng-model="date" ng-model-options="modelOptions" uib-datepicker-popup></div>')($rootScope);
$rootScope.ngModelOptions = {
allowInvalid: true
};
element = $compile('<div><input ng-model="date" ng-model-options="ngModelOptions" uib-datepicker-popup></div>')($rootScope);
inputEl = element.find('input');
$rootScope.$digest();
}));
@@ -557,8 +559,13 @@ describe('datepicker popup', function() {
$timeout = _$timeout_;
$rootScope.isopen = true;
$rootScope.date = new Date('2010-09-30T10:00:00.000Z');
$rootScope.options = {
ngModelOptions: {
updateOn: 'default'
}
};
wrapElement = $compile('<div><input ng-model="date" ' +
'ng-model-options="{ updateOn: \'default\' }" ' +
'datepicker-options="options" ' +
'uib-datepicker-popup is-open="isopen"><div>')($rootScope);
$rootScope.$digest();
assignElements(wrapElement);
@@ -1034,6 +1041,13 @@ describe('datepicker popup', function() {
expect($rootScope.date).toBe(null);
});
it('should clear the previously selected date', function() {
$rootScope.date = new Date();
$rootScope.$digest();
buttons.eq(1).click();
expect($rootScope.date).toBe(null);
});
it('should have a button to close calendar', function() {
buttons.eq(2).click();
assignElements(wrapElement);
@@ -1330,7 +1344,7 @@ describe('datepicker popup', function() {
$compile(elm)($rootScope);
$rootScope.$digest();
expect(elm.children().eq(1).html()).toBe('baz');
expect(elm.children().eq(1).html()).toBe('<div>baz</div>');
});
});
@@ -1353,7 +1367,7 @@ describe('datepicker popup', function() {
var datepicker = elm.find('[uib-datepicker]');
expect(datepicker.html()).toBe('baz');
expect(datepicker.html()).toBe('<div>baz</div>');
});
});
@@ -1598,9 +1612,13 @@ describe('datepicker popup', function() {
beforeEach(function() {
$rootScope.date = new Date('2010-09-30T10:00:00.000Z');
$rootScope.ngModelOptions = { timezone: '+600' };
$rootScope.options = {
ngModelOptions: {
timezone: '+600'
}
};
$rootScope.isopen = true;
var wrapper = $compile('<div><input ng-model="date" uib-datepicker-popup="MM/dd/yyyy" ng-model-options="ngModelOptions" is-open="isopen"></div>')($rootScope);
var wrapper = $compile('<div><input ng-model="date" uib-datepicker-popup="MM/dd/yyyy" datepicker-options="options" is-open="isopen"></div>')($rootScope);
$rootScope.$digest();
assignElements(wrapper);
});
@@ -1631,9 +1649,13 @@ describe('datepicker popup', function() {
beforeEach(function() {
$rootScope.date = new Date('2010-09-30T10:00:00.000Z');
$rootScope.ngModelOptions = { timezone: '+600' };
$rootScope.options = {
ngModelOptions: {
timezone: '+600'
}
};
$rootScope.isopen = true;
var wrapper = $compile('<div><input type="date" ng-model="date" uib-datepicker-popup ng-model-options="ngModelOptions" is-open="isopen"></div>')($rootScope);
var wrapper = $compile('<div><input type="date" ng-model="date" uib-datepicker-popup datepicker-options="options" is-open="isopen"></div>')($rootScope);
$rootScope.$digest();
assignElements(wrapper);
});
+22 -6
View File
@@ -11,7 +11,6 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
this.open = function(dropdownScope, element) {
if (!openScope) {
$document.on('click', closeDropdown);
element.on('keydown', keybindFilter);
}
if (openScope && openScope !== dropdownScope) {
@@ -25,7 +24,10 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
if (openScope === dropdownScope) {
openScope = null;
$document.off('click', closeDropdown);
element.off('keydown', keybindFilter);
var dropdownMenu = dropdownScope.getDropdownElement();
if (dropdownMenu) {
dropdownMenu.off('keydown', this.keybindFilter);
}
}
};
@@ -50,13 +52,14 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
}
openScope.isOpen = false;
openScope.focusToggleElement();
if (!$rootScope.$$phase) {
openScope.$apply();
}
};
var keybindFilter = function(evt) {
this.keybindFilter = function(evt) {
if (evt.which === 27) {
evt.stopPropagation();
openScope.focusToggleElement();
@@ -191,7 +194,9 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
if (appendTo && self.dropdownMenu) {
var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true),
css,
rightalign;
rightalign,
scrollbarPadding,
scrollbarWidth = 0;
css = {
top: pos.top + 'px',
@@ -204,7 +209,13 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
css.right = 'auto';
} else {
css.left = 'auto';
css.right = window.innerWidth -
scrollbarPadding = $position.scrollbarPadding(appendTo);
if (scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) {
scrollbarWidth = scrollbarPadding.scrollbarWidth;
}
css.right = window.innerWidth - scrollbarWidth -
(pos.left + $element.prop('offsetWidth')) + 'px';
}
@@ -245,13 +256,19 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
var newEl = dropdownElement;
self.dropdownMenu.replaceWith(newEl);
self.dropdownMenu = newEl;
self.dropdownMenu.on('keydown', uibDropdownService.keybindFilter);
});
});
} else {
if (self.dropdownMenu) {
self.dropdownMenu.on('keydown', uibDropdownService.keybindFilter);
}
}
scope.focusToggleElement();
uibDropdownService.open(scope, $element);
} else {
uibDropdownService.close(scope, $element);
if (self.dropdownMenuTemplateUrl) {
if (templateScope) {
templateScope.$destroy();
@@ -261,7 +278,6 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
self.dropdownMenu = newEl;
}
uibDropdownService.close(scope, $element);
self.selectedOption = null;
}
+101 -34
View File
@@ -35,7 +35,7 @@ describe('uib-dropdown', function() {
describe('basic', function() {
function dropdown() {
return $compile('<li uib-dropdown><a href uib-dropdown-toggle></a><ul><li><a href>Hello</a></li></ul></li>')($rootScope);
return $compile('<li uib-dropdown><a href uib-dropdown-toggle></a><ul uib-dropdown-menu><li><a href>Hello</a></li></ul></li>')($rootScope);
}
beforeEach(function() {
@@ -69,9 +69,10 @@ describe('uib-dropdown', function() {
});
it('should close on escape key & focus toggle element', function() {
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
clickDropdownToggle();
var event = triggerKeyDown(element, 27);
var event = triggerKeyDown(dropdownMenu, 27);
expect(element).not.toHaveClass(dropdownConfig.openClass);
expect(element.find('a')).toHaveFocus();
expect(event.stopPropagation).toHaveBeenCalled();
@@ -108,7 +109,7 @@ describe('uib-dropdown', function() {
});
it('should not toggle if the element has `disabled` class', function() {
var elm = $compile('<li uib-dropdown><a class="disabled" uib-dropdown-toggle></a><ul><li>Hello</li></ul></li>')($rootScope);
var elm = $compile('<li uib-dropdown><a class="disabled" uib-dropdown-toggle></a><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
clickDropdownToggle( elm );
expect(elm).not.toHaveClass(dropdownConfig.openClass);
});
@@ -121,7 +122,7 @@ describe('uib-dropdown', function() {
it('should not toggle if the element has `ng-disabled` as true', function() {
$rootScope.isdisabled = true;
var elm = $compile('<li uib-dropdown><div ng-disabled="isdisabled" uib-dropdown-toggle></div><ul><li>Hello</li></ul></li>')($rootScope);
var elm = $compile('<li uib-dropdown><div ng-disabled="isdisabled" uib-dropdown-toggle></div><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
$rootScope.$digest();
elm.find('div').click();
expect(elm).not.toHaveClass(dropdownConfig.openClass);
@@ -134,7 +135,7 @@ describe('uib-dropdown', function() {
it('should unbind events on scope destroy', function() {
var $scope = $rootScope.$new();
var elm = $compile('<li uib-dropdown><button ng-disabled="isdisabled" uib-dropdown-toggle></button><ul><li>Hello</li></ul></li>')($scope);
var elm = $compile('<li uib-dropdown><button ng-disabled="isdisabled" uib-dropdown-toggle></button><ul uib-dropdown-menu><li>Hello</li></ul></li>')($scope);
$scope.$digest();
var buttonEl = elm.find('button');
@@ -220,12 +221,28 @@ describe('uib-dropdown', function() {
beforeEach(function() {
element = dropdown();
$document.find('body').append(element);
});
afterEach(function() {
element.remove();
});
it('adds the menu to the body', function() {
expect($document.find('#dropdown-menu').parent()[0]).toBe($document.find('body')[0]);
});
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');
expect(document.activeElement).toBe(toggle[0]);
});
it('removes the menu when the dropdown is removed', function() {
element.remove();
$rootScope.$digest();
@@ -268,6 +285,17 @@ describe('uib-dropdown', 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');
expect(document.activeElement).toBe(toggle[0]);
});
it('removes the menu when the dropdown is removed', function() {
element.remove();
$rootScope.$digest();
@@ -279,7 +307,7 @@ describe('uib-dropdown', function() {
describe('with uib-dropdown-toggle', function() {
beforeEach(function() {
$rootScope.isopen = true;
element = $compile('<li uib-dropdown is-open="isopen"><a href uib-dropdown-toggle></a><ul><li>Hello</li></ul></li>')($rootScope);
element = $compile('<li uib-dropdown is-open="isopen"><a href uib-dropdown-toggle></a><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
$rootScope.$digest();
});
@@ -313,7 +341,7 @@ describe('uib-dropdown', function() {
describe('without uib-dropdown-toggle', function() {
beforeEach(function() {
$rootScope.isopen = true;
element = $compile('<li uib-dropdown is-open="isopen"><ul><li>Hello</li></ul></li>')($rootScope);
element = $compile('<li uib-dropdown is-open="isopen"><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
$rootScope.$digest();
});
@@ -334,7 +362,7 @@ describe('uib-dropdown', function() {
beforeEach(function() {
$rootScope.toggleHandler = jasmine.createSpy('toggleHandler');
$rootScope.isopen = false;
element = $compile('<li uib-dropdown on-toggle="toggleHandler(open)" is-open="isopen"><a uib-dropdown-toggle></a><ul><li>Hello</li></ul></li>')($rootScope);
element = $compile('<li uib-dropdown on-toggle="toggleHandler(open)" is-open="isopen"><a uib-dropdown-toggle></a><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
$rootScope.$digest();
});
@@ -361,7 +389,7 @@ describe('uib-dropdown', function() {
beforeEach(function() {
$rootScope.toggleHandler = jasmine.createSpy('toggleHandler');
$rootScope.isopen = true;
element = $compile('<li uib-dropdown on-toggle="toggleHandler(open)" is-open="isopen"><a uib-dropdown-toggle></a><ul><li>Hello</li></ul></li>')($rootScope);
element = $compile('<li uib-dropdown on-toggle="toggleHandler(open)" is-open="isopen"><a uib-dropdown-toggle></a><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
$rootScope.$digest();
});
@@ -389,7 +417,7 @@ describe('uib-dropdown', function() {
describe('without is-open', function() {
beforeEach(function() {
$rootScope.toggleHandler = jasmine.createSpy('toggleHandler');
element = $compile('<li uib-dropdown on-toggle="toggleHandler(open)"><a uib-dropdown-toggle></a><ul><li>Hello</li></ul></li>')($rootScope);
element = $compile('<li uib-dropdown on-toggle="toggleHandler(open)"><a uib-dropdown-toggle></a><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
$rootScope.$digest();
});
@@ -417,7 +445,7 @@ describe('uib-dropdown', function() {
function dropdown(autoClose) {
return $compile('<li uib-dropdown ' +
(autoClose === undefined ? '' : 'auto-close="' + autoClose + '"') +
'><a href uib-dropdown-toggle></a><ul><li><a href>Hello</a></li></ul></li>')($rootScope);
'><a href uib-dropdown-toggle></a><ul uib-dropdown-menu><li><a href>Hello</a></li></ul></li>')($rootScope);
}
describe('always', function() {
@@ -449,7 +477,7 @@ describe('uib-dropdown', function() {
it('control with is-open', function() {
$rootScope.isopen = true;
element = $compile('<li uib-dropdown is-open="isopen" auto-close="disabled"><a href uib-dropdown-toggle></a><ul><li>Hello</li></ul></li>')($rootScope);
element = $compile('<li uib-dropdown is-open="isopen" auto-close="disabled"><a href uib-dropdown-toggle></a><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
$rootScope.$digest();
expect(element).toHaveClass(dropdownConfig.openClass);
@@ -472,9 +500,10 @@ describe('uib-dropdown', function() {
it('should close anyway if esc is pressed', function() {
element = dropdown('disabled');
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
clickDropdownToggle();
triggerKeyDown(element, 27);
triggerKeyDown(dropdownMenu, 27);
expect(element).not.toHaveClass(dropdownConfig.openClass);
expect(element.find('a')).toHaveFocus();
});
@@ -519,16 +548,17 @@ describe('uib-dropdown', function() {
describe('using keyboard-nav', function() {
function dropdown() {
return $compile('<li uib-dropdown keyboard-nav><a href uib-dropdown-toggle></a><ul><li><a href>Hello</a></li><li><a href>Hello Again</a></li></ul></li>')($rootScope);
return $compile('<li uib-dropdown keyboard-nav><a href uib-dropdown-toggle></a><ul uib-dropdown-menu><li><a href>Hello</a></li><li><a href>Hello Again</a></li></ul></li>')($rootScope);
}
beforeEach(function() {
element = dropdown();
});
it('should focus first list element when down arrow pressed', function() {
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
clickDropdownToggle();
triggerKeyDown(element, 40);
triggerKeyDown(dropdownMenu, 40);
expect(element).toHaveClass(dropdownConfig.openClass);
var optionEl = element.find('ul').eq(0).find('a').eq(0);
@@ -536,8 +566,9 @@ describe('uib-dropdown', function() {
});
it('should not focus first list element when down arrow pressed if closed', function() {
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
triggerKeyDown(element, 40);
triggerKeyDown(dropdownMenu, 40);
expect(element).not.toHaveClass(dropdownConfig.openClass);
var focusEl = element.find('ul').eq(0).find('a').eq(0);
@@ -545,10 +576,11 @@ describe('uib-dropdown', function() {
});
it('should focus second list element when down arrow pressed twice', function() {
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
clickDropdownToggle();
triggerKeyDown(element, 40);
triggerKeyDown(element, 40);
triggerKeyDown(dropdownMenu, 40);
triggerKeyDown(dropdownMenu, 40);
expect(element).toHaveClass(dropdownConfig.openClass);
var focusEl = element.find('ul').eq(0).find('a').eq(1);
@@ -556,19 +588,21 @@ describe('uib-dropdown', function() {
});
it('should not focus first list element when up arrow pressed after dropdown toggled', function() {
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
clickDropdownToggle();
expect(element).toHaveClass(dropdownConfig.openClass);
triggerKeyDown(element, 38);
triggerKeyDown(dropdownMenu, 38);
var focusEl = element.find('ul').eq(0).find('a').eq(0);
expect(focusEl).not.toHaveFocus();
});
it('should focus last list element when up arrow pressed after dropdown toggled', function() {
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
clickDropdownToggle();
triggerKeyDown(element, 38);
triggerKeyDown(dropdownMenu, 38);
expect(element).toHaveClass(dropdownConfig.openClass);
var focusEl = element.find('ul').eq(0).find('a').eq(1);
@@ -576,9 +610,10 @@ describe('uib-dropdown', function() {
});
it('should not change focus when other keys are pressed', function() {
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
clickDropdownToggle();
triggerKeyDown(element, 37);
triggerKeyDown(dropdownMenu, 37);
expect(element).toHaveClass(dropdownConfig.openClass);
var focusEl = element.find('ul').eq(0).find('a');
@@ -587,12 +622,13 @@ describe('uib-dropdown', function() {
});
it('should focus first list element when down arrow pressed 2x and up pressed 1x', function() {
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
clickDropdownToggle();
triggerKeyDown(element, 40);
triggerKeyDown(element, 40);
triggerKeyDown(dropdownMenu, 40);
triggerKeyDown(dropdownMenu, 40);
triggerKeyDown(element, 38);
triggerKeyDown(dropdownMenu, 38);
expect(element).toHaveClass(dropdownConfig.openClass);
var focusEl = element.find('ul').eq(0).find('a').eq(0);
@@ -600,10 +636,11 @@ describe('uib-dropdown', function() {
});
it('should stay focused on final list element if down pressed at list end', function() {
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
clickDropdownToggle();
triggerKeyDown(element, 40);
triggerKeyDown(element, 40);
triggerKeyDown(dropdownMenu, 40);
triggerKeyDown(dropdownMenu, 40);
expect(element).toHaveClass(dropdownConfig.openClass);
var focusEl = element.find('ul').eq(0).find('a').eq(1);
@@ -615,16 +652,17 @@ describe('uib-dropdown', function() {
it('should close if esc is pressed while focused', function() {
element = dropdown('disabled');
var dropdownMenu = element.find('[uib-dropdown-menu]');
$document.find('body').append(element);
clickDropdownToggle();
triggerKeyDown(element, 40);
triggerKeyDown(dropdownMenu, 40);
expect(element).toHaveClass(dropdownConfig.openClass);
var focusEl = element.find('ul').eq(0).find('a').eq(0);
expect(focusEl).toHaveFocus();
triggerKeyDown(element, 27);
triggerKeyDown(dropdownMenu, 27);
expect(element).not.toHaveClass(dropdownConfig.openClass);
});
@@ -640,10 +678,10 @@ describe('uib-dropdown', function() {
it('should focus first list element when down arrow pressed', function() {
clickDropdownToggle();
triggerKeyDown(element, 40);
var dropdownMenu = $document.find('#dropdown-menu');
triggerKeyDown(dropdownMenu, 40);
expect(dropdownMenu.parent()).toHaveClass(dropdownConfig.appendToOpenClass);
var focusEl = $document.find('ul').eq(0).find('a');
expect(focusEl).toHaveFocus();
@@ -651,11 +689,10 @@ describe('uib-dropdown', function() {
it('should focus second list element when down arrow pressed twice', function() {
clickDropdownToggle();
triggerKeyDown(element, 40);
triggerKeyDown(element, 40);
triggerKeyDown(element, 40);
var dropdownMenu = $document.find('#dropdown-menu');
triggerKeyDown(dropdownMenu, 40);
triggerKeyDown(dropdownMenu, 40);
triggerKeyDown(dropdownMenu, 40);
expect(dropdownMenu.parent()).toHaveClass(dropdownConfig.appendToOpenClass);
var elem1 = $document.find('ul');
@@ -665,4 +702,34 @@ describe('uib-dropdown', function() {
});
});
});
// issue #5942
describe('using dropdown-append-to-body with dropdown-menu-right class', function() {
function dropdown() {
return $compile('<li style="float: right;" uib-dropdown dropdown-append-to-body><a href uib-dropdown-toggle>Toggle menu</a><ul uib-dropdown-menu class="dropdown-menu-right" id="dropdown-menu"><li><a href>Hello On Body</a></li></ul></li>')($rootScope);
}
beforeEach(function() {
element = dropdown();
$document.find('body').append(element);
var menu = $document.find('#dropdown-menu');
menu.css('position', 'absolute');
});
afterEach(function() {
element.remove();
});
it('should align the menu correctly when the body has no vertical scrollbar', function() {
var toggle = element.find('[uib-dropdown-toggle]');
var menu = $document.find('#dropdown-menu');
toggle.trigger('click');
// Get the offsets of the rightmost position of both the toggle and the menu (offset from the left of the window)
var toggleRight = Math.round(toggle.offset().left + toggle.outerWidth());
var menuRight = Math.round(menu.offset().left + menu.outerWidth());
expect(menuRight).toBe(toggleRight);
});
});
});
+2
View File
@@ -135,3 +135,5 @@ Events fired:
##### UI Router resolves
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.
+1 -2
View File
@@ -1,11 +1,10 @@
require('../position/index-nocss.js');
require('../stackedMap');
require('../../template/modal/backdrop.html.js');
require('../../template/modal/window.html.js');
require('./modal');
var MODULE_NAME = 'ui.bootstrap.module.modal';
angular.module(MODULE_NAME, ['ui.bootstrap.modal', 'uib/template/modal/backdrop.html', 'uib/template/modal/window.html']);
angular.module(MODULE_NAME, ['ui.bootstrap.modal', 'uib/template/modal/window.html']);
module.exports = MODULE_NAME;
+53 -40
View File
@@ -106,8 +106,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
.directive('uibModalBackdrop', ['$animate', '$injector', '$uibModalStack',
function($animate, $injector, $modalStack) {
return {
replace: true,
templateUrl: 'uib/template/modal/backdrop.html',
restrict: 'A',
compile: function(tElement, tAttrs) {
tElement.addClass(tAttrs.backdropClass);
return linkFn;
@@ -136,13 +135,12 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
scope: {
index: '@'
},
replace: true,
restrict: 'A',
transclude: true,
templateUrl: function(tElement, tAttrs) {
return tAttrs.templateUrl || 'uib/template/modal/window.html';
},
link: function(scope, element, attrs) {
element.addClass(attrs.windowClass || '');
element.addClass(attrs.windowTopClass || '');
scope.size = attrs.size;
@@ -167,12 +165,9 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
// Deferred object that will be resolved when this modal is render.
var modalRenderDeferObj = $q.defer();
// Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready.
// In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template.
attrs.$observe('modalRender', function(value) {
if (value === 'true') {
modalRenderDeferObj.resolve();
}
// Resolve render promise post-digest
scope.$$postDigest(function() {
modalRenderDeferObj.resolve();
});
modalRenderDeferObj.promise.then(function() {
@@ -235,16 +230,16 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
};
})
.directive('uibModalTransclude', function() {
.directive('uibModalTransclude', ['$animate', function($animate) {
return {
link: function(scope, element, attrs, controller, transclude) {
transclude(scope.$parent, function(clone) {
element.empty();
element.append(clone);
$animate.enter(clone, element);
});
}
};
})
}])
.factory('$uibModalStack', ['$animate', '$animateCss', '$document',
'$compile', '$rootScope', '$q', '$$multiMap', '$$stackedMap', '$uibPosition',
@@ -261,9 +256,9 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
var previousTopOpenedModal = null;
//Modal focus behavior
var tabableSelector = 'a[href], area[href], input:not([disabled]), ' +
'button:not([disabled]),select:not([disabled]), textarea:not([disabled]), ' +
'iframe, object, embed, *[tabindex], *[contenteditable=true]';
var tabbableSelector = 'a[href], area[href], input:not([disabled]):not([tabindex=\'-1\']), ' +
'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;
function isVisible(element) {
@@ -380,6 +375,10 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
afterAnimating.done = true;
$animate.leave(domEl).then(function() {
if (done) {
done();
}
domEl.remove();
if (closedDeferred) {
closedDeferred.resolve();
@@ -387,9 +386,6 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
});
scope.$destroy();
if (done) {
done();
}
}
}
@@ -477,15 +473,26 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
backdropScope.modalOptions = modal;
backdropScope.index = currBackdropIndex;
backdropDomEl = angular.element('<div uib-modal-backdrop="modal-backdrop"></div>');
backdropDomEl.attr('backdrop-class', modal.backdropClass);
backdropDomEl.attr({
'class': 'modal-backdrop',
'ng-style': '{\'z-index\': 1040 + (index && 1 || 0) + index*10}',
'uib-modal-animation-class': 'fade',
'modal-in-class': 'in'
});
if (modal.backdropClass) {
backdropDomEl.addClass(modal.backdropClass);
}
if (modal.animation) {
backdropDomEl.attr('modal-animation', 'true');
}
$compile(backdropDomEl)(backdropScope);
$animate.enter(backdropDomEl, appendToElement);
scrollbarPadding = $uibPosition.scrollbarPadding(appendToElement);
if (scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) {
appendToElement.css({paddingRight: scrollbarPadding.right + 'px'});
if ($uibPosition.isScrollable(appendToElement)) {
scrollbarPadding = $uibPosition.scrollbarPadding(appendToElement);
if (scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) {
appendToElement.css({paddingRight: scrollbarPadding.right + 'px'});
}
}
}
@@ -493,13 +500,22 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
topModalIndex = previousTopOpenedModal ? parseInt(previousTopOpenedModal.value.modalDomEl.attr('index'), 10) + 1 : 0;
var angularDomEl = angular.element('<div uib-modal-window="modal-window"></div>');
angularDomEl.attr({
'class': 'modal',
'template-url': modal.windowTemplateUrl,
'window-class': modal.windowClass,
'window-top-class': modal.windowTopClass,
'role': 'dialog',
'size': modal.size,
'index': topModalIndex,
'animate': 'animate'
'animate': 'animate',
'ng-style': '{\'z-index\': 1050 + index*10, display: \'block\'}',
'tabindex': -1,
'uib-modal-animation-class': 'fade',
'modal-in-class': 'in'
}).html(modal.content);
if (modal.windowClass) {
angularDomEl.addClass(modal.windowClass);
}
if (modal.animation) {
angularDomEl.attr('modal-animation', 'true');
}
@@ -599,7 +615,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
if (modalWindow) {
var modalDomE1 = modalWindow.value.modalDomEl;
if (modalDomE1 && modalDomE1.length) {
var elements = modalDomE1[0].querySelectorAll(tabableSelector);
var elements = modalDomE1[0].querySelectorAll(tabbableSelector);
return elements ?
Array.prototype.filter.call(elements, function(element) {
return isVisible(element);
@@ -695,30 +711,27 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
//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;
});
// 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
ctrlInstantiate = $controller(modalOptions.controller, ctrlLocals, true);
if (modalOptions.controllerAs) {
ctrlInstantiate = $controller(modalOptions.controller, ctrlLocals, true, modalOptions.controllerAs);
if (modalOptions.controllerAs && modalOptions.bindToController) {
ctrlInstance = ctrlInstantiate.instance;
if (modalOptions.bindToController) {
ctrlInstance.$close = modalScope.$close;
ctrlInstance.$dismiss = modalScope.$dismiss;
angular.extend(ctrlInstance, providedScope);
}
ctrlInstance = ctrlInstantiate();
modalScope[modalOptions.controllerAs] = ctrlInstance;
} else {
ctrlInstance = ctrlInstantiate();
ctrlInstance.$close = modalScope.$close;
ctrlInstance.$dismiss = modalScope.$dismiss;
angular.extend(ctrlInstance, {
$resolve: ctrlLocals.$scope.$resolve
}, providedScope);
}
ctrlInstance = ctrlInstantiate();
if (angular.isFunction(ctrlInstance.$onInit)) {
ctrlInstance.$onInit();
}
+127 -19
View File
@@ -46,13 +46,68 @@ describe('$uibResolve', function() {
});
});
describe('uibModalTransclude', function() {
var uibModalTranscludeDDO,
$animate;
beforeEach(module('ui.bootstrap.modal'));
beforeEach(module(function($provide) {
$animate = jasmine.createSpyObj('$animate', ['enter']);
$provide.value('$animate', $animate);
}));
beforeEach(inject(function(uibModalTranscludeDirective) {
uibModalTranscludeDDO = uibModalTranscludeDirective[0];
}));
describe('when initialised', function() {
var scope,
element,
transcludeSpy,
transcludeFn;
beforeEach(function() {
scope = {
$parent: 'parentScope'
};
element = jasmine.createSpyObj('containerElement', ['empty']);
transcludeSpy = jasmine.createSpy('transcludeSpy').and.callFake(function(scope, fn) {
transcludeFn = fn;
});
uibModalTranscludeDDO.link(scope, element, {}, {}, transcludeSpy);
});
it('should call the transclusion function', function() {
expect(transcludeSpy).toHaveBeenCalledWith(scope.$parent, jasmine.any(Function));
});
describe('transclusion callback', function() {
var transcludedContent;
beforeEach(function() {
transcludedContent = 'my transcluded content';
transcludeFn(transcludedContent);
});
it('should empty the element', function() {
expect(element.empty).toHaveBeenCalledWith();
});
it('should append the transcluded content', function() {
expect($animate.enter).toHaveBeenCalledWith(transcludedContent, element);
});
});
});
});
describe('$uibModal', function() {
var $animate, $controllerProvider, $rootScope, $document, $compile, $templateCache, $timeout, $q;
var $uibModal, $uibModalStack, $uibModalProvider;
beforeEach(module('ngAnimateMock'));
beforeEach(module('ui.bootstrap.modal'));
beforeEach(module('uib/template/modal/backdrop.html'));
beforeEach(module('uib/template/modal/window.html'));
beforeEach(module(function(_$controllerProvider_, _$uibModalProvider_, $compileProvider) {
$controllerProvider = _$controllerProvider_;
@@ -807,6 +862,52 @@ describe('$uibModal', function() {
initialPage.remove();
});
it('should change focus to next tabbable element when tab is pressed', function() {
var initialPage = angular.element('<a href="#" id="cannot-get-focus-from-modal">Outland link</a>');
angular.element(document.body).append(initialPage);
initialPage.focus();
open({
template:'<button id="tab-focus-button1" tabindex="-1">Skip me!</button><a href="#" id="tab-focus-link1">a</a>' +
'<a href="#" id="tab-focus-link2">b</a><a href="#" id="tab-focus-link3">c</a>' +
'<button id="tab-focus-button2" tabindex="-1">Skip me!</button>',
keyboard: false
});
$rootScope.$digest();
expect($document).toHaveModalsOpen(1);
$('#tab-focus-link3').focus();
expect(document.activeElement.getAttribute('id')).toBe('tab-focus-link3');
triggerKeyDown(angular.element(document.activeElement), 9, false);
expect(document.activeElement.getAttribute('id')).toBe('tab-focus-link1');
initialPage.remove();
});
it('should change focus to previous tabbable element when shift+tab is pressed', function() {
var initialPage = angular.element('<a href="#" id="cannot-get-focus-from-modal">Outland link</a>');
angular.element(document.body).append(initialPage);
initialPage.focus();
open({
template:'<button id="tab-focus-button1" tabindex="-1">Skip me!</button><a href="#" id="tab-focus-link1">a</a>' +
'<a href="#" id="tab-focus-link2">b</a><a href="#" id="tab-focus-link3">c</a>' +
'<button id="tab-focus-button2" tabindex="-1">Skip me!</button>',
keyboard: false
});
$rootScope.$digest();
expect($document).toHaveModalsOpen(1);
$('#tab-focus-link1').focus();
expect(document.activeElement.getAttribute('id')).toBe('tab-focus-link1');
triggerKeyDown(angular.element(document.activeElement), 9, true);
expect(document.activeElement.getAttribute('id')).toBe('tab-focus-link3');
initialPage.remove();
});
});
describe('default options can be changed in a provider', function() {
@@ -1053,6 +1154,20 @@ describe('$uibModal', function() {
});
expect($document).toHaveModalOpenWithContent('Content from root scope', 'div');
});
it('should expose $resolve in template', function() {
open({
controller: function($scope) {},
resolve: {
$foo: function() {
return 'Content from resolve';
}
},
template: '<div>{{$resolve.$foo}}</div>'
});
expect($document).toHaveModalOpenWithContent('Content from resolve', 'div');
});
});
describe('keyboard', function () {
@@ -1275,7 +1390,7 @@ describe('$uibModal', function() {
expect(body).not.toHaveClass('modal-open');
});
it('should remove the custom class on closing of modal', function() {
it('should remove the custom class on closing of modal after animations have completed', function() {
var modal = open({
template: '<div>dummy modal</div>',
openedClass: 'foo'
@@ -1283,7 +1398,13 @@ describe('$uibModal', function() {
expect(body).toHaveClass('foo');
close(modal);
close(modal, null, true);
expect(body).toHaveClass('foo');
$animate.flush();
$rootScope.$digest();
$animate.flush();
$rootScope.$digest();
expect(body).not.toHaveClass('foo');
});
@@ -1355,15 +1476,6 @@ describe('$uibModal', function() {
expect($rootScope.foo).toBeTruthy();
});
it('should support custom CSS classes as string', function() {
$rootScope.animate = false;
var windowEl = $compile('<div uib-modal-window animate="animate" window-class="test foo">content</div>')($rootScope);
$rootScope.$digest();
expect(windowEl).toHaveClass('test');
expect(windowEl).toHaveClass('foo');
});
it('should support window top class', function () {
$rootScope.animate = false;
var windowEl = $compile('<div uib-modal-window animate="animate" window-top-class="test foo">content</div>')($rootScope);
@@ -1374,13 +1486,12 @@ describe('$uibModal', function() {
});
it('should support custom template url', inject(function($templateCache) {
$templateCache.put('window.html', '<div class="mywindow" ng-transclude></div>');
$templateCache.put('window.html', '<div ng-transclude></div>');
var windowEl = $compile('<div uib-modal-window template-url="window.html" window-class="test">content</div>')($rootScope);
var windowEl = $compile('<div uib-modal-window template-url="window.html">content</div>')($rootScope);
$rootScope.$digest();
expect(windowEl).toHaveClass('mywindow');
expect(windowEl).toHaveClass('test');
expect(windowEl.html()).toBe('<div ng-transclude=""><span class="ng-scope">content</span></div>');
}));
});
@@ -1666,7 +1777,6 @@ describe('$uibModal', function() {
content: '<div>Modal1</div>'
});
expect($document).toHaveModalsOpen(0);
$rootScope.$digest();
$animate.flush();
expect($document).toHaveModalsOpen(1);
@@ -1686,7 +1796,6 @@ describe('$uibModal', function() {
modal2Index = parseInt($uibModalStack.getTop().value.modalDomEl.attr('index'), 10);
});
expect($document).toHaveModalsOpen(1);
$rootScope.$digest();
$animate.flush();
expect($document).toHaveModalsOpen(2);
@@ -1708,7 +1817,6 @@ describe('$uibModal', function() {
modal3Index = parseInt($uibModalStack.getTop().value.modalDomEl.attr('index'), 10);
});
expect($document).toHaveModalsOpen(1);
$rootScope.$digest();
$animate.flush();
expect($document).toHaveModalsOpen(2);
+1 -1
View File
@@ -1,5 +1,5 @@
<div ng-controller="PagerDemoCtrl">
<h4>Pager</h4>
<pre>You are currently on page {{currentPage}}</pre>
<uib-pager total-items="totalItems" ng-model="currentPage"></uib-pager>
<ul uib-pager total-items="totalItems" ng-model="currentPage"></ul>
</div>
+1
View File
@@ -1,4 +1,5 @@
require('../paging');
require('../tabindex');
require('../../template/pager/pager.html.js');
require('./pager');
+3 -2
View File
@@ -1,4 +1,4 @@
angular.module('ui.bootstrap.pager', ['ui.bootstrap.paging'])
angular.module('ui.bootstrap.pager', ['ui.bootstrap.paging', 'ui.bootstrap.tabindex'])
.controller('UibPagerController', ['$scope', '$attrs', 'uibPaging', 'uibPagerConfig', function($scope, $attrs, uibPaging, uibPagerConfig) {
$scope.align = angular.isDefined($attrs.align) ? $scope.$parent.$eval($attrs.align) : uibPagerConfig.align;
@@ -22,13 +22,14 @@ angular.module('ui.bootstrap.pager', ['ui.bootstrap.paging'])
ngDisabled: '='
},
require: ['uibPager', '?ngModel'],
restrict: 'A',
controller: 'UibPagerController',
controllerAs: 'pager',
templateUrl: function(element, attrs) {
return attrs.templateUrl || 'uib/template/pager/pager.html';
},
replace: true,
link: function(scope, element, attrs, ctrls) {
element.addClass('pager');
var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
if (!ngModelCtrl) {
+12 -12
View File
@@ -10,7 +10,7 @@ describe('pager directive', function() {
$document = _$document_;
$templateCache = _$templateCache_;
body = $document.find('body');
element = $compile('<uib-pager total-items="total" ng-model="currentPage"></uib-pager>')($rootScope);
element = $compile('<ul uib-pager total-items="total" ng-model="currentPage"></ul>')($rootScope);
$rootScope.$digest();
}));
@@ -56,7 +56,7 @@ describe('pager directive', function() {
it('exposes the controller on the template', function() {
$templateCache.put('uib/template/pager/pager.html', '<div>{{pager.text}}</div>');
element = $compile('<uib-pager></uib-pager>')($rootScope);
element = $compile('<ul uib-pager></ul uib-pager>')($rootScope);
$rootScope.$digest();
var ctrl = element.controller('uibPager');
@@ -65,7 +65,7 @@ describe('pager directive', function() {
ctrl.text = 'foo';
$rootScope.$digest();
expect(element.html()).toBe('foo');
expect(element.html()).toBe('<div class="ng-binding">foo</div>');
});
it('disables the "previous" link if current page is 1', function() {
@@ -102,7 +102,7 @@ describe('pager directive', function() {
it('executes the `ng-change` expression when an element is clicked', function() {
$rootScope.selectPageHandler = jasmine.createSpy('selectPageHandler');
element = $compile('<uib-pager total-items="total" ng-model="currentPage" ng-change="selectPageHandler()"></uib-pager>')($rootScope);
element = $compile('<ul uib-pager total-items="total" ng-model="currentPage" ng-change="selectPageHandler()"></ul>')($rootScope);
$rootScope.$digest();
clickPaginationEl(-1);
@@ -147,16 +147,16 @@ describe('pager directive', function() {
it('allows custom templates', function() {
$templateCache.put('foo/bar.html', '<div>baz</div>');
element = $compile('<uib-pager template-url="foo/bar.html"></uib-pager>')($rootScope);
element = $compile('<ul uib-pager template-url="foo/bar.html"></ul>')($rootScope);
$rootScope.$digest();
expect(element.html()).toBe('baz');
expect(element.html()).toBe('<div>baz</div>');
});
describe('`items-per-page`', function() {
beforeEach(function() {
$rootScope.perpage = 5;
element = $compile('<uib-pager total-items="total" items-per-page="perpage" ng-model="currentPage"></uib-pager>')($rootScope);
element = $compile('<ul uib-pager total-items="total" items-per-page="perpage" ng-model="currentPage"></ul>')($rootScope);
$rootScope.$digest();
});
@@ -190,7 +190,7 @@ describe('pager directive', function() {
describe('`num-pages`', function() {
beforeEach(function() {
$rootScope.numpg = null;
element = $compile('<uib-pager total-items="total" ng-model="currentPage" num-pages="numpg"></uib-pager>')($rootScope);
element = $compile('<ul uib-pager total-items="total" ng-model="currentPage" num-pages="numpg"></ul>')($rootScope);
$rootScope.$digest();
});
@@ -206,7 +206,7 @@ describe('pager directive', function() {
uibPagerConfig.previousText = 'PR';
uibPagerConfig.nextText = 'NE';
uibPagerConfig.align = false;
element = $compile('<uib-pager total-items="total" ng-model="currentPage"></uib-pager>')($rootScope);
element = $compile('<ul uib-pager total-items="total" ng-model="currentPage"></ul>')($rootScope);
$rootScope.$digest();
}));
afterEach(inject(function(uibPagerConfig) {
@@ -227,7 +227,7 @@ describe('pager directive', function() {
describe('override configuration from attributes', function() {
beforeEach(function() {
element = $compile('<uib-pager align="false" previous-text="<" next-text=">" total-items="total" ng-model="currentPage"></uib-pager>')($rootScope);
element = $compile('<ul uib-pager align="false" previous-text="<" next-text=">" total-items="total" ng-model="currentPage"></ul>')($rootScope);
$rootScope.$digest();
});
@@ -248,7 +248,7 @@ describe('pager directive', function() {
it('changes "previous" & "next" text from interpolated attributes', function() {
$rootScope.previousText = '<<';
$rootScope.nextText = '>>';
element = $compile('<uib-pager align="false" previous-text="{{previousText}}" next-text="{{nextText}}" total-items="total" ng-model="currentPage"></uib-pager>')($rootScope);
element = $compile('<ul uib-pager align="false" previous-text="{{previousText}}" next-text="{{nextText}}" total-items="total" ng-model="currentPage"></ul>')($rootScope);
$rootScope.$digest();
expect(getPaginationEl(0).text()).toBe('<<');
@@ -259,7 +259,7 @@ describe('pager directive', function() {
it('disables the component when ng-disabled is true', function() {
$rootScope.disable = true;
element = $compile('<uib-pager total-items="total" ng-disabled="disable" ng-model="currentPage"></uib-pager>')($rootScope);
element = $compile('<ul uib-pager total-items="total" ng-disabled="disable" ng-model="currentPage"></ul>')($rootScope);
$rootScope.$digest();
updateCurrentPage(2);
+9 -9
View File
@@ -1,24 +1,24 @@
<div ng-controller="PaginationDemoCtrl">
<h4>Default</h4>
<uib-pagination total-items="totalItems" ng-model="currentPage" ng-change="pageChanged()"></uib-pagination>
<uib-pagination boundary-links="true" total-items="totalItems" ng-model="currentPage" class="pagination-sm" previous-text="&lsaquo;" next-text="&rsaquo;" first-text="&laquo;" last-text="&raquo;"></uib-pagination>
<uib-pagination direction-links="false" boundary-links="true" total-items="totalItems" ng-model="currentPage"></uib-pagination>
<uib-pagination direction-links="false" total-items="totalItems" ng-model="currentPage" num-pages="smallnumPages"></uib-pagination>
<ul uib-pagination total-items="totalItems" ng-model="currentPage" ng-change="pageChanged()"></ul>
<ul uib-pagination boundary-links="true" total-items="totalItems" ng-model="currentPage" class="pagination-sm" previous-text="&lsaquo;" next-text="&rsaquo;" first-text="&laquo;" last-text="&raquo;"></ul>
<ul uib-pagination direction-links="false" boundary-links="true" total-items="totalItems" ng-model="currentPage"></ul>
<ul uib-pagination direction-links="false" total-items="totalItems" ng-model="currentPage" num-pages="smallnumPages"></ul>
<pre>The selected page no: {{currentPage}}</pre>
<button type="button" class="btn btn-info" ng-click="setPage(3)">Set current page to: 3</button>
<hr />
<h4>Limit the maximum visible buttons</h4>
<h6><code>rotate</code> defaulted to <code>true</code>:</h6>
<uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-links="true" num-pages="numPages"></uib-pagination>
<ul uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-links="true" num-pages="numPages"></ul>
<h6><code>rotate</code> defaulted to <code>true</code> and <code>force-ellipses</code> set to <code>true</code>:</h6>
<uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-links="true" force-ellipses="true"></uib-pagination>
<ul uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-links="true" force-ellipses="true"></ul>
<h6><code>rotate</code> set to <code>false</code>:</h6>
<uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-links="true" rotate="false"></uib-pagination>
<ul uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-links="true" rotate="false"></ul>
<h6><code>boundary-link-numbers</code> set to <code>true</code> and <code>rotate</code> defaulted to <code>true</code>:</h6>
<uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-link-numbers="true"></uib-pagination>
<ul uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-link-numbers="true"></ul>
<h6><code>boundary-link-numbers</code> set to <code>true</code> and <code>rotate</code> set to <code>false</code>:</h6>
<uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-link-numbers="true" rotate="false"></uib-pagination>
<ul uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-link-numbers="true" rotate="false"></ul>
<pre>Page: {{bigCurrentPage}} / {{numPages}}</pre>
</div>
+1
View File
@@ -1,4 +1,5 @@
require('../paging');
require('../tabindex');
require('../../template/pagination/pagination.html.js');
require('./pagination');
+3 -2
View File
@@ -1,4 +1,4 @@
angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging'])
angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging', 'ui.bootstrap.tabindex'])
.controller('UibPaginationController', ['$scope', '$attrs', '$parse', 'uibPaging', 'uibPaginationConfig', function($scope, $attrs, $parse, uibPaging, uibPaginationConfig) {
var ctrl = this;
// Setup configuration parameters
@@ -132,13 +132,14 @@ angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging'])
ngDisabled:'='
},
require: ['uibPagination', '?ngModel'],
restrict: 'A',
controller: 'UibPaginationController',
controllerAs: 'pagination',
templateUrl: function(element, attrs) {
return attrs.templateUrl || 'uib/template/pagination/pagination.html';
},
replace: true,
link: function(scope, element, attrs, ctrls) {
element.addClass('pagination');
var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
if (!ngModelCtrl) {
+27 -27
View File
@@ -11,7 +11,7 @@ describe('pagination directive', function() {
$document = _$document_;
$templateCache = _$templateCache_;
body = $document.find('body');
element = $compile('<uib-pagination total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage"></ul>')($rootScope);
$rootScope.$digest();
}));
@@ -58,7 +58,7 @@ describe('pagination directive', function() {
$templateCache.put('uib/template/pagination/pagination.html', '<div>{{pagination.randomText}}</div>');
var scope = $rootScope.$new();
element = $compile('<uib-pagination></uib-pagination>')(scope);
element = $compile('<ul uib-pagination></ul>')(scope);
$rootScope.$digest();
var ctrl = element.controller('uibPagination');
@@ -68,17 +68,17 @@ describe('pagination directive', function() {
ctrl.randomText = 'foo';
$rootScope.$digest();
expect(element.html()).toBe('foo');
expect(element.html()).toBe('<div class="ng-binding">foo</div>');
});
it('allows custom templates', function() {
$templateCache.put('foo/bar.html', '<div>baz</div>');
var scope = $rootScope.$new();
element = $compile('<uib-pagination template-url="foo/bar.html"></uib-pagination>')(scope);
element = $compile('<ul uib-pagination template-url="foo/bar.html"></ul>')(scope);
$rootScope.$digest();
expect(element.html()).toBe('baz');
expect(element.html()).toBe('<div>baz</div>');
});
it('contains num-pages + 2 li elements', function() {
@@ -213,7 +213,7 @@ describe('pagination directive', function() {
describe('`items-per-page`', function() {
beforeEach(function() {
$rootScope.perpage = 5;
element = $compile('<uib-pagination total-items="total" items-per-page="perpage" ng-model="currentPage"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination total-items="total" items-per-page="perpage" ng-model="currentPage"></ul>')($rootScope);
$rootScope.$digest();
});
@@ -256,7 +256,7 @@ describe('pagination directive', function() {
describe('executes `ng-change` expression', function() {
beforeEach(function() {
$rootScope.selectPageHandler = jasmine.createSpy('selectPageHandler');
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" ng-change="selectPageHandler()"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage" ng-change="selectPageHandler()"></ul>')($rootScope);
$rootScope.$digest();
});
@@ -286,7 +286,7 @@ describe('pagination directive', function() {
$rootScope.total = 98; // 10 pages
$rootScope.currentPage = 3;
$rootScope.maxSize = 5;
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" max-size="maxSize"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage" max-size="maxSize"></ul>')($rootScope);
$rootScope.$digest();
});
@@ -367,7 +367,7 @@ describe('pagination directive', function() {
$rootScope.total = 98; // 10 pages
$rootScope.currentPage = 3;
$rootScope.maxSize = 5;
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" max-size="maxSize" force-ellipses="true"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage" max-size="maxSize" force-ellipses="true"></ul>')($rootScope);
$rootScope.$digest();
});
@@ -429,7 +429,7 @@ describe('pagination directive', function() {
$rootScope.total = 98; // 10 pages
$rootScope.currentPage = 3;
$rootScope.maxSize = 5;
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" max-size="maxSize" boundary-link-numbers="true"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage" max-size="maxSize" boundary-link-numbers="true"></ul>')($rootScope);
$rootScope.$digest();
});
@@ -522,7 +522,7 @@ describe('pagination directive', function() {
$rootScope.currentPage = 7;
$rootScope.maxSize = 5;
$rootScope.rotate = false;
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" max-size="maxSize" rotate="rotate"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage" max-size="maxSize" rotate="rotate"></ul>')($rootScope);
$rootScope.$digest();
});
@@ -581,7 +581,7 @@ describe('pagination directive', function() {
describe('pagination directive with `boundary-links`', function() {
beforeEach(function() {
element = $compile('<uib-pagination boundary-links="true" total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination boundary-links="true" total-items="total" ng-model="currentPage"></ul>')($rootScope);
$rootScope.$digest();
});
@@ -636,7 +636,7 @@ describe('pagination directive', function() {
});
it('changes "first" & "last" text from attributes', function() {
element = $compile('<uib-pagination boundary-links="true" first-text="<<<" last-text=">>>" total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination boundary-links="true" first-text="<<<" last-text=">>>" total-items="total" ng-model="currentPage"></ul>')($rootScope);
$rootScope.$digest();
expect(getPaginationEl(0).text()).toBe('<<<');
@@ -644,7 +644,7 @@ describe('pagination directive', function() {
});
it('changes "previous" & "next" text from attributes', function() {
element = $compile('<uib-pagination boundary-links="true" previous-text="<<" next-text=">>" total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination boundary-links="true" previous-text="<<" next-text=">>" total-items="total" ng-model="currentPage"></ul>')($rootScope);
$rootScope.$digest();
expect(getPaginationEl(1).text()).toBe('<<');
@@ -654,7 +654,7 @@ describe('pagination directive', function() {
it('changes "first" & "last" text from interpolated attributes', function() {
$rootScope.myfirstText = '<<<';
$rootScope.mylastText = '>>>';
element = $compile('<uib-pagination boundary-links="true" first-text="{{myfirstText}}" last-text="{{mylastText}}" total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination boundary-links="true" first-text="{{myfirstText}}" last-text="{{mylastText}}" total-items="total" ng-model="currentPage"></ul>')($rootScope);
$rootScope.$digest();
expect(getPaginationEl(0).text()).toBe('<<<');
@@ -664,7 +664,7 @@ describe('pagination directive', function() {
it('changes "previous" & "next" text from interpolated attributes', function() {
$rootScope.previousText = '<<';
$rootScope.nextText = '>>';
element = $compile('<uib-pagination boundary-links="true" previous-text="{{previousText}}" next-text="{{nextText}}" total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination boundary-links="true" previous-text="{{previousText}}" next-text="{{nextText}}" total-items="total" ng-model="currentPage"></ul>')($rootScope);
$rootScope.$digest();
expect(getPaginationEl(1).text()).toBe('<<');
@@ -700,7 +700,7 @@ describe('pagination directive', function() {
describe('pagination directive with just number links', function() {
beforeEach(function() {
element = $compile('<uib-pagination direction-links="false" total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination direction-links="false" total-items="total" ng-model="currentPage"></ul>')($rootScope);
$rootScope.$digest();
});
@@ -752,7 +752,7 @@ describe('pagination directive', function() {
describe('with just boundary & number links', function() {
beforeEach(function() {
$rootScope.directions = false;
element = $compile('<uib-pagination boundary-links="true" direction-links="directions" total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination boundary-links="true" direction-links="directions" total-items="total" ng-model="currentPage"></ul>')($rootScope);
$rootScope.$digest();
});
@@ -784,7 +784,7 @@ describe('pagination directive', function() {
describe('`num-pages`', function() {
beforeEach(function() {
$rootScope.numpg = null;
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" num-pages="numpg"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage" num-pages="numpg"></ul>')($rootScope);
$rootScope.$digest();
});
@@ -827,7 +827,7 @@ describe('pagination directive', function() {
paginationConfig.previousText = 'PR';
paginationConfig.nextText = 'NE';
paginationConfig.lastText = 'LA';
element = $compile('<uib-pagination total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage"></ul>')($rootScope);
$rootScope.$digest();
expect(getPaginationEl(0).text()).toBe('FI');
@@ -838,7 +838,7 @@ describe('pagination directive', function() {
it('contains number of pages + 2 li elements', function() {
paginationConfig.itemsPerPage = 5;
element = $compile('<uib-pagination total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage"></ul>')($rootScope);
$rootScope.$digest();
expect(getPaginationBarSize()).toBe(12);
@@ -846,7 +846,7 @@ describe('pagination directive', function() {
it('should take maxSize defaults into account', function() {
paginationConfig.maxSize = 2;
element = $compile('<uib-pagination total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage"></ul>')($rootScope);
$rootScope.$digest();
expect(getPaginationBarSize()).toBe(4);
@@ -854,7 +854,7 @@ describe('pagination directive', function() {
it('should take forceEllipses defaults into account', function () {
paginationConfig.forceEllipses = true;
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" max-size="2"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage" max-size="2"></ul>')($rootScope);
$rootScope.$digest();
// Should contain 2 nav buttons, 2 pages, and 2 ellipsis since the currentPage defaults to 3, which is in the middle
@@ -865,7 +865,7 @@ describe('pagination directive', function() {
paginationConfig.boundaryLinkNumbers = true;
$rootScope.total = 88; // 9 pages
$rootScope.currentPage = 5;
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" max-size="3"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage" max-size="3"></ul>')($rootScope);
$rootScope.$digest();
// Should contain 2 nav buttons, 2 pages, 2 ellipsis, and 2 extra end numbers since the currentPage is in the middle
@@ -879,7 +879,7 @@ describe('pagination directive', function() {
$rootScope.pageLabel = function(id) {
return 'test_'+ id;
};
element = $compile('<uib-pagination boundary-links="true" page-label="pageLabel($page)" first-text="<<" previous-text="<" next-text=">" last-text=">>" total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination boundary-links="true" page-label="pageLabel($page)" first-text="<<" previous-text="<" next-text=">" last-text=">>" total-items="total" ng-model="currentPage"></ul>')($rootScope);
$rootScope.$digest();
});
@@ -904,7 +904,7 @@ describe('pagination directive', function() {
describe('disabled with ngDisable', function() {
beforeEach(function() {
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" ng-disabled="disabled"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage" ng-disabled="disabled"></ul>')($rootScope);
$rootScope.currentPage = 3;
$rootScope.$digest();
});
@@ -941,7 +941,7 @@ describe('pagination directive', function() {
it('should retain the model value when total-items starts as undefined', function() {
$rootScope.currentPage = 5;
$rootScope.total = undefined;
element = $compile('<uib-pagination total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage"></ul>')($rootScope);
$rootScope.$digest();
expect($rootScope.currentPage).toBe(5);
+2 -2
View File
@@ -34,9 +34,9 @@
<hr />
<h4>Triggers</h4>
<p>
<button uib-popover="I appeared on mouse enter!" popover-trigger="mouseenter" type="button" class="btn btn-default">Mouseenter</button>
<button uib-popover="I appeared on mouse enter!" popover-trigger="'mouseenter'" type="button" class="btn btn-default">Mouseenter</button>
</p>
<input type="text" value="Click me!" uib-popover="I appeared on focus! Click away and I'll vanish..." popover-trigger="focus" class="form-control">
<input type="text" value="Click me!" uib-popover="I appeared on focus! Click away and I'll vanish..." popover-trigger="'focus'" class="form-control">
<hr />
<h4>Other</h4>
+3 -2
View File
@@ -76,8 +76,9 @@ All these settings are available for the three types of popovers.
A string to display as a fancy title.
* `popover-trigger`
_(Default: `click`)_ -
What should trigger a show of the popover? Supports a space separated list of event names (see below).
<small class="badge">$</small>
_(Default: `'click'`)_ -
What should trigger a show of the popover? Supports a space separated list of event names, or objects (see below).
**Note:** To configure the tooltips, you need to do it on `$uibTooltipProvider` (also see below).
+6 -7
View File
@@ -7,9 +7,8 @@ angular.module('ui.bootstrap.popover', ['ui.bootstrap.tooltip'])
.directive('uibPopoverTemplatePopup', function() {
return {
replace: true,
scope: { uibTitle: '@', contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
originScope: '&' },
restrict: 'A',
scope: { uibTitle: '@', contentExp: '&', originScope: '&' },
templateUrl: 'uib/template/popover/popover-template.html'
};
})
@@ -22,8 +21,8 @@ angular.module('ui.bootstrap.popover', ['ui.bootstrap.tooltip'])
.directive('uibPopoverHtmlPopup', function() {
return {
replace: true,
scope: { contentExp: '&', uibTitle: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
restrict: 'A',
scope: { contentExp: '&', uibTitle: '@' },
templateUrl: 'uib/template/popover/popover-html.html'
};
})
@@ -36,8 +35,8 @@ angular.module('ui.bootstrap.popover', ['ui.bootstrap.tooltip'])
.directive('uibPopoverPopup', function() {
return {
replace: true,
scope: { uibTitle: '@', content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
restrict: 'A',
scope: { uibTitle: '@', content: '@' },
templateUrl: 'uib/template/popover/popover.html'
};
})
+2
View File
@@ -21,6 +21,7 @@ describe('popover', function() {
scope.template = $sce.trustAsHtml('<span>My template</span>');
$compile(elmBody)(scope);
scope.$digest();
$document.find('body').append(elmBody);
elm = elmBody.find('span');
elmScope = elm.scope();
tooltipScope = elmScope.$$childTail;
@@ -87,6 +88,7 @@ describe('popover', function() {
it('should hide popover when template becomes empty', inject(function($timeout) {
elm.trigger('click');
tooltipScope.$digest();
$timeout.flush(0);
expect(tooltipScope.isOpen).toBe(true);
scope.template = '';
@@ -25,6 +25,7 @@ describe('popover template', function() {
scope = $rootScope;
$compile(elmBody)(scope);
$document.find('body').append(elmBody);
scope.templateUrl = 'myUrl';
scope.$digest();
@@ -35,6 +36,7 @@ describe('popover template', function() {
afterEach(function() {
$document.off('keypress');
elmBody.remove();
});
it('should open on click', inject(function() {
@@ -75,6 +77,7 @@ describe('popover template', function() {
it('should hide popover when template becomes empty', inject(function($timeout) {
elm.trigger('click');
tooltipScope.$digest();
$timeout.flush(0);
expect(tooltipScope.isOpen).toBe(true);
scope.templateUrl = '';
+27 -7
View File
@@ -529,13 +529,33 @@ angular.module('ui.bootstrap.position', [])
},
/**
* Provides a way for positioning tooltip & dropdown
* arrows when using placement options beyond the standard
* left, right, top, or bottom.
*
* @param {element} elem - The tooltip/dropdown element.
* @param {string} placement - The placement for the elem.
*/
* Provides a way to adjust the top positioning after first
* render to correctly align element to top after content
* rendering causes resized element height
*
* @param {array} placementClasses - The array of strings of classes
* element should have.
* @param {object} containerPosition - The object with container
* position information
* @param {number} initialHeight - The initial height for the elem.
* @param {number} currentHeight - The current height for the elem.
*/
adjustTop: function(placementClasses, containerPosition, initialHeight, currentHeight) {
if (placementClasses.indexOf('top') !== -1 && initialHeight !== currentHeight) {
return {
top: containerPosition.top - currentHeight + 'px'
};
}
},
/**
* Provides a way for positioning tooltip & dropdown
* arrows when using placement options beyond the standard
* left, right, top, or bottom.
*
* @param {element} elem - The tooltip/dropdown element.
* @param {string} placement - The placement for the elem.
*/
positionArrow: function(elem, placement) {
elem = this.getRawNode(elem);
+1 -1
View File
@@ -58,7 +58,7 @@ It supports multiple (stacked) `<uib-bar>` into the same `<uib-progress>` elemen
* `type`
_(Default: `null`)_ -
Bootstrap style type. Possible values are 'success', 'info', 'warning', and, 'error' to use Bootstrap's pre-existing styling, or any desired custom suffix.
Bootstrap style type. Possible values are 'success', 'info', 'warning', and, 'danger' to use Bootstrap's pre-existing styling, or any desired custom suffix.
* `title`
_(Default: `progressbar`)_ -
+3 -3
View File
@@ -1,6 +1,6 @@
<div ng-controller="RatingDemoCtrl">
<h4>Default</h4>
<uib-rating ng-model="rate" max="max" read-only="isReadonly" on-hover="hoveringOver(value)" on-leave="overStar = null" titles="['one','two','three']" aria-labelledby="default-rating"></uib-rating>
<span uib-rating ng-model="rate" max="max" read-only="isReadonly" on-hover="hoveringOver(value)" on-leave="overStar = null" titles="['one','two','three']" aria-labelledby="default-rating"></span>
<span class="label" ng-class="{'label-warning': percent<30, 'label-info': percent>=30 && percent<70, 'label-success': percent>=70}" ng-show="overStar && !isReadonly">{{percent}}%</span>
<pre style="margin:15px 0;">Rate: <b>{{rate}}</b> - Readonly is: <i>{{isReadonly}}</i> - Hovering over: <b>{{overStar || "none"}}</b></pre>
@@ -10,6 +10,6 @@
<hr />
<h4>Custom icons</h4>
<div ng-init="x = 5"><uib-rating ng-model="x" max="15" state-on="'glyphicon-ok-sign'" state-off="'glyphicon-ok-circle'" aria-labelledby="custom-icons-1"></uib-rating> <b>(<i>Rate:</i> {{x}})</b></div>
<div ng-init="y = 2"><uib-rating ng-model="y" rating-states="ratingStates" aria-labelledby="custom-icons-2"></uib-rating> <b>(<i>Rate:</i> {{y}})</b></div>
<div ng-init="x = 5"><span uib-rating ng-model="x" max="15" state-on="'glyphicon-ok-sign'" state-off="'glyphicon-ok-circle'" aria-labelledby="custom-icons-1"></span> <b>(<i>Rate:</i> {{x}})</b></div>
<div ng-init="y = 2"><span uib-rating ng-model="y" rating-states="ratingStates" aria-labelledby="custom-icons-2"></span> <b>(<i>Rate:</i> {{y}})</b></div>
</div>
+2 -2
View File
@@ -5,7 +5,7 @@ angular.module('ui.bootstrap.rating', [])
stateOn: null,
stateOff: null,
enableReset: true,
titles : ['one', 'two', 'three', 'four', 'five']
titles: ['one', 'two', 'three', 'four', 'five']
})
.controller('UibRatingController', ['$scope', '$attrs', 'uibRatingConfig', function($scope, $attrs, ratingConfig) {
@@ -90,6 +90,7 @@ angular.module('ui.bootstrap.rating', [])
.directive('uibRating', function() {
return {
require: ['uibRating', 'ngModel'],
restrict: 'A',
scope: {
readonly: '=?readOnly',
onHover: '&',
@@ -97,7 +98,6 @@ angular.module('ui.bootstrap.rating', [])
},
controller: 'UibRatingController',
templateUrl: 'uib/template/rating/rating.html',
replace: true,
link: function(scope, element, attrs, ctrls) {
var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1];
ratingCtrl.init(ngModelCtrl);
+46 -33
View File
@@ -1,17 +1,18 @@
describe('rating directive', function() {
var $rootScope, $compile, element;
var $rootScope, $compile, element, innerElem;
beforeEach(module('ui.bootstrap.rating'));
beforeEach(module('uib/template/rating/rating.html'));
beforeEach(inject(function(_$compile_, _$rootScope_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$rootScope.rate = 3;
element = $compile('<uib-rating ng-model="rate"></uib-rating>')($rootScope);
element = $compile('<span uib-rating ng-model="rate"></span>')($rootScope);
$rootScope.$digest();
innerElem = element.children().eq(0);
}));
function getStars() {
return element.find('i');
return innerElem.find('i');
}
function getStar(number) {
@@ -38,17 +39,17 @@ describe('rating directive', function() {
function triggerKeyDown(keyCode) {
var e = $.Event('keydown');
e.which = keyCode;
element.trigger(e);
innerElem.trigger(e);
}
it('contains the default number of icons', function() {
expect(getStars().length).toBe(5);
expect(element.attr('aria-valuemax')).toBe('5');
expect(innerElem.attr('aria-valuemax')).toBe('5');
});
it('initializes the default star icons as selected', function() {
expect(getState()).toEqual([true, true, true, false, false]);
expect(element.attr('aria-valuenow')).toBe('3');
expect(innerElem.attr('aria-valuenow')).toBe('3');
});
it('handles correctly the click event', function() {
@@ -56,19 +57,19 @@ describe('rating directive', function() {
$rootScope.$digest();
expect(getState()).toEqual([true, true, false, false, false]);
expect($rootScope.rate).toBe(2);
expect(element.attr('aria-valuenow')).toBe('2');
expect(innerElem.attr('aria-valuenow')).toBe('2');
getStar(5).click();
$rootScope.$digest();
expect(getState()).toEqual([true, true, true, true, true]);
expect($rootScope.rate).toBe(5);
expect(element.attr('aria-valuenow')).toBe('5');
expect(innerElem.attr('aria-valuenow')).toBe('5');
getStar(5).click();
$rootScope.$digest();
expect(getState()).toEqual([false, false, false, false, false]);
expect($rootScope.rate).toBe(0);
expect(element.attr('aria-valuenow')).toBe('0');
expect(innerElem.attr('aria-valuenow')).toBe('0');
});
it('handles correctly the hover event', function() {
@@ -82,7 +83,7 @@ describe('rating directive', function() {
expect(getState()).toEqual([true, true, true, true, true]);
expect($rootScope.rate).toBe(3);
element.trigger('mouseout');
innerElem.trigger('mouseout');
expect(getState()).toEqual([true, true, true, false, false]);
expect($rootScope.rate).toBe(3);
});
@@ -92,13 +93,13 @@ describe('rating directive', function() {
$rootScope.$digest();
expect(getState()).toEqual([true, true, false, false, false]);
expect(element.attr('aria-valuenow')).toBe('2');
expect(innerElem.attr('aria-valuenow')).toBe('2');
$rootScope.rate = 2.5;
$rootScope.$digest();
expect(getState()).toEqual([true, true, true, false, false]);
expect(element.attr('aria-valuenow')).toBe('3');
expect(innerElem.attr('aria-valuenow')).toBe('3');
});
it('changes the number of selected icons when value changes', function() {
@@ -106,30 +107,33 @@ describe('rating directive', function() {
$rootScope.$digest();
expect(getState()).toEqual([true, true, false, false, false]);
expect(element.attr('aria-valuenow')).toBe('2');
expect(element.attr('aria-valuetext')).toBe('two');
expect(innerElem.attr('aria-valuenow')).toBe('2');
expect(innerElem.attr('aria-valuetext')).toBe('two');
});
it('shows different number of icons when `max` attribute is set', function() {
element = $compile('<uib-rating ng-model="rate" max="7"></uib-rating>')($rootScope);
element = $compile('<span uib-rating ng-model="rate" max="7"></span>')($rootScope);
$rootScope.$digest();
innerElem = element.children().eq(0);
expect(getStars().length).toBe(7);
expect(element.attr('aria-valuemax')).toBe('7');
expect(innerElem.attr('aria-valuemax')).toBe('7');
});
it('shows different number of icons when `max` attribute is from scope variable', function() {
$rootScope.max = 15;
element = $compile('<uib-rating ng-model="rate" max="max"></uib-rating>')($rootScope);
element = $compile('<span uib-rating ng-model="rate" max="max"></span>')($rootScope);
$rootScope.$digest();
innerElem = element.children().eq(0);
expect(getStars().length).toBe(15);
expect(element.attr('aria-valuemax')).toBe('15');
expect(innerElem.attr('aria-valuemax')).toBe('15');
});
it('handles read-only attribute', function() {
$rootScope.isReadonly = true;
element = $compile('<uib-rating ng-model="rate" read-only="isReadonly"></uib-rating>')($rootScope);
element = $compile('<span uib-rating ng-model="rate" read-only="isReadonly"></span>')($rootScope);
$rootScope.$digest();
innerElem = element.children().eq(0);
expect(getState()).toEqual([true, true, true, false, false]);
@@ -148,8 +152,9 @@ describe('rating directive', function() {
it('handles enable-reset attribute', function() {
$rootScope.canReset = false;
element = $compile('<uib-rating ng-model="rate" enable-reset="canReset"></uib-rating>')($rootScope);
element = $compile('<span uib-rating ng-model="rate" enable-reset="canReset"></span>')($rootScope);
$rootScope.$digest();
innerElem = element.children().eq(0);
var star = {
states: [true, true, true, true, true],
@@ -162,19 +167,20 @@ describe('rating directive', function() {
$rootScope.$digest();
expect(getState()).toEqual(star.states);
expect($rootScope.rate).toBe(5);
expect(element.attr('aria-valuenow')).toBe('5');
expect(innerElem.attr('aria-valuenow')).toBe('5');
selectStar.click();
$rootScope.$digest();
expect(getState()).toEqual(star.states);
expect($rootScope.rate).toBe(5);
expect(element.attr('aria-valuenow')).toBe('5');
expect(innerElem.attr('aria-valuenow')).toBe('5');
});
it('should fire onHover', function() {
$rootScope.hoveringOver = jasmine.createSpy('hoveringOver');
element = $compile('<uib-rating ng-model="rate" on-hover="hoveringOver(value)"></uib-rating>')($rootScope);
element = $compile('<span uib-rating ng-model="rate" on-hover="hoveringOver(value)"></span>')($rootScope);
$rootScope.$digest();
innerElem = element.children().eq(0);
getStar(3).trigger('mouseover');
$rootScope.$digest();
@@ -183,10 +189,11 @@ describe('rating directive', function() {
it('should fire onLeave', function() {
$rootScope.leaving = jasmine.createSpy('leaving');
element = $compile('<uib-rating ng-model="rate" on-leave="leaving()"></uib-rating>')($rootScope);
element = $compile('<span uib-rating ng-model="rate" on-leave="leaving()"></span>')($rootScope);
$rootScope.$digest();
innerElem = element.children().eq(0);
element.trigger('mouseleave');
innerElem.trigger('mouseleave');
$rootScope.$digest();
expect($rootScope.leaving).toHaveBeenCalled();
});
@@ -243,8 +250,9 @@ describe('rating directive', function() {
beforeEach(inject(function() {
$rootScope.classOn = 'icon-ok-sign';
$rootScope.classOff = 'icon-ok-circle';
element = $compile('<uib-rating ng-model="rate" state-on="classOn" state-off="classOff"></uib-rating>')($rootScope);
element = $compile('<span uib-rating ng-model="rate" state-on="classOn" state-off="classOff"></span>')($rootScope);
$rootScope.$digest();
innerElem = element.children().eq(0);
}));
it('changes the default icons', function() {
@@ -260,13 +268,14 @@ describe('rating directive', function() {
{stateOn: 'heart'},
{stateOff: 'off'}
];
element = $compile('<uib-rating ng-model="rate" rating-states="states"></uib-rating>')($rootScope);
element = $compile('<span uib-rating ng-model="rate" rating-states="states"></span>')($rootScope);
$rootScope.$digest();
innerElem = element.children().eq(0);
}));
it('should define number of icon elements', function() {
expect(getStars().length).toBe(4);
expect(element.attr('aria-valuemax')).toBe('4');
expect(innerElem.attr('aria-valuemax')).toBe('4');
});
it('handles each icon', function() {
@@ -291,8 +300,9 @@ describe('rating directive', function() {
uibRatingConfig.max = 10;
uibRatingConfig.stateOn = 'on';
uibRatingConfig.stateOff = 'off';
element = $compile('<uib-rating ng-model="rate"></uib-rating>')($rootScope);
element = $compile('<span uib-rating ng-model="rate"></span>')($rootScope);
$rootScope.$digest();
innerElem = element.children().eq(0);
}));
afterEach(inject(function(uibRatingConfig) {
// return it to the original state
@@ -320,8 +330,9 @@ describe('rating directive', function() {
$rootScope.rate = 5;
angular.extend(originalConfig, uibRatingConfig);
uibRatingConfig.max = 10;
element = $compile('<uib-rating ng-model="rate"></uib-rating>')($rootScope);
element = $compile('<span uib-rating ng-model="rate"></span>')($rootScope);
$rootScope.$digest();
innerElem = element.children().eq(0);
}));
afterEach(inject(function(uibRatingConfig) {
// return it to the original state
@@ -336,18 +347,20 @@ describe('rating directive', function() {
describe('shows custom titles ', function() {
it('should return the custom title for each star', function() {
$rootScope.titles = [44,45,46];
element = $compile('<uib-rating ng-model="rate" titles="titles"></uib-rating>')($rootScope);
element = $compile('<span uib-rating ng-model="rate" titles="titles"></span>')($rootScope);
$rootScope.$digest();
innerElem = element.children().eq(0);
expect(getTitles()).toEqual(['44', '45', '46', '4', '5']);
});
it('should return the default title if the custom title is empty', function() {
$rootScope.titles = [];
element = $compile('<uib-rating ng-model="rate" titles="titles"></uib-rating>')($rootScope);
element = $compile('<span uib-rating ng-model="rate" titles="titles"></span>')($rootScope);
$rootScope.$digest();
innerElem = element.children().eq(0);
expect(getTitles()).toEqual(['one', 'two', 'three', 'four', 'five']);
});
it('should return the default title if the custom title is not an array', function() {
element = $compile('<uib-rating ng-model="rate" titles="test"></uib-rating>')($rootScope);
element = $compile('<span uib-rating ng-model="rate" titles="test"></span>')($rootScope);
$rootScope.$digest();
expect(getTitles()).toEqual(['one', 'two', 'three', 'four', 'five']);
});
+1 -1
View File
@@ -43,7 +43,7 @@ angular.module('ui.bootstrap.stackedMap', [])
return stack.splice(idx, 1)[0];
},
removeTop: function() {
return stack.splice(stack.length - 1, 1)[0];
return stack.pop();
},
length: function() {
return stack.length;
+7
View File
@@ -0,0 +1,7 @@
require('./tabindex');
var MODULE_NAME = 'ui.bootstrap.module.tabindex';
angular.module(MODULE_NAME, ['ui.bootstrap.tabindex']);
module.exports = MODULE_NAME;
+12
View File
@@ -0,0 +1,12 @@
angular.module('ui.bootstrap.tabindex', [])
.directive('uibTabindexToggle', function() {
return {
restrict: 'A',
link: function(scope, elem, attrs) {
attrs.$observe('disabled', function(disabled) {
attrs.$set('tabindex', disabled ? -1 : null);
});
}
};
});
+23
View File
@@ -0,0 +1,23 @@
describe('tabindex toggle directive', function() {
var $rootScope, element;
beforeEach(module('ui.bootstrap.tabindex'));
beforeEach(inject(function($compile, _$rootScope_) {
$rootScope = _$rootScope_;
element = $compile('<a href uib-tabindex-toggle ng-disabled="disabled">foo</a>')($rootScope);
$rootScope.$digest();
}));
it('should toggle the tabindex on disabled toggle', function() {
expect(element.prop('tabindex')).toBe(0);
$rootScope.disabled = true;
$rootScope.$digest();
expect(element.prop('tabindex')).toBe(-1);
$rootScope.disabled = false;
$rootScope.$digest();
expect(element.prop('tabindex')).toBe(0);
});
});
+1 -1
View File
@@ -17,7 +17,7 @@
<uib-tabset active="active">
<uib-tab index="0" heading="Static title">Static content</uib-tab>
<uib-tab index="$index + 1" ng-repeat="tab in tabs" heading="{{tab.title}}" active="tab.active" disable="tab.disabled">
<uib-tab index="$index + 1" ng-repeat="tab in tabs" heading="{{tab.title}}" disable="tab.disabled">
{{tab.content}}
</uib-tab>
<uib-tab index="3" select="alertMe()">
+2 -2
View File
@@ -33,7 +33,7 @@ AngularJS version of the tabs directive.
* `deselect()`
<small class="badge">$</small> -
An optional expression called when tab is deactivated. Supports $event in template for expression. You may call `$event.preventDefault()` in this event handler to prevent a tab change from occurring.
An optional expression called when tab is deactivated. Supports `$event` and `$selectedIndex` in template for expression. You may call `$event.preventDefault()` in this event handler to prevent a tab change from occurring. The `$selectedIndex` can be used to determine which tab was attempted to be opened.
* `disable`
<small class="badge">$</small>
@@ -45,7 +45,7 @@ AngularJS version of the tabs directive.
Heading text.
* `index` -
Tab index. Must be unique.
Tab index. Must be unique number or string.
* `select()`
<small class="badge">$</small> -
+5 -7
View File
@@ -11,7 +11,8 @@ angular.module('ui.bootstrap.tabs', [])
var previousSelected = ctrl.tabs[previousIndex];
if (previousSelected) {
previousSelected.tab.onDeselect({
$event: evt
$event: evt,
$selectedIndex: index
});
if (evt && evt.isDefaultPrevented()) {
return;
@@ -27,7 +28,7 @@ angular.module('ui.bootstrap.tabs', [])
selected.tab.active = true;
ctrl.active = selected.index;
oldIndex = selected.index;
} else if (!selected && angular.isNumber(oldIndex)) {
} else if (!selected && angular.isDefined(oldIndex)) {
ctrl.active = null;
oldIndex = null;
}
@@ -51,7 +52,7 @@ angular.module('ui.bootstrap.tabs', [])
return 0;
});
if (tab.index === ctrl.active || !angular.isNumber(ctrl.active) && ctrl.tabs.length === 1) {
if (tab.index === ctrl.active || !angular.isDefined(ctrl.active) && ctrl.tabs.length === 1) {
var newActiveIndex = findTabIndex(tab.index);
ctrl.select(newActiveIndex);
}
@@ -76,7 +77,7 @@ angular.module('ui.bootstrap.tabs', [])
};
$scope.$watch('tabset.active', function(val) {
if (angular.isNumber(val) && val !== oldIndex) {
if (angular.isDefined(val) && val !== oldIndex) {
ctrl.select(findTabIndex(val));
}
});
@@ -114,9 +115,6 @@ angular.module('ui.bootstrap.tabs', [])
scope.$parent.$eval(attrs.vertical) : false;
scope.justified = angular.isDefined(attrs.justified) ?
scope.$parent.$eval(attrs.justified) : false;
if (angular.isUndefined(attrs.active)) {
scope.active = 0;
}
}
};
})
+41 -3
View File
@@ -46,14 +46,14 @@ describe('tabs', function() {
};
elm = $compile([
'<uib-tabset class="hello" data-pizza="pepperoni" active="active">',
' <uib-tab index="1" heading="First Tab {{first}}" classes="{{firstClass}}" select="selectFirst($event)" deselect="deselectFirst($event)">',
' <uib-tab index="1" heading="First Tab {{first}}" classes="{{firstClass}}" select="selectFirst($event)" deselect="deselectFirst($event, $selectedIndex)">',
' first content is {{first}}',
' </uib-tab>',
' <uib-tab index="2" classes="{{secondClass}}" select="selectSecond($event)" deselect="deselectSecond($event)">',
' <uib-tab index="2" classes="{{secondClass}}" select="selectSecond($event)" deselect="deselectSecond($event, $selectedIndex)">',
' <uib-tab-heading><b>Second</b> Tab {{second}}</uib-tab-heading>',
' second content is {{second}}',
' </uib-tab>',
' <uib-tab index="3" classes="{{thirdClass}}" deselect="deselectThird($event)">',
' <uib-tab index="3" classes="{{thirdClass}}" deselect="deselectThird($event, $selectedIndex)">',
' <uib-tab-heading><b>Second</b> Tab {{third}}</uib-tab-heading>',
' third content is {{third}}',
' </uib-tab>',
@@ -118,12 +118,15 @@ describe('tabs', function() {
titles().eq(1).find('> a').click();
expect(scope.deselectFirst).toHaveBeenCalled();
expect(scope.deselectFirst.calls.argsFor(0)[0].target).toBe(titles().eq(1).find('> a')[0]);
expect(scope.deselectFirst.calls.argsFor(0)[1]).toBe(1);
titles().eq(0).find('> a').click();
expect(scope.deselectSecond).toHaveBeenCalled();
expect(scope.deselectSecond.calls.argsFor(0)[0].target).toBe(titles().eq(0).find('> a')[0]);
expect(scope.deselectSecond.calls.argsFor(0)[1]).toBe(0);
titles().eq(1).find('> a').click();
expect(scope.deselectFirst.calls.count()).toBe(2);
expect(scope.deselectFirst.calls.argsFor(1)[0].target).toBe(titles().eq(1).find('> a')[0]);
expect(scope.deselectFirst.calls.argsFor(1)[1]).toBe(1);
});
it('should prevent tab deselection when $event.preventDefault() is called', function() {
@@ -223,6 +226,41 @@ describe('tabs', function() {
});
});
describe('index as strings', function() {
beforeEach(inject(function($compile, $rootScope) {
scope = $rootScope.$new();
scope.first = 'one';
scope.second = 'two';
scope.active = 'two';
elm = $compile([
'<uib-tabset active="active">',
' <uib-tab index="first" heading="First Tab">',
' first content',
' </uib-tab>',
' <uib-tab index="second" heading="Second Tab">',
' second content',
' </uib-tab>',
'</uib-tabset>'
].join('\n'))(scope);
scope.$apply();
return elm;
}));
it('should set second tab active', function() {
expect(titles().eq(0)).not.toHaveClass('active');
expect(titles().eq(1)).toHaveClass('active');
expect(elm.controller('uibTabset').active).toBe('two');
});
it('should change active on click', function() {
expect(titles().eq(0)).not.toHaveClass('active');
titles().eq(0).find('> a').click();
expect(titles().eq(0)).toHaveClass('active');
expect(titles().eq(1)).not.toHaveClass('active');
expect(elm.controller('uibTabset').active).toBe('one');
});
});
describe('tab callback order', function() {
var execOrder;
beforeEach(inject(function($compile, $rootScope) {
+1 -1
View File
@@ -1,6 +1,6 @@
<div ng-controller="TimepickerDemoCtrl">
<uib-timepicker ng-model="mytime" ng-change="changed()" hour-step="hstep" minute-step="mstep" show-meridian="ismeridian"></uib-timepicker>
<div uib-timepicker ng-model="mytime" ng-change="changed()" hour-step="hstep" minute-step="mstep" show-meridian="ismeridian"></div>
<pre class="alert alert-info">Time is: {{mytime | date:'shortTime' }}</pre>
+29 -29
View File
@@ -9,7 +9,7 @@ describe('timepicker directive', function() {
$rootScope.time = newTime(14, 40, 25);
$templateCache = _$templateCache_;
element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time"></div>')($rootScope);
$rootScope.$digest();
modelCtrl = element.controller('ngModel');
@@ -130,7 +130,7 @@ describe('timepicker directive', function() {
it('has `selected` current time when model is initially cleared', function() {
$rootScope.time = null;
element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time"></div>')($rootScope);
$rootScope.$digest();
expect($rootScope.time).toBe(null);
@@ -385,7 +385,7 @@ describe('timepicker directive', function() {
});
it('changes only the time part when minutes change', function() {
element = $compile('<uib-timepicker ng-model="time" minute-step="15"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time" minute-step="15"></div>')($rootScope);
$rootScope.time = newTime(0, 0, 0);
$rootScope.$digest();
@@ -648,7 +648,7 @@ describe('timepicker directive', function() {
$rootScope.mstep = 30;
$rootScope.sstep = 30;
$rootScope.time = newTime(14, 0 , 0);
element = $compile('<uib-timepicker ng-model="time" hour-step="hstep" minute-step="mstep" second-step="sstep"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time" hour-step="hstep" minute-step="mstep" second-step="sstep"></div>')($rootScope);
$rootScope.$digest();
});
@@ -840,7 +840,7 @@ describe('timepicker directive', function() {
beforeEach(function(){
$rootScope.displaysSeconds = false;
$rootScope.time = newTime(14,40,35);
element = $compile('<uib-timepicker ng-model="time" show-seconds="displaysSeconds"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time" show-seconds="displaysSeconds"></div>')($rootScope);
$rootScope.$digest();
});
@@ -914,7 +914,7 @@ describe('timepicker directive', function() {
beforeEach(function() {
$rootScope.meridian = false;
$rootScope.time = newTime(14, 10, 20);
element = $compile('<uib-timepicker ng-model="time" show-meridian="meridian"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time" show-meridian="meridian"></div>')($rootScope);
$rootScope.$digest();
});
@@ -946,7 +946,7 @@ describe('timepicker directive', function() {
it('handles correctly initially empty model on parent element', function() {
$rootScope.time = null;
element = $compile('<span ng-model="time"><uib-timepicker show-meridian="meridian"></uib-timepicker></span>')($rootScope);
element = $compile('<span ng-model="time"><div uib-timepicker show-meridian="meridian"></div></span>')($rootScope);
$rootScope.$digest();
expect($rootScope.time).toBe(null);
@@ -956,7 +956,7 @@ describe('timepicker directive', function() {
describe('`meridians` attribute', function() {
beforeEach(inject(function() {
$rootScope.meridiansArray = ['am', 'pm'];
element = $compile('<uib-timepicker ng-model="time" meridians="meridiansArray"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time" meridians="meridiansArray"></div>')($rootScope);
$rootScope.$digest();
}));
@@ -974,7 +974,7 @@ describe('timepicker directive', function() {
describe('`readonly-input` attribute', function() {
beforeEach(inject(function() {
$rootScope.meridiansArray = ['am', 'pm'];
element = $compile('<uib-timepicker ng-model="time" readonly-input="true"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time" readonly-input="true"></div>')($rootScope);
$rootScope.$digest();
}));
@@ -993,7 +993,7 @@ describe('timepicker directive', function() {
}
it('should pad the hours by default', function() {
element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time"></div>')($rootScope);
$rootScope.$digest();
var inputs = element.find('input');
@@ -1005,7 +1005,7 @@ describe('timepicker directive', function() {
});
it('should not pad the hours', function() {
element = $compile('<uib-timepicker ng-model="time" pad-hours="false"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time" pad-hours="false"></div>')($rootScope);
$rootScope.$digest();
var inputs = element.find('input');
@@ -1025,7 +1025,7 @@ describe('timepicker directive', function() {
uibTimepickerConfig.minuteStep = 10;
uibTimepickerConfig.secondStep = 10;
uibTimepickerConfig.showMeridian = false;
element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time"></div>')($rootScope);
$rootScope.$digest();
}));
@@ -1086,7 +1086,7 @@ describe('timepicker directive', function() {
angular.extend(originalConfig, uibTimepickerConfig);
uibTimepickerConfig.meridians = ['π.μ.', 'μ.μ.'];
uibTimepickerConfig.showMeridian = true;
element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time"></div>')($rootScope);
$rootScope.$digest();
}));
afterEach(inject(function(uibTimepickerConfig) {
@@ -1116,7 +1116,7 @@ describe('timepicker directive', function() {
$templateCache.put(newTemplateUrl, '<div>baz</div>');
uibTimepickerConfig.templateUrl = newTemplateUrl;
element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time"></div>')($rootScope);
$rootScope.$digest();
}));
afterEach(inject(function(uibTimepickerConfig) {
@@ -1126,7 +1126,7 @@ describe('timepicker directive', function() {
it('should use a custom template', function() {
expect(element[0].tagName.toLowerCase()).toBe('div');
expect(element.html()).toBe('baz');
expect(element.html()).toBe('<div>baz</div>');
});
});
@@ -1328,7 +1328,7 @@ describe('timepicker directive', function() {
it('handles 12/24H mode change', function() {
$rootScope.meridian = true;
element = $compile('<uib-timepicker ng-model="time" show-meridian="meridian"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time" show-meridian="meridian"></div>')($rootScope);
$rootScope.$digest();
var el = getHoursInputEl();
@@ -1346,14 +1346,14 @@ describe('timepicker directive', function() {
});
it('should have a default tabindex of 0', function() {
element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time"></div>')($rootScope);
$rootScope.$digest();
expect(element.isolateScope().tabindex).toBe(0);
});
it('should have the correct tabindex', function() {
element = $compile('<uib-timepicker ng-model="time" tabindex="5"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time" tabindex="5"></div>')($rootScope);
$rootScope.$digest();
expect(element.attr('tabindex')).toBe(undefined);
@@ -1363,7 +1363,7 @@ describe('timepicker directive', function() {
describe('when model is not a Date', function() {
beforeEach(inject(function() {
element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time"></div>')($rootScope);
}));
it('should not be invalid when the model is null', function() {
@@ -1415,7 +1415,7 @@ describe('timepicker directive', function() {
describe('use with `ng-required` directive', function() {
beforeEach(inject(function() {
$rootScope.time = null;
element = $compile('<uib-timepicker ng-model="time" ng-required="true"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time" ng-required="true"></div>')($rootScope);
$rootScope.$digest();
}));
@@ -1434,7 +1434,7 @@ describe('timepicker directive', function() {
beforeEach(inject(function() {
$rootScope.changeHandler = jasmine.createSpy('changeHandler');
$rootScope.time = new Date();
element = $compile('<uib-timepicker ng-model="time" ng-change="changeHandler()"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time" ng-change="changeHandler()"></div>')($rootScope);
$rootScope.$digest();
}));
@@ -1465,7 +1465,7 @@ describe('timepicker directive', function() {
describe('when used with min', function() {
var changeInputValueTo;
beforeEach(inject(function($sniffer) {
element = $compile('<uib-timepicker ng-model="time" min="min"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time" min="min"></div>')($rootScope);
$rootScope.$digest();
changeInputValueTo = function(inputEl, value) {
inputEl.val(value);
@@ -1792,7 +1792,7 @@ describe('timepicker directive', function() {
describe('when used with max', function() {
var changeInputValueTo;
beforeEach(inject(function($sniffer) {
element = $compile('<uib-timepicker ng-model="time" max="max"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time" max="max"></div>')($rootScope);
$rootScope.$digest();
changeInputValueTo = function(inputEl, value) {
inputEl.val(value);
@@ -2121,16 +2121,16 @@ describe('timepicker directive', function() {
it('should allow custom templates', function() {
$templateCache.put('foo/bar.html', '<div>baz</div>');
element = $compile('<uib-timepicker ng-model="time" template-url="foo/bar.html"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time" template-url="foo/bar.html"></div>')($rootScope);
$rootScope.$digest();
expect(element[0].tagName.toLowerCase()).toBe('div');
expect(element.html()).toBe('baz');
expect(element.html()).toBe('<div>baz</div>');
});
it('should expose the controller on the view', function() {
$templateCache.put('uib/template/timepicker/timepicker.html', '<div><div>{{timepicker.text}}</div></div>');
element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($rootScope);
element = $compile('<div uib-timepicker ng-model="time"></div>')($rootScope);
$rootScope.$digest();
var ctrl = element.controller('uibTimepicker');
@@ -2139,14 +2139,14 @@ describe('timepicker directive', function() {
ctrl.text = 'foo';
$rootScope.$digest();
expect(element.html()).toBe('<div class="ng-binding">foo</div>');
expect(element.html()).toBe('<div><div class="ng-binding">foo</div></div>');
});
});
describe('ngDisabled', function() {
it('prevents modifying date via controls when true', function() {
$rootScope.disabled = false;
element = $compile('<uib-timepicker ng-model="time" ng-disabled="disabled"></uib-timepicker')($rootScope);
element = $compile('<div uib-timepicker ng-model="time" ng-disabled="disabled"></div>')($rootScope);
$rootScope.$digest();
var inputs = element.find('input');
@@ -2250,7 +2250,7 @@ describe('timepicker directive', function() {
var $scope;
beforeEach(inject(function() {
$scope = $rootScope.$new();
element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($scope);
element = $compile('<div uib-timepicker ng-model="time"></div>')($scope);
$rootScope.$digest();
}));
+1 -1
View File
@@ -547,9 +547,9 @@ angular.module('ui.bootstrap.timepicker', [])
.directive('uibTimepicker', ['uibTimepickerConfig', function(uibTimepickerConfig) {
return {
require: ['uibTimepicker', '?^ngModel'],
restrict: 'A',
controller: 'UibTimepickerController',
controllerAs: 'timepicker',
replace: true,
scope: {},
templateUrl: function(element, attrs) {
return attrs.templateUrl || uibTimepickerConfig.templateUrl;
+2 -2
View File
@@ -51,7 +51,7 @@
<div class="form-group">
<label>Or use custom triggers, like focus: </label>
<input type="text" value="Click me!" uib-tooltip="See? Now click away..." tooltip-trigger="focus" tooltip-placement="right" class="form-control" />
<input type="text" value="Click me!" uib-tooltip="See? Now click away..." tooltip-trigger="'focus'" tooltip-placement="right" class="form-control" />
</div>
<div class="form-group" ng-class="{'has-error' : !inputModel}">
@@ -60,7 +60,7 @@
placeholder="Hover over this for a tooltip until this is filled"
uib-tooltip="Enter something in this input field to disable this tooltip"
tooltip-placement="top"
tooltip-trigger="mouseenter"
tooltip-trigger="'mouseenter'"
tooltip-enable="!inputModel" />
</div>
<div class="form-group">
+3 -2
View File
@@ -74,8 +74,9 @@ All these settings are available for the three types of tooltips.
Popup delay in milliseconds until it opens.
* `tooltip-trigger`
_(Default: `mouseenter`)_ -
What should trigger a show of the tooltip? Supports a space separated list of event names (see below).
<small class="badge">$</small>
_(Default: `'mouseenter'`)_ -
What should trigger a show of the tooltip? Supports a space separated list of event names, or objects (see below).
**Note:** To configure the tooltips, you need to do it on `$uibTooltipProvider` (also see below).
@@ -75,6 +75,7 @@ describe('tooltip template', function() {
it('should hide tooltip when template becomes empty', inject(function($timeout) {
trigger(elm, 'mouseenter');
$timeout.flush(0);
expect(tooltipScope.isOpen).toBe(true);
scope.templateUrl = '';
+27 -12
View File
@@ -179,7 +179,6 @@ describe('tooltip', function() {
expect(elm.attr('alt')).toBe(scope.alt);
ttScope = angular.element(elmBody.children()[1]).isolateScope();
expect(ttScope.placement).toBe('top');
expect(ttScope.content).toBe(scope.tooltipMsg);
trigger(elm, 'mouseleave');
@@ -188,7 +187,6 @@ describe('tooltip', function() {
trigger(elm, 'mouseenter');
ttScope = angular.element(elmBody.children()[1]).isolateScope();
expect(ttScope.placement).toBe('top');
expect(ttScope.content).toBe(scope.tooltipMsg);
}));
@@ -504,7 +502,7 @@ describe('tooltip', function() {
it('should use it to show but set the hide trigger based on the map for mapped triggers', inject(function($compile) {
elmBody = angular.element(
'<div><input uib-tooltip="Hello!" tooltip-trigger="focus" /></div>'
'<div><input uib-tooltip="Hello!" tooltip-trigger="\'focus\'" /></div>'
);
$compile(elmBody)(scope);
scope.$apply();
@@ -521,7 +519,7 @@ describe('tooltip', function() {
it('should use it as both the show and hide triggers for unmapped triggers', inject(function($compile) {
elmBody = angular.element(
'<div><input uib-tooltip="Hello!" tooltip-trigger="fakeTriggerAttr" /></div>'
'<div><input uib-tooltip="Hello!" tooltip-trigger="\'fakeTriggerAttr\'" /></div>'
);
$compile(elmBody)(scope);
scope.$apply();
@@ -540,8 +538,8 @@ describe('tooltip', function() {
scope.test = true;
elmBody = angular.element(
'<div>' +
'<input uib-tooltip="Hello!" tooltip-trigger="{{ (test && \'mouseenter\' || \'click\') }}" />' +
'<input uib-tooltip="Hello!" tooltip-trigger="{{ (test && \'mouseenter\' || \'click\') }}" />' +
'<input uib-tooltip="Hello!" tooltip-trigger="test && \'mouseenter\' || \'click\'" />' +
'<input uib-tooltip="Hello!" tooltip-trigger="test && \'mouseenter\' || \'click\'" />' +
'</div>'
);
@@ -566,7 +564,7 @@ describe('tooltip', function() {
it('should accept multiple triggers based on the map for mapped triggers', inject(function($compile) {
elmBody = angular.element(
'<div><input uib-tooltip="Hello!" tooltip-trigger="focus fakeTriggerAttr" /></div>'
'<div><input uib-tooltip="Hello!" tooltip-trigger="\'focus fakeTriggerAttr\'" /></div>'
);
$compile(elmBody)(scope);
scope.$apply();
@@ -587,7 +585,7 @@ describe('tooltip', function() {
it('should not show when trigger is set to "none"', inject(function($compile) {
elmBody = angular.element(
'<div><input uib-tooltip="Hello!" tooltip-trigger="none" /></div>'
'<div><input uib-tooltip="Hello!" tooltip-trigger="\'none\'" /></div>'
);
$compile(elmBody)(scope);
scope.$apply();
@@ -601,7 +599,7 @@ describe('tooltip', function() {
it('should toggle on click and hide when anything else is clicked when trigger is set to "outsideClick"', inject(function($compile, $document) {
elm = $compile(angular.element(
'<span uib-tooltip="tooltip text" tooltip-trigger="outsideClick">Selector Text</span>'
'<span uib-tooltip="tooltip text" tooltip-trigger="\'outsideClick\'">Selector Text</span>'
))(scope);
scope.$apply();
elmScope = elm.scope();
@@ -623,6 +621,23 @@ describe('tooltip', function() {
tooltipScope.$digest();
expect(tooltipScope.isOpen).toBeFalsy();
}));
it('should support objects', inject(function($compile) {
elmBody = angular.element(
'<div><input uib-tooltip="Hello!" tooltip-trigger="{show: \'hide\'}" /></div>'
);
$compile(elmBody)(scope);
scope.$apply();
elm = elmBody.find('input');
elmScope = elm.scope();
tooltipScope = elmScope.$$childTail;
expect(tooltipScope.isOpen).toBeFalsy();
trigger(elm, 'show');
expect(tooltipScope.isOpen).toBeTruthy();
trigger(elm, 'hide');
expect(tooltipScope.isOpen).toBeFalsy();
}));
});
describe('with an append-to-body attribute', function() {
@@ -673,7 +688,7 @@ describe('tooltip', function() {
}
beforeEach(inject(function($compile, $rootScope) {
elmBody = angular.element('<div><input uib-tooltip="Hello!" tooltip-trigger="fooTrigger" /></div>');
elmBody = angular.element('<div><input uib-tooltip="Hello!" tooltip-trigger="\'fooTrigger\'" /></div>');
$compile(elmBody)($rootScope);
$rootScope.$apply();
@@ -754,7 +769,7 @@ describe('tooltipWithDifferentSymbols', function() {
it('should show the correct tooltip text', inject(function($compile, $rootScope) {
elmBody = angular.element(
'<div><input type="text" uib-tooltip="My tooltip" tooltip-trigger="focus" tooltip-placement="right" /></div>'
'<div><input type="text" uib-tooltip="My tooltip" tooltip-trigger="\'focus\'" tooltip-placement="right" /></div>'
);
$compile(elmBody)($rootScope);
$rootScope.$apply();
@@ -1030,7 +1045,7 @@ describe('$uibTooltipProvider', function() {
it('should override the show and hide triggers if there is an attribute', inject(function($rootScope, $compile) {
elmBody = angular.element(
'<div><input uib-tooltip="tooltip text" tooltip-trigger="mouseenter"/></div>'
'<div><input uib-tooltip="tooltip text" tooltip-trigger="\'mouseenter\'"/></div>'
);
scope = $rootScope;
+16 -12
View File
@@ -1,5 +1,5 @@
describe('tooltip directive', function() {
var $rootScope, $compile, $document, $timeout;
var $rootScope, $compile, $document, $timeout, body, fragment;
beforeEach(module('ui.bootstrap.tooltip'));
beforeEach(module('uib/template/tooltip/tooltip-popup.html'));
@@ -10,6 +10,8 @@ describe('tooltip directive', function() {
$compile = _$compile_;
$document = _$document_;
$timeout = _$timeout_;
body = $document.find('body');
}));
beforeEach(function() {
@@ -39,12 +41,13 @@ describe('tooltip directive', function() {
afterEach(function() {
$document.off('keypress');
fragment.remove();
});
function compileTooltip(ttipMarkup) {
var fragment = $compile('<div>' + ttipMarkup + '</div>')($rootScope);
fragment = $compile('<div>' + ttipMarkup + '</div>')($rootScope);
$rootScope.$digest();
return fragment;
body.append(fragment);
}
function closeTooltip(hostEl, triggerEvt, shouldNotFlush) {
@@ -62,7 +65,7 @@ describe('tooltip directive', function() {
describe('basic scenarios with default options', function() {
it('shows default tooltip on mouse enter and closes on mouse leave', function() {
var fragment = compileTooltip('<span uib-tooltip="tooltip text">Trigger here</span>');
compileTooltip('<span uib-tooltip="tooltip text">Trigger here</span>');
trigger(fragment.find('span'), 'mouseenter');
expect(fragment).toHaveOpenTooltips();
@@ -72,16 +75,17 @@ describe('tooltip directive', function() {
});
it('should not show a tooltip when its content is empty', function() {
var fragment = compileTooltip('<span uib-tooltip=""></span>');
compileTooltip('<span uib-tooltip=""></span>');
trigger(fragment.find('span'), 'mouseenter');
expect(fragment).not.toHaveOpenTooltips();
});
it('should not show a tooltip when its content becomes empty', function() {
$rootScope.content = 'some text';
var fragment = compileTooltip('<span uib-tooltip="{{ content }}"></span>');
compileTooltip('<span uib-tooltip="{{ content }}"></span>');
trigger(fragment.find('span'), 'mouseenter');
$timeout.flush(0);
expect(fragment).toHaveOpenTooltips();
$rootScope.content = '';
@@ -92,7 +96,7 @@ describe('tooltip directive', function() {
it('should update tooltip when its content becomes empty', function() {
$rootScope.content = 'some text';
var fragment = compileTooltip('<span uib-tooltip="{{ content }}"></span>');
compileTooltip('<span uib-tooltip="{{ content }}"></span>');
$rootScope.content = '';
$rootScope.$digest();
@@ -119,7 +123,7 @@ describe('tooltip directive', function() {
describe(key, function() {
describe('placement', function() {
it('can specify an alternative, valid placement', function() {
var fragment = compileTooltip('<span ' + html + ' tooltip-placement="left">Trigger here</span>');
compileTooltip('<span ' + html + ' tooltip-placement="left">Trigger here</span>');
trigger(fragment.find('span'), 'mouseenter');
var ttipElement = fragment.find('div.tooltip');
@@ -133,7 +137,7 @@ describe('tooltip directive', function() {
describe('class', function() {
it('can specify a custom class', function() {
var fragment = compileTooltip('<span ' + html + ' tooltip-class="custom">Trigger here</span>');
compileTooltip('<span ' + html + ' tooltip-class="custom">Trigger here</span>');
trigger(fragment.find('span'), 'mouseenter');
var ttipElement = fragment.find('div.tooltip');
@@ -149,7 +153,7 @@ describe('tooltip directive', function() {
});
it('should show even after close trigger is called multiple times - issue #1847', function() {
var fragment = compileTooltip('<span uib-tooltip="tooltip text">Trigger here</span>');
compileTooltip('<span uib-tooltip="tooltip text">Trigger here</span>');
trigger(fragment.find('span'), 'mouseenter');
expect(fragment).toHaveOpenTooltips();
@@ -169,7 +173,7 @@ describe('tooltip directive', function() {
});
it('should hide even after show trigger is called multiple times', function() {
var fragment = compileTooltip('<span uib-tooltip="tooltip text" tooltip-popup-delay="1000">Trigger here</span>');
compileTooltip('<span uib-tooltip="tooltip text" tooltip-popup-delay="1000">Trigger here</span>');
trigger(fragment.find('span'), 'mouseenter');
trigger(fragment.find('span'), 'mouseenter');
@@ -179,7 +183,7 @@ describe('tooltip directive', function() {
});
it('should not show tooltips element is disabled (button) - issue #3167', function() {
var fragment = compileTooltip('<button uib-tooltip="cancel!" ng-disabled="disabled" ng-click="disabled = true">Cancel</button>');
compileTooltip('<button uib-tooltip="cancel!" ng-disabled="disabled" ng-click="disabled = true">Cancel</button>');
trigger(fragment.find('button'), 'mouseenter');
expect(fragment).toHaveOpenTooltips();
+38 -17
View File
@@ -126,12 +126,11 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
(options.useContentExp ?
'content-exp="contentExp()" ' :
'content="' + startSym + 'content' + endSym + '" ') +
'placement="' + startSym + 'placement' + endSym + '" ' +
'popup-class="' + startSym + 'popupClass' + endSym + '" ' +
'animation="animation" ' +
'is-open="isOpen" ' +
'origin-scope="origScope" ' +
'class="uib-position-measure"' +
'class="uib-position-measure ' + prefix + '" ' +
'tooltip-animation-class="fade"' +
'uib-tooltip-classes ' +
'ng-class="{ in: isOpen }" ' +
'>' +
'</div>';
@@ -163,11 +162,14 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
if (!positionTimeout) {
positionTimeout = $timeout(function() {
var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody);
var initialHeight = angular.isDefined(tooltip.offsetHeight) ? tooltip.offsetHeight : tooltip.prop('offsetHeight');
var elementPos = appendToBody ? $position.offset(element) : $position.position(element);
tooltip.css({ top: ttPosition.top + 'px', left: ttPosition.left + 'px' });
var placementClasses = ttPosition.placement.split('-');
if (!tooltip.hasClass(ttPosition.placement.split('-')[0])) {
if (!tooltip.hasClass(placementClasses[0])) {
tooltip.removeClass(lastPlacement.split('-')[0]);
tooltip.addClass(ttPosition.placement.split('-')[0]);
tooltip.addClass(placementClasses[0]);
}
if (!tooltip.hasClass(options.placementClassPrefix + ttPosition.placement)) {
@@ -175,6 +177,14 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
tooltip.addClass(options.placementClassPrefix + ttPosition.placement);
}
$timeout(function() {
var currentHeight = angular.isDefined(tooltip.offsetHeight) ? tooltip.offsetHeight : tooltip.prop('offsetHeight');
var adjustment = $position.adjustTop(placementClasses, elementPos, initialHeight, currentHeight);
if (adjustment) {
tooltip.css(adjustment);
}
}, 0, false);
// first time through tt element will have the
// uib-position-measure class or if the placement
// has changed we need to position the arrow.
@@ -496,10 +506,22 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
};
function prepTriggers() {
var val = attrs[prefix + 'Trigger'];
var showTriggers = [], hideTriggers = [];
var val = scope.$eval(attrs[prefix + 'Trigger']);
unregisterTriggers();
triggers = getTriggers(val);
if (angular.isObject(val)) {
Object.keys(val).forEach(function(key) {
showTriggers.push(key);
hideTriggers.push(val[key]);
});
triggers = {
show: showTriggers,
hide: hideTriggers
};
} else {
triggers = getTriggers(val);
}
if (triggers.show !== 'none') {
triggers.show.forEach(function(trigger, idx) {
@@ -644,7 +666,7 @@ function ($animate, $sce, $compile, $templateRequest) {
element.addClass(scope.popupClass);
}
if (scope.animation()) {
if (scope.animation) {
element.addClass(attrs.tooltipAnimationClass);
}
}
@@ -653,8 +675,8 @@ function ($animate, $sce, $compile, $templateRequest) {
.directive('uibTooltipPopup', function() {
return {
replace: true,
scope: { content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
restrict: 'A',
scope: { content: '@' },
templateUrl: 'uib/template/tooltip/tooltip-popup.html'
};
})
@@ -665,9 +687,8 @@ function ($animate, $sce, $compile, $templateRequest) {
.directive('uibTooltipTemplatePopup', function() {
return {
replace: true,
scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
originScope: '&' },
restrict: 'A',
scope: { contentExp: '&', originScope: '&' },
templateUrl: 'uib/template/tooltip/tooltip-template-popup.html'
};
})
@@ -680,8 +701,8 @@ function ($animate, $sce, $compile, $templateRequest) {
.directive('uibTooltipHtmlPopup', function() {
return {
replace: true,
scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
restrict: 'A',
scope: { contentExp: '&' },
templateUrl: 'uib/template/tooltip/tooltip-html-popup.html'
};
})
+4 -4
View File
@@ -42,10 +42,10 @@
<p class="message">select location from drop down.</p>
<ul class="dropdown-menu" role="listbox">
<li ng-repeat="match in matches track by $index" ng-class="{active: isActive($index) }"
ng-mouseenter="selectActive($index)" ng-click="selectMatch($index)" role="option" id="{{::match.id}}">
<div uib-typeahead-match index="$index" match="match" query="query" template-url="templateUrl"></div>
</li>
<li class="uib-typeahead-match" ng-repeat="match in matches track by $index" ng-class="{active: isActive($index) }"
ng-mouseenter="selectActive($index)" ng-click="selectMatch($index)" role="option" id="{{::match.id}}">
<div uib-typeahead-match index="$index" match="match" query="query" template-url="templateUrl"></div>
</li>
</ul>
</div>
</script>
+9
View File
@@ -77,6 +77,11 @@ This directive works with promises, meaning you can retrieve matches using the `
_(Default: `angular.noop`)_ -
Binding to a variable that indicates if no matching results were found.
* `typeahead-should-select($event)`
<small class="badge">$</small>
_(Default: `null`)_ -
A callback executed when a `keyup` event that might trigger a selection occurs. Selection will only occur if this function returns true.
* `typeahead-on-select($item, $model, $label, $event)`
<small class="badge">$</small>
_(Default: `null`)_ -
@@ -115,3 +120,7 @@ This directive works with promises, meaning you can retrieve matches using the `
<small class="badge">$</small>
<i class="glyphicon glyphicon-eye-open"></i> -
Comprehension Angular expression (see [select directive](http://docs.angularjs.org/api/ng.directive:select)).
**Notes**
If a custom template for the popup is used, the wrapper selector used for the match items is the `uib-typeahead-match` class.
+84 -2
View File
@@ -222,8 +222,6 @@ describe('typeahead tests', function() {
expect(element).toBeClosed();
});
it('should support custom model selecting function', function() {
$scope.updaterFn = function(selectedItem) {
return 'prefix' + selectedItem;
@@ -315,6 +313,39 @@ describe('typeahead tests', function() {
expect($scope.form.input.$error.parse).toBeFalsy();
});
// fix for #6032
it('should clear errors and refresh scope after blur for typeahead-editable="false"', function () {
var element = prepareInputEl(
'<div><form name="form" ng-class="{invalid : form.input.$invalid}">' +
'<input name="input" ng-model="result" uib-typeahead="item for item in source | filter:$viewValue" typeahead-editable="false">' +
'</form></div>');
var inputEl = findInput(element);
// first try
changeInputValueTo(element, 'not in matches');
expect($scope.result).toEqual(undefined);
expect(inputEl.val()).toEqual('not in matches');
expect(element.find('form')).toHaveClass('invalid');
inputEl.blur();
expect(inputEl.val()).toEqual(''); // <-- input is reset
expect($scope.form.input.$error.editable).toBeFalsy();
expect($scope.form.input.$error.parse).toBeFalsy();
expect(element.find('form')).not.toHaveClass('invalid'); // <-- form has no error (it always works for some reason)
// second try
changeInputValueTo(element, 'not in matches');
expect($scope.result).toEqual(undefined);
expect(inputEl.val()).toEqual('not in matches');
expect(element.find('form')).toHaveClass('invalid');
inputEl.blur();
expect(inputEl.val()).toEqual(''); // <-- input is reset
expect($scope.form.input.$error.editable).toBeFalsy();
expect($scope.form.input.$error.parse).toBeFalsy();
expect(element.find('form')).not.toHaveClass('invalid'); // <-- form has no error (it didn't work prior to #6032 fix)
});
it('should go through other validators after blur for typeahead-editable="false"', function () {
var element = prepareInputEl(
'<div><form name="form">' +
@@ -470,6 +501,57 @@ describe('typeahead tests', function() {
};
expect(prepareInvalidDir).toThrow();
});
it('should remove the id attribute from the original DOM element', function() {
var element = prepareInputEl('<div><input id="typeahead-element" ng-model="result" uib-typeahead="item for item in source | filter:$viewValue" typeahead-show-hint="true"></div>');
var inputEl = findInput(element);
expect(inputEl.size()).toBe(2);
expect(inputEl.eq(0).attr('id')).toBe(undefined);
expect(inputEl.eq(1).attr('id')).toBe('typeahead-element');
});
});
describe('shouldSelect', function() {
it('should select a match when function returns true', function() {
$scope.shouldSelectFn = function() {
return true;
};
var element = prepareInputEl('<div><input ng-model="result" typeahead-should-select="shouldSelectFn($event)" uib-typeahead="item for item in source | filter:$viewValue"></div>');
var inputEl = findInput(element);
changeInputValueTo(element, 'b');
triggerKeyDown(element, 13);
expect($scope.result).toEqual('bar');
expect(inputEl.val()).toEqual('bar');
expect(element).toBeClosed();
});
it('should not select a match when function returns false', function() {
$scope.shouldSelectFn = function() {
return false;
};
var element = prepareInputEl('<div><input ng-model="result" typeahead-should-select="shouldSelectFn($event)" uib-typeahead="item for item in source | filter:$viewValue"></div>');
var inputEl = findInput(element);
changeInputValueTo(element, 'b');
triggerKeyDown(element, 13);
// no change
expect($scope.result).toEqual('b');
expect(inputEl.val()).toEqual('b');
});
it('should pass key event into select trigger function', function() {
$scope.shouldSelectFn = jasmine.createSpy('shouldSelectFn');//.and.returnValue(true);
var element = prepareInputEl('<div><input ng-model="result" typeahead-should-select="shouldSelectFn($event)" uib-typeahead="item for item in source | filter:$viewValue"></div>');
var inputEl = findInput(element);
changeInputValueTo(element, 'b');
triggerKeyDown(element, 13);
expect($scope.shouldSelectFn.calls.count()).toEqual(1);
expect($scope.shouldSelectFn.calls.argsFor(0)[0].which).toEqual(13);
});
});
describe('selecting a match', function() {
+36 -22
View File
@@ -42,7 +42,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
originalScope.$watch(attrs.typeaheadMinLength, function (newVal) {
minLength = !newVal && newVal !== 0 ? 1 : newVal;
});
//minimal wait time after last character typed before typeahead kicks-in
var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
@@ -55,6 +55,12 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
//binding to a variable that indicates if matches are being retrieved asynchronously
var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
//a function to determine if an event should cause selection
var isSelectEvent = attrs.typeaheadShouldSelect ? $parse(attrs.typeaheadShouldSelect) : function(scope, vals) {
var evt = vals.$event;
return evt.which === 13 || evt.which === 9;
};
//a callback executed when a match is selected
var onSelectCallback = $parse(attrs.typeaheadOnSelect);
@@ -146,6 +152,10 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
'vertical-align': 'top',
'background-color': 'transparent'
});
if (hintInputElem.attr('id')) {
hintInputElem.removeAttr('id'); // remove duplicate id if present.
}
inputsContainer.append(hintInputElem);
hintInputElem.after(element);
}
@@ -370,13 +380,15 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
return;
}
var shouldSelect = isSelectEvent(originalScope, {$event: evt});
/**
* if there's nothing selected (i.e. focusFirst) and enter or tab is hit
* or
* shift + tab is pressed to bring focus to the previous element
* then clear the results
*/
if (scope.activeIdx === -1 && (evt.which === 9 || evt.which === 13) || evt.which === 9 && !!evt.shiftKey) {
if (scope.activeIdx === -1 && shouldSelect || evt.which === 9 && !!evt.shiftKey) {
resetMatches();
scope.$digest();
return;
@@ -385,36 +397,36 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
evt.preventDefault();
var target;
switch (evt.which) {
case 9:
case 13:
scope.$apply(function () {
if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) {
$$debounce(function() {
scope.select(scope.activeIdx, evt);
}, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']);
} else {
scope.select(scope.activeIdx, evt);
}
});
break;
case 27:
case 27: // escape
evt.stopPropagation();
resetMatches();
originalScope.$digest();
break;
case 38:
case 38: // up arrow
scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1;
scope.$digest();
target = popUpEl.find('li')[scope.activeIdx];
target = popUpEl[0].querySelectorAll('.uib-typeahead-match')[scope.activeIdx];
target.parentNode.scrollTop = target.offsetTop;
break;
case 40:
case 40: // down arrow
scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
scope.$digest();
target = popUpEl.find('li')[scope.activeIdx];
target = popUpEl[0].querySelectorAll('.uib-typeahead-match')[scope.activeIdx];
target.parentNode.scrollTop = target.offsetTop;
break;
default:
if (shouldSelect) {
scope.$apply(function() {
if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) {
$$debounce(function() {
scope.select(scope.activeIdx, evt);
}, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']);
} else {
scope.select(scope.activeIdx, evt);
}
});
}
}
});
@@ -442,9 +454,11 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
}
if (!isEditable && modelCtrl.$error.editable) {
modelCtrl.$setViewValue();
// Reset validity as we are clearing
modelCtrl.$setValidity('editable', true);
modelCtrl.$setValidity('parse', true);
scope.$apply(function() {
// Reset validity as we are clearing
modelCtrl.$setValidity('editable', true);
modelCtrl.$setValidity('parse', true);
});
element.val('');
}
hasFocus = false;
+7 -9
View File
@@ -1,10 +1,8 @@
<div class="panel" ng-class="panelClass || 'panel-default'">
<div role="tab" id="{{::headingId}}" aria-selected="{{isOpen}}" class="panel-heading" ng-keypress="toggleOpen($event)">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" href aria-expanded="{{isOpen}}" aria-controls="{{::panelId}}" tabindex="0" class="accordion-toggle" ng-click="toggleOpen()" uib-accordion-transclude="heading"><span uib-accordion-header ng-class="{'text-muted': isDisabled}">{{heading}}</span></a>
</h4>
</div>
<div id="{{::panelId}}" aria-labelledby="{{::headingId}}" aria-hidden="{{!isOpen}}" role="tabpanel" class="panel-collapse collapse" uib-collapse="!isOpen">
<div class="panel-body" ng-transclude></div>
</div>
<div role="tab" id="{{::headingId}}" aria-selected="{{isOpen}}" class="panel-heading" ng-keypress="toggleOpen($event)">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" href aria-expanded="{{isOpen}}" aria-controls="{{::panelId}}" tabindex="0" class="accordion-toggle" ng-click="toggleOpen()" uib-accordion-transclude="heading" ng-disabled="isDisabled" uib-tabindex-toggle><span uib-accordion-header ng-class="{'text-muted': isDisabled}">{{heading}}</span></a>
</h4>
</div>
<div id="{{::panelId}}" aria-labelledby="{{::headingId}}" aria-hidden="{{!isOpen}}" role="tabpanel" class="panel-collapse collapse" uib-collapse="!isOpen">
<div class="panel-body" ng-transclude></div>
</div>
+5 -7
View File
@@ -1,7 +1,5 @@
<div class="alert" ng-class="['alert-' + (type || 'warning'), closeable ? 'alert-dismissible' : null]" role="alert">
<button ng-show="closeable" type="button" class="close" ng-click="close({$event: $event})">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
<div ng-transclude></div>
</div>
<button ng-show="closeable" type="button" class="close" ng-click="close({$event: $event})">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
<div ng-transclude></div>
+14 -16
View File
@@ -1,16 +1,14 @@
<div ng-mouseenter="pause()" ng-mouseleave="play()" class="carousel" ng-swipe-right="prev()" ng-swipe-left="next()">
<div class="carousel-inner" ng-transclude></div>
<a role="button" href class="left carousel-control" ng-click="prev()" ng-class="{ disabled: isPrevDisabled() }" ng-show="slides.length > 1">
<span aria-hidden="true" class="glyphicon glyphicon-chevron-left"></span>
<span class="sr-only">previous</span>
</a>
<a role="button" href class="right carousel-control" ng-click="next()" ng-class="{ disabled: isNextDisabled() }" ng-show="slides.length > 1">
<span aria-hidden="true" class="glyphicon glyphicon-chevron-right"></span>
<span class="sr-only">next</span>
</a>
<ol class="carousel-indicators" ng-show="slides.length > 1">
<li ng-repeat="slide in slides | orderBy:indexOfSlide track by $index" ng-class="{ active: isActive(slide) }" ng-click="select(slide)">
<span class="sr-only">slide {{ $index + 1 }} of {{ slides.length }}<span ng-if="isActive(slide)">, currently active</span></span>
</li>
</ol>
</div>
<div class="carousel-inner" ng-transclude></div>
<a role="button" href class="left carousel-control" ng-click="prev()" ng-class="{ disabled: isPrevDisabled() }" ng-show="slides.length > 1">
<span aria-hidden="true" class="glyphicon glyphicon-chevron-left"></span>
<span class="sr-only">previous</span>
</a>
<a role="button" href class="right carousel-control" ng-click="next()" ng-class="{ disabled: isNextDisabled() }" ng-show="slides.length > 1">
<span aria-hidden="true" class="glyphicon glyphicon-chevron-right"></span>
<span class="sr-only">next</span>
</a>
<ol class="carousel-indicators" ng-show="slides.length > 1">
<li ng-repeat="slide in slides | orderBy:indexOfSlide track by $index" ng-class="{ active: isActive(slide) }" ng-click="select(slide)">
<span class="sr-only">slide {{ $index + 1 }} of {{ slides.length }}<span ng-if="isActive(slide)">, currently active</span></span>
</li>
</ol>
+1 -3
View File
@@ -1,3 +1 @@
<div ng-class="{
'active': active
}" class="item text-center" ng-transclude></div>
<div class="text-center" ng-transclude></div>
+4 -4
View File
@@ -1,5 +1,5 @@
<div class="uib-datepicker" ng-switch="datepickerMode" role="application" ng-keydown="keydown($event)">
<uib-daypicker ng-switch-when="day" tabindex="0"></uib-daypicker>
<uib-monthpicker ng-switch-when="month" tabindex="0"></uib-monthpicker>
<uib-yearpicker ng-switch-when="year" tabindex="0"></uib-yearpicker>
<div ng-switch="datepickerMode">
<div uib-daypicker ng-switch-when="day" tabindex="0" class="uib-daypicker"></div>
<div uib-monthpicker ng-switch-when="month" tabindex="0" class="uib-monthpicker"></div>
<div uib-yearpicker ng-switch-when="year" tabindex="0" class="uib-yearpicker"></div>
</div>
+1 -1
View File
@@ -1,4 +1,4 @@
<table class="uib-daypicker" role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">
<table role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">
<thead>
<tr>
<th><button type="button" class="btn btn-default btn-sm pull-left uib-left" ng-click="move(-1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-left"></i></button></th>
+2 -2
View File
@@ -1,8 +1,8 @@
<table class="uib-monthpicker" role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">
<table role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">
<thead>
<tr>
<th><button type="button" class="btn btn-default btn-sm pull-left uib-left" ng-click="move(-1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-left"></i></button></th>
<th><button id="{{::uniqueId}}-title" role="heading" aria-live="assertive" aria-atomic="true" type="button" class="btn btn-default btn-sm uib-title" ng-click="toggleMode()" ng-disabled="datepickerMode === maxMode" tabindex="-1"><strong>{{title}}</strong></button></th>
<th colspan="{{::yearHeaderColspan}}"><button id="{{::uniqueId}}-title" role="heading" aria-live="assertive" aria-atomic="true" type="button" class="btn btn-default btn-sm uib-title" ng-click="toggleMode()" ng-disabled="datepickerMode === maxMode" tabindex="-1"><strong>{{title}}</strong></button></th>
<th><button type="button" class="btn btn-default btn-sm pull-right uib-right" ng-click="move(1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-right"></i></button></th>
</tr>
</thead>
+1 -1
View File
@@ -1,4 +1,4 @@
<table class="uib-yearpicker" role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">
<table role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">
<thead>
<tr>
<th><button type="button" class="btn btn-default btn-sm pull-left uib-left" ng-click="move(-1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-left"></i></button></th>
+10 -12
View File
@@ -1,12 +1,10 @@
<div>
<ul class="uib-datepicker-popup dropdown-menu uib-position-measure" dropdown-nested ng-if="isOpen" ng-keydown="keydown($event)" ng-click="$event.stopPropagation()">
<li ng-transclude></li>
<li ng-if="showButtonBar" class="uib-button-bar">
<span class="btn-group pull-left">
<button type="button" class="btn btn-sm btn-info uib-datepicker-current" ng-click="select('today', $event)" ng-disabled="isDisabled('today')">{{ getText('current') }}</button>
<button type="button" class="btn btn-sm btn-danger uib-clear" ng-click="select(null, $event)">{{ getText('clear') }}</button>
</span>
<button type="button" class="btn btn-sm btn-success pull-right uib-close" ng-click="close($event)">{{ getText('close') }}</button>
</li>
</ul>
</div>
<ul class="uib-datepicker-popup dropdown-menu uib-position-measure" dropdown-nested ng-if="isOpen" ng-keydown="keydown($event)" ng-click="$event.stopPropagation()">
<li ng-transclude></li>
<li ng-if="showButtonBar" class="uib-button-bar">
<span class="btn-group pull-left">
<button type="button" class="btn btn-sm btn-info uib-datepicker-current" ng-click="select('today', $event)" ng-disabled="isDisabled('today')">{{ getText('current') }}</button>
<button type="button" class="btn btn-sm btn-danger uib-clear" ng-click="select(null, $event)">{{ getText('clear') }}</button>
</span>
<button type="button" class="btn btn-sm btn-success pull-right uib-close" ng-click="close($event)">{{ getText('close') }}</button>
</li>
</ul>
-5
View File
@@ -1,5 +0,0 @@
<div class="modal-backdrop"
uib-modal-animation-class="fade"
modal-in-class="in"
ng-style="{'z-index': 1040 + (index && 1 || 0) + index*10}"
></div>
+1 -6
View File
@@ -1,6 +1 @@
<div modal-render="{{$isRendered}}" tabindex="-1" role="dialog" class="modal"
uib-modal-animation-class="fade"
modal-in-class="in"
ng-style="{'z-index': 1050 + index*10, display: 'block'}">
<div class="modal-dialog {{size ? 'modal-' + size : ''}}"><div class="modal-content" uib-modal-transclude></div></div>
</div>
<div class="modal-dialog {{size ? 'modal-' + size : ''}}"><div class="modal-content" uib-modal-transclude></div></div>
+2 -4
View File
@@ -1,4 +1,2 @@
<ul class="pager">
<li ng-class="{disabled: noPrevious()||ngDisabled, previous: align}"><a href ng-click="selectPage(page - 1, $event)">{{::getText('previous')}}</a></li>
<li ng-class="{disabled: noNext()||ngDisabled, next: align}"><a href ng-click="selectPage(page + 1, $event)">{{::getText('next')}}</a></li>
</ul>
<li ng-class="{disabled: noPrevious()||ngDisabled, previous: align}"><a href ng-click="selectPage(page - 1, $event)" ng-disabled="noPrevious()||ngDisabled" uib-tabindex-toggle>{{::getText('previous')}}</a></li>
<li ng-class="{disabled: noNext()||ngDisabled, next: align}"><a href ng-click="selectPage(page + 1, $event)" ng-disabled="noNext()||ngDisabled" uib-tabindex-toggle>{{::getText('next')}}</a></li>
+5 -7
View File
@@ -1,7 +1,5 @@
<ul class="pagination">
<li ng-if="::boundaryLinks" ng-class="{disabled: noPrevious()||ngDisabled}" class="pagination-first"><a href ng-click="selectPage(1, $event)">{{::getText('first')}}</a></li>
<li ng-if="::directionLinks" ng-class="{disabled: noPrevious()||ngDisabled}" class="pagination-prev"><a href ng-click="selectPage(page - 1, $event)">{{::getText('previous')}}</a></li>
<li ng-repeat="page in pages track by $index" ng-class="{active: page.active,disabled: ngDisabled&&!page.active}" class="pagination-page"><a href ng-click="selectPage(page.number, $event)">{{page.text}}</a></li>
<li ng-if="::directionLinks" ng-class="{disabled: noNext()||ngDisabled}" class="pagination-next"><a href ng-click="selectPage(page + 1, $event)">{{::getText('next')}}</a></li>
<li ng-if="::boundaryLinks" ng-class="{disabled: noNext()||ngDisabled}" class="pagination-last"><a href ng-click="selectPage(totalPages, $event)">{{::getText('last')}}</a></li>
</ul>
<li ng-if="::boundaryLinks" ng-class="{disabled: noPrevious()||ngDisabled}" class="pagination-first"><a href ng-click="selectPage(1, $event)" ng-disabled="noPrevious()||ngDisabled" uib-tabindex-toggle>{{::getText('first')}}</a></li>
<li ng-if="::directionLinks" ng-class="{disabled: noPrevious()||ngDisabled}" class="pagination-prev"><a href ng-click="selectPage(page - 1, $event)" ng-disabled="noPrevious()||ngDisabled" uib-tabindex-toggle>{{::getText('previous')}}</a></li>
<li ng-repeat="page in pages track by $index" ng-class="{active: page.active,disabled: ngDisabled&&!page.active}" class="pagination-page"><a href ng-click="selectPage(page.number, $event)" ng-disabled="ngDisabled&&!page.active" uib-tabindex-toggle>{{page.text}}</a></li>
<li ng-if="::directionLinks" ng-class="{disabled: noNext()||ngDisabled}" class="pagination-next"><a href ng-click="selectPage(page + 1, $event)" ng-disabled="noNext()||ngDisabled" uib-tabindex-toggle>{{::getText('next')}}</a></li>
<li ng-if="::boundaryLinks" ng-class="{disabled: noNext()||ngDisabled}" class="pagination-last"><a href ng-click="selectPage(totalPages, $event)" ng-disabled="noNext()||ngDisabled" uib-tabindex-toggle>{{::getText('last')}}</a></li>
+4 -9
View File
@@ -1,11 +1,6 @@
<div class="popover"
tooltip-animation-class="fade"
uib-tooltip-classes
ng-class="{ in: isOpen() }">
<div class="arrow"></div>
<div class="arrow"></div>
<div class="popover-inner">
<h3 class="popover-title" ng-bind="uibTitle" ng-if="uibTitle"></h3>
<div class="popover-content" ng-bind-html="contentExp()"></div>
</div>
<div class="popover-inner">
<h3 class="popover-title" ng-bind="uibTitle" ng-if="uibTitle"></h3>
<div class="popover-content" ng-bind-html="contentExp()"></div>
</div>
+6 -11
View File
@@ -1,13 +1,8 @@
<div class="popover"
tooltip-animation-class="fade"
uib-tooltip-classes
ng-class="{ in: isOpen() }">
<div class="arrow"></div>
<div class="arrow"></div>
<div class="popover-inner">
<h3 class="popover-title" ng-bind="uibTitle" ng-if="uibTitle"></h3>
<div class="popover-content"
uib-tooltip-template-transclude="contentExp()"
tooltip-template-transclude-scope="originScope()"></div>
</div>
<div class="popover-inner">
<h3 class="popover-title" ng-bind="uibTitle" ng-if="uibTitle"></h3>
<div class="popover-content"
uib-tooltip-template-transclude="contentExp()"
tooltip-template-transclude-scope="originScope()"></div>
</div>
+4 -9
View File
@@ -1,11 +1,6 @@
<div class="popover"
tooltip-animation-class="fade"
uib-tooltip-classes
ng-class="{ in: isOpen() }">
<div class="arrow"></div>
<div class="arrow"></div>
<div class="popover-inner">
<h3 class="popover-title" ng-bind="uibTitle" ng-if="uibTitle"></h3>
<div class="popover-content" ng-bind="content"></div>
</div>
<div class="popover-inner">
<h3 class="popover-title" ng-bind="uibTitle" ng-if="uibTitle"></h3>
<div class="popover-content" ng-bind="content"></div>
</div>
+6 -6
View File
@@ -1,11 +1,11 @@
<table class="uib-timepicker">
<tbody>
<tr class="text-center" ng-show="::showSpinners">
<td class="uib-increment hours"><a ng-click="incrementHours()" ng-class="{disabled: noIncrementHours()}" class="btn btn-link" ng-disabled="noIncrementHours()" tabindex="{{::tabindex}}"><span class="glyphicon glyphicon-chevron-up"></span></a></td>
<td class="uib-increment hours"><a ng-click="incrementHours()" ng-class="{disabled: noIncrementHours()}" class="btn btn-link" ng-disabled="noIncrementHours()" tabindex="-1"><span class="glyphicon glyphicon-chevron-up"></span></a></td>
<td>&nbsp;</td>
<td class="uib-increment minutes"><a ng-click="incrementMinutes()" ng-class="{disabled: noIncrementMinutes()}" class="btn btn-link" ng-disabled="noIncrementMinutes()" tabindex="{{::tabindex}}"><span class="glyphicon glyphicon-chevron-up"></span></a></td>
<td class="uib-increment minutes"><a ng-click="incrementMinutes()" ng-class="{disabled: noIncrementMinutes()}" class="btn btn-link" ng-disabled="noIncrementMinutes()" tabindex="-1"><span class="glyphicon glyphicon-chevron-up"></span></a></td>
<td ng-show="showSeconds">&nbsp;</td>
<td ng-show="showSeconds" class="uib-increment seconds"><a ng-click="incrementSeconds()" ng-class="{disabled: noIncrementSeconds()}" class="btn btn-link" ng-disabled="noIncrementSeconds()" tabindex="{{::tabindex}}"><span class="glyphicon glyphicon-chevron-up"></span></a></td>
<td ng-show="showSeconds" class="uib-increment seconds"><a ng-click="incrementSeconds()" ng-class="{disabled: noIncrementSeconds()}" class="btn btn-link" ng-disabled="noIncrementSeconds()" tabindex="-1"><span class="glyphicon glyphicon-chevron-up"></span></a></td>
<td ng-show="showMeridian"></td>
</tr>
<tr>
@@ -23,11 +23,11 @@
<td ng-show="showMeridian" class="uib-time am-pm"><button type="button" ng-class="{disabled: noToggleMeridian()}" class="btn btn-default text-center" ng-click="toggleMeridian()" ng-disabled="noToggleMeridian()" tabindex="{{::tabindex}}">{{meridian}}</button></td>
</tr>
<tr class="text-center" ng-show="::showSpinners">
<td class="uib-decrement hours"><a ng-click="decrementHours()" ng-class="{disabled: noDecrementHours()}" class="btn btn-link" ng-disabled="noDecrementHours()" tabindex="{{::tabindex}}"><span class="glyphicon glyphicon-chevron-down"></span></a></td>
<td class="uib-decrement hours"><a ng-click="decrementHours()" ng-class="{disabled: noDecrementHours()}" class="btn btn-link" ng-disabled="noDecrementHours()" tabindex="-1"><span class="glyphicon glyphicon-chevron-down"></span></a></td>
<td>&nbsp;</td>
<td class="uib-decrement minutes"><a ng-click="decrementMinutes()" ng-class="{disabled: noDecrementMinutes()}" class="btn btn-link" ng-disabled="noDecrementMinutes()" tabindex="{{::tabindex}}"><span class="glyphicon glyphicon-chevron-down"></span></a></td>
<td class="uib-decrement minutes"><a ng-click="decrementMinutes()" ng-class="{disabled: noDecrementMinutes()}" class="btn btn-link" ng-disabled="noDecrementMinutes()" tabindex="-1"><span class="glyphicon glyphicon-chevron-down"></span></a></td>
<td ng-show="showSeconds">&nbsp;</td>
<td ng-show="showSeconds" class="uib-decrement seconds"><a ng-click="decrementSeconds()" ng-class="{disabled: noDecrementSeconds()}" class="btn btn-link" ng-disabled="noDecrementSeconds()" tabindex="{{::tabindex}}"><span class="glyphicon glyphicon-chevron-down"></span></a></td>
<td ng-show="showSeconds" class="uib-decrement seconds"><a ng-click="decrementSeconds()" ng-class="{disabled: noDecrementSeconds()}" class="btn btn-link" ng-disabled="noDecrementSeconds()" tabindex="-1"><span class="glyphicon glyphicon-chevron-down"></span></a></td>
<td ng-show="showMeridian"></td>
</tr>
</tbody>
+2 -7
View File
@@ -1,7 +1,2 @@
<div class="tooltip"
tooltip-animation-class="fade"
uib-tooltip-classes
ng-class="{ in: isOpen() }">
<div class="tooltip-arrow"></div>
<div class="tooltip-inner" ng-bind-html="contentExp()"></div>
</div>
<div class="tooltip-arrow"></div>
<div class="tooltip-inner" ng-bind-html="contentExp()"></div>
+2 -7
View File
@@ -1,7 +1,2 @@
<div class="tooltip"
tooltip-animation-class="fade"
uib-tooltip-classes
ng-class="{ in: isOpen() }">
<div class="tooltip-arrow"></div>
<div class="tooltip-inner" ng-bind="content"></div>
</div>
<div class="tooltip-arrow"></div>
<div class="tooltip-inner" ng-bind="content"></div>
+4 -9
View File
@@ -1,9 +1,4 @@
<div class="tooltip"
tooltip-animation-class="fade"
uib-tooltip-classes
ng-class="{ in: isOpen() }">
<div class="tooltip-arrow"></div>
<div class="tooltip-inner"
uib-tooltip-template-transclude="contentExp()"
tooltip-template-transclude-scope="originScope()"></div>
</div>
<div class="tooltip-arrow"></div>
<div class="tooltip-inner"
uib-tooltip-template-transclude="contentExp()"
tooltip-template-transclude-scope="originScope()"></div>
+1 -1
View File
@@ -1,5 +1,5 @@
<ul class="dropdown-menu" ng-show="isOpen() && !moveInProgress" ng-style="{top: position().top+'px', left: position().left+'px'}" role="listbox" aria-hidden="{{!isOpen()}}">
<li ng-repeat="match in matches track by $index" ng-class="{active: isActive($index) }" ng-mouseenter="selectActive($index)" ng-click="selectMatch($index, $event)" role="option" id="{{::match.id}}">
<li class="uib-typeahead-match" ng-repeat="match in matches track by $index" ng-class="{active: isActive($index) }" ng-mouseenter="selectActive($index)" ng-click="selectMatch($index, $event)" role="option" id="{{::match.id}}">
<div uib-typeahead-match index="$index" match="match" query="query" template-url="templateUrl"></div>
</li>
</ul>