Skip to content
This repository has been archived by the owner on Aug 29, 2023. It is now read-only.

feat(datepicker): migrate to mdPanel #9641

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/components/datepicker/datePicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
angular.module('material.components.datepicker', [
'material.core',
'material.components.icon',
'material.components.virtualRepeat'
'material.components.virtualRepeat',
'material.components.panel'
]);
19 changes: 2 additions & 17 deletions src/components/datepicker/datePicker.scss
Original file line number Diff line number Diff line change
Expand Up @@ -116,19 +116,8 @@ md-datepicker {
}
}

.md-datepicker-is-showing .md-scroll-mask {
z-index: $z-index-calendar-pane - 1;
}

// Floating pane that contains the calendar at the bottom of the input.
.md-datepicker-calendar-pane {
// On most browsers the `scale(0)` below prevents this element from
// overflowing it's parent, however IE and Edge seem to disregard it.
// The `left: -100%` pulls the element back in order to ensure that
// it doesn't cause an overflow.
position: absolute;
top: 0;
left: -100%;
z-index: $z-index-calendar-pane;
border-width: 1px;
border-style: solid;
Expand All @@ -149,19 +138,15 @@ md-datepicker {
width: $md-calendar-width;
position: relative;
overflow: hidden;

background: transparent;
pointer-events: none;
cursor: text;
}

// The calendar portion of the floating pane (vs. the input mask).
.md-datepicker-calendar {
opacity: 0;
// Use a modified timing function (from swift-ease-out) so that the opacity part of the
// animation doesn't come in as quickly so that the floating pane doesn't ever seem to
// cover up the trigger input.
transition: opacity $md-datepicker-open-animation-duration cubic-bezier(0.5, 0, 0.25, 1);
opacity: 0;

.md-pane-open & {
opacity: 1;
Expand Down Expand Up @@ -194,7 +179,7 @@ md-datepicker {
.md-datepicker-triangle-button {
position: absolute;
@include rtl-prop(right, left, 0, auto);
top: $md-date-arrow-size;
top: $md-date-arrow-size / 2;

// TODO(jelbourn): This position isn't great on all platforms.
@include rtl(transform, translateY(-25%) translateX(45%), translateY(-25%) translateX(-45%));
Expand Down
56 changes: 31 additions & 25 deletions src/components/datepicker/js/calendar.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@
minDate: '=mdMinDate',
maxDate: '=mdMaxDate',
dateFilter: '=mdDateFilter',
_currentView: '@mdCurrentView'
_currentView: '@mdCurrentView',

// private way of passing in the panel from the datepicker
_panelRef: '=mdPanelRef'
},
require: ['ngModel', 'mdCalendar'],
controller: CalendarCtrl,
Expand Down Expand Up @@ -197,13 +200,18 @@

var boundKeyHandler = angular.bind(this, this.handleKeyEvent);

// Bind the keydown handler to the body, in order to handle cases where the focused
// element gets removed from the DOM and stops propagating click events.
angular.element(document.body).on('keydown', boundKeyHandler);
if (this._panelRef) {
// Bind the keydown handler to the body, in order to handle cases where the focused
// element gets removed from the DOM and stops propagating key events.
angular.element(document.body).on('keydown', boundKeyHandler);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is my first time looking at the date picker code. Can you explain what edge case this helps to prevent? Do other components care about this or have similar functionality?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The calendar currently focuses the active date's cell (I believe for accessibility reasons), which causes any keyboard events to be captured by the calendar. The issue is that the calendar uses mdVirtualRepeat which will remove the cell eventually, if you scroll long enough. This causes the browser to shift focus back to the body which stops the keyboard events from working.


$scope.$on('$destroy', function() {
angular.element(document.body).off('keydown', boundKeyHandler);
});
$scope.$on('$destroy', function() {
angular.element(document.body).off('keydown', boundKeyHandler);
});
} else {
// If the calendar on it's own, it shouldn't bind global key handlers.
$element.on('keydown', boundKeyHandler);
}

if (this.minDate && this.minDate > $mdDateLocale.firstRenderableDate) {
this.firstRenderableDate = this.minDate;
Expand Down Expand Up @@ -345,27 +353,25 @@
CalendarCtrl.prototype.handleKeyEvent = function(event) {
var self = this;

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) {
self.$scope.$emit('md-calendar-close');

if (event.which == self.keyCode.TAB) {
if (!this._panelRef || this._panelRef.isAttached) {
this.$scope.$apply(function() {
// Capture tabbing and emit back up so that a wrapping component
// (such as a date-picker) can decide to close.
if (event.which === self.keyCode.TAB) {
self.$scope.$emit('md-calendar-close');
event.preventDefault();
return;
}

return;
}

// Broadcast the action that any child controllers should take.
var action = self.getActionFromKeyEvent(event);
if (action) {
event.preventDefault();
event.stopPropagation();
self.$scope.$broadcast('md-calendar-parent-action', action);
}
});
// Broadcast the action that any child controllers should take.
var action = self.getActionFromKeyEvent(event);
if (action) {
event.preventDefault();
event.stopPropagation();
self.$scope.$broadcast('md-calendar-parent-action', action);
}
});
}
};

/**
Expand Down
12 changes: 6 additions & 6 deletions src/components/datepicker/js/calendar.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ describe('md-calendar', function() {
function dispatchKeyEvent(keyCode, opt_modifiers) {
var mod = opt_modifiers || {};

angular.element(document.body).triggerHandler({
calendarController.$element.triggerHandler({
type: 'keydown',
keyCode: keyCode,
which: keyCode,
Expand Down Expand Up @@ -717,19 +717,19 @@ describe('md-calendar', function() {
});
});

it('should fire an event when escape is pressed', function() {
var escapeHandler = jasmine.createSpy('escapeHandler');
pageScope.$on('md-calendar-close', escapeHandler);
it('should fire an event when tabbing away', function() {
var tabHandler = jasmine.createSpy('tabHandler');
pageScope.$on('md-calendar-close', tabHandler);

pageScope.myDate = new Date(2014, FEB, 11);
applyDateChange();
var selectedDate = element.querySelector('.md-calendar-selected-date');
selectedDate.focus();

dispatchKeyEvent(keyCodes.ESCAPE);
dispatchKeyEvent(keyCodes.TAB);
pageScope.$apply();

expect(escapeHandler).toHaveBeenCalled();
expect(tabHandler).toHaveBeenCalled();
});
});

Expand Down