From a6cdd6eed72b4346eb790a2a8559fee2658dbca5 Mon Sep 17 00:00:00 2001
From: Wesley Cho <wesley.cho@gmail.com>
Date: Sun, 11 Oct 2015 08:07:06 -0700
Subject: [PATCH] feat(timepicker): add accessibility improvements

- Add ng-disabled usage in template
- Add `timepickerTabindex` attribute support for binding tabindex to
  timepicker controls
---
 src/timepicker/docs/readme.md          |  4 ++++
 src/timepicker/test/timepicker.spec.js | 15 +++++++++++++++
 src/timepicker/timepicker.js           |  8 ++++++--
 template/timepicker/timepicker.html    | 14 +++++++-------
 4 files changed, 32 insertions(+), 9 deletions(-)

diff --git a/src/timepicker/docs/readme.md b/src/timepicker/docs/readme.md
index a168b0dcc4..4e2a231b7c 100644
--- a/src/timepicker/docs/readme.md
+++ b/src/timepicker/docs/readme.md
@@ -50,3 +50,7 @@ All settings can be provided as attributes in the `<uib-timepicker>` or globally
  * `max`
     _(Defaults: undefined)_ :
      Maximum time a user can select
+
+ * `tabindex`
+    _(Defaults: 0)_ :
+     Sets tabindex for each control in timepicker
\ No newline at end of file
diff --git a/src/timepicker/test/timepicker.spec.js b/src/timepicker/test/timepicker.spec.js
index 93dac89664..128471ec27 100644
--- a/src/timepicker/test/timepicker.spec.js
+++ b/src/timepicker/test/timepicker.spec.js
@@ -937,6 +937,21 @@ describe('timepicker directive', function() {
       expect(getModelState()).toEqual([16, 40]);
       expect(element.hasClass('ng-invalid-time')).toBe(false);
     });
+
+    it('should have a default tabindex of 0', function() {
+      element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($rootScope);
+      $rootScope.$digest();
+
+      expect(element.isolateScope().tabindex).toBe(0);
+    });
+
+    fit('should have the correct tabindex', function() {
+      element = $compile('<uib-timepicker ng-model="time" tabindex="5"></uib-timepicker>')($rootScope);
+      $rootScope.$digest();
+
+      expect(element.attr('tabindex')).toBe(undefined);
+      expect(element.isolateScope().tabindex).toBe('5');
+    });
   });
 
   describe('when model is not a Date', function() {
diff --git a/src/timepicker/timepicker.js b/src/timepicker/timepicker.js
index 6ab5917750..8e91e9b0e3 100644
--- a/src/timepicker/timepicker.js
+++ b/src/timepicker/timepicker.js
@@ -11,11 +11,14 @@ angular.module('ui.bootstrap.timepicker', [])
   showSpinners: true
 })
 
-.controller('UibTimepickerController', ['$scope', '$attrs', '$parse', '$log', '$locale', 'uibTimepickerConfig', function($scope, $attrs, $parse, $log, $locale, timepickerConfig) {
+.controller('UibTimepickerController', ['$scope', '$element', '$attrs', '$parse', '$log', '$locale', 'uibTimepickerConfig', function($scope, $element, $attrs, $parse, $log, $locale, timepickerConfig) {
   var selected = new Date(),
       ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
       meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS;
 
+  $scope.tabindex = angular.isDefined($attrs.tabindex) ? $attrs.tabindex : 0;
+  $element.removeAttr('tabindex');
+
   this.init = function(ngModelCtrl_, inputs) {
     ngModelCtrl = ngModelCtrl_;
     ngModelCtrl.$render = this.render;
@@ -386,13 +389,14 @@ angular.module('ui.bootstrap.timepicker')
 
 .value('$timepickerSuppressWarning', false)
 
-.controller('TimepickerController', ['$scope', '$attrs', '$controller', '$log', '$timepickerSuppressWarning', function($scope, $attrs, $controller, $log, $timepickerSuppressWarning) {
+.controller('TimepickerController', ['$scope', '$element', '$attrs', '$controller', '$log', '$timepickerSuppressWarning', function($scope, $element, $attrs, $controller, $log, $timepickerSuppressWarning) {
   if (!$timepickerSuppressWarning) {
     $log.warn('TimepickerController is now deprecated. Use UibTimepickerController instead.');
   }
 
   return $controller('UibTimepickerController', {
     $scope: $scope,
+    $element: $element,
     $attrs: $attrs
   });
 }])
diff --git a/template/timepicker/timepicker.html b/template/timepicker/timepicker.html
index f09963333d..1873841f49 100644
--- a/template/timepicker/timepicker.html
+++ b/template/timepicker/timepicker.html
@@ -1,25 +1,25 @@
 <table>
   <tbody>
     <tr class="text-center" ng-show="::showSpinners">
-      <td><a ng-click="incrementHours()" ng-class="{disabled: noIncrementHours()}" class="btn btn-link"><span class="glyphicon glyphicon-chevron-up"></span></a></td>
+      <td><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>&nbsp;</td>
-      <td><a ng-click="incrementMinutes()" ng-class="{disabled: noIncrementMinutes()}" class="btn btn-link"><span class="glyphicon glyphicon-chevron-up"></span></a></td>
+      <td><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 ng-show="showMeridian"></td>
     </tr>
     <tr>
       <td class="form-group" ng-class="{'has-error': invalidHours}">
-        <input style="width:50px;" type="text" ng-model="hours" ng-change="updateHours()" class="form-control text-center" ng-readonly="::readonlyInput" maxlength="2">
+        <input style="width:50px;" type="text" ng-model="hours" ng-change="updateHours()" class="form-control text-center" ng-readonly="::readonlyInput" maxlength="2" tabindex="{{::tabindex}}">
       </td>
       <td>:</td>
       <td class="form-group" ng-class="{'has-error': invalidMinutes}">
-        <input style="width:50px;" type="text" ng-model="minutes" ng-change="updateMinutes()" class="form-control text-center" ng-readonly="::readonlyInput" maxlength="2">
+        <input style="width:50px;" type="text" ng-model="minutes" ng-change="updateMinutes()" class="form-control text-center" ng-readonly="::readonlyInput" maxlength="2" tabindex="{{::tabindex}}">
       </td>
-      <td ng-show="showMeridian"><button type="button" ng-class="{disabled: noToggleMeridian()}" class="btn btn-default text-center" ng-click="toggleMeridian()">{{meridian}}</button></td>
+      <td ng-show="showMeridian"><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><a ng-click="decrementHours()" ng-class="{disabled: noDecrementHours()}" class="btn btn-link"><span class="glyphicon glyphicon-chevron-down"></span></a></td>
+      <td><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>&nbsp;</td>
-      <td><a ng-click="decrementMinutes()" ng-class="{disabled: noDecrementMinutes()}" class="btn btn-link"><span class="glyphicon glyphicon-chevron-down"></span></a></td>
+      <td><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 ng-show="showMeridian"></td>
     </tr>
   </tbody>