From d0800659003656deee7e0b96fea495eb8bbececb Mon Sep 17 00:00:00 2001 From: Michael Prentice Date: Tue, 22 Sep 2020 21:49:41 -0400 Subject: [PATCH] fix(calendar): allow tabbing out when in standalone mode - using the `md-calendar` outside of the `md-datepicker` no longer breaks the page's tab order - change one access of the global `document` to use `$document` - improve Closure types - update JSDoc for `CalendarCtrl.handleKeyEvent()` Fixes #9794 --- src/components/datepicker/js/calendar.js | 33 +++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/components/datepicker/js/calendar.js b/src/components/datepicker/js/calendar.js index 004303a50ac..c6f1f3414bb 100644 --- a/src/components/datepicker/js/calendar.js +++ b/src/components/datepicker/js/calendar.js @@ -107,9 +107,8 @@ * Controller for the mdCalendar component. * @ngInject @constructor */ - function CalendarCtrl($element, $scope, $$mdDateUtil, $mdUtil, - $mdConstant, $mdTheming, $$rAF, $attrs, $mdDateLocale, $filter) { - + function CalendarCtrl($element, $scope, $$mdDateUtil, $mdUtil, $mdConstant, $mdTheming, $$rAF, + $attrs, $mdDateLocale, $filter, $document) { $mdTheming($element); /** @@ -229,6 +228,12 @@ */ this.scrollbarWidth = 0; + /** + * @type {boolean} set to true if the calendar is being used "standalone" (outside of a + * md-datepicker). + */ + this.standaloneMode = false; + // Unless the user specifies so, the calendar should not be a tab stop. // This is necessary because ngAria might add a tabindex to anything with an ng-model // (based on whether or not the user has turned that particular feature on/off). @@ -245,8 +250,9 @@ var handleKeyElement; if ($element.parent().hasClass('md-datepicker-calendar')) { - handleKeyElement = angular.element(document.body); + handleKeyElement = angular.element($document[0].body); } else { + this.standaloneMode = true; handleKeyElement = $element; } @@ -445,8 +451,8 @@ * Normalizes the key event into an action name. The action will be broadcast * to the child controllers. * @param {KeyboardEvent} event - * @returns {String} The action that should be taken, or null if the key - * does not match a calendar shortcut. + * @returns {string} The action that should be taken, or null if the key + * does not match a calendar shortcut. */ CalendarCtrl.prototype.getActionFromKeyEvent = function(event) { var keyCode = this.keyCode; @@ -471,8 +477,13 @@ }; /** - * Handles a key event in the calendar with the appropriate action. The action will either - * be to select the focused date or to navigate to focus a new date. + * Handles a key event in the calendar with the appropriate action. + * The action will either + * - select the focused date + * - navigate to focus a new date + * - emit a md-calendar-close event if in a md-datepicker panel + * - emit a md-calendar-parent-action + * - delegate to normal tab order if the TAB key is pressed in standalone mode * @param {KeyboardEvent} event */ CalendarCtrl.prototype.handleKeyEvent = function(event) { @@ -481,13 +492,17 @@ this.$scope.$apply(function() { // Capture escape and emit back up so that a wrapping component // (such as a date-picker) can decide to close. - if (event.which === self.keyCode.ESCAPE || event.which === self.keyCode.TAB) { + if (event.which === self.keyCode.ESCAPE || + (event.which === self.keyCode.TAB && !self.standaloneMode)) { self.$scope.$emit('md-calendar-close'); if (event.which === self.keyCode.TAB) { event.preventDefault(); } + return; + } else if (event.which === self.keyCode.TAB && self.standaloneMode) { + // delegate to the normal tab order if the TAB key is pressed in standalone mode return; }