Skip to content
This repository has been archived by the owner on Sep 5, 2024. It is now read-only.

fix(datepicker): fix bad valid date check and resize-on-open. #4270

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions src/components/datepicker/datePicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@

// This pane will be detached from here and re-attached to the document body.
'<div class="md-datepicker-calendar-pane md-whiteframe-z1">' +
'<div class="md-datepicker-input-mask"></div>' +
'<div class="md-datepicker-input-mask">' +
'<div class="md-datepicker-input-mask-opaque"></div>' +
'</div>' +
'<div class="md-datepicker-calendar">' +
'<md-calendar role="dialog" aria-label="{{::ctrl.dateLocale.msgCalendar}}" ' +
'ng-model="ctrl.date" ng-if="ctrl.isCalendarOpen"></md-calendar>' +
Expand Down Expand Up @@ -134,6 +136,12 @@
/** @type {HTMLElement} Calendar icon button. */
this.calendarButton = $element[0].querySelector('.md-datepicker-button');

/**
* Element covering everything but the input in the top of the floating calendar pane.
* @type {HTMLElement}
*/
this.inputMask = $element[0].querySelector('.md-datepicker-input-mask-opaque');

/** @final {!angular.JQLite} */
this.$element = $element;

Expand Down Expand Up @@ -262,7 +270,7 @@

Object.defineProperty(this, 'placeholder', {
get: function() { return self.inputElement.placeholder; },
set: function(value) { self.inputElement.placeholder = value; }
set: function(value) { self.inputElement.placeholder = value || ''; }
});
};

Expand Down Expand Up @@ -313,6 +321,12 @@
calendarPane.style.top = (elementRect.top - bodyRect.top) + 'px';
document.body.appendChild(this.calendarPane);

// The top of the calendar pane is a transparent box that shows the text input underneath.
// Since the pane is flowing, though, the page underneath the pane *adjacent* to the input is
// also shown unless we cover it up. The inputMask does this by filling up the remaining space
// based on the width of the input.
this.inputMask.style.left = elementRect.width + 'px';

// Add CSS class after one frame to trigger open animation.
this.$$rAF(function() {
calendarPane.classList.add('md-pane-open');
Expand Down
15 changes: 14 additions & 1 deletion src/components/datepicker/datePicker.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ $md-datepicker-button-gap: 12px; // Space between the text input and the calend
$md-datepicker-border-bottom-gap: 5px; // Space between input and the grey underline.
$md-datepicker-open-animation-duration: 0.2s;

md-datepicker {
// Don't let linebreaks happen between the open icon-button and the input.
white-space: nowrap;
}

// The calendar icon button used to open the calendar pane.
// Need absurd specificty to override md-button.md-icon-button.
Expand Down Expand Up @@ -62,12 +66,22 @@ $md-datepicker-open-animation-duration: 0.2s;
.md-datepicker-input-mask {
height: 40px;
width: $md-calendar-width;
position: relative;

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

.md-datepicker-input-mask-opaque {
position: absolute;
right: 0;
left: 120px;
background: white;

height: 100%;
}

// The calendar portion of the floating pane (vs. the input mask).
.md-datepicker-calendar {
opacity: 0;
Expand Down Expand Up @@ -136,7 +150,6 @@ md-datepicker[disabled] {
// Open state for all of the elements of the picker.
.md-datepicker-open {
.md-datepicker-input-container {
min-width: $md-calendar-width;
margin-left: -$md-datepicker-button-gap;
border: none;
}
Expand Down
1 change: 1 addition & 0 deletions src/components/datepicker/datePicker.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ describe('md-date-picker', function() {
$timeout.flush();

expect(controller.calendarPane.offsetHeight).toBeGreaterThan(0);
expect(controller.inputMask.style.left).toBe(controller.inputContainer.clientWidth + 'px');

// Click off of the calendar.
document.body.click();
Expand Down
2 changes: 1 addition & 1 deletion src/components/datepicker/dateUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@
* @return {boolean} Whether the date is a valid Date.
*/
function isValidDate(date) {
return date !== null && date.getTime && !isNaN(date.getTime());
return date != null && date.getTime && !isNaN(date.getTime());
Copy link
Contributor

Choose a reason for hiding this comment

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

Would this not be more clear (and consistent with our other code):

return date && date.getTime && !isNaN(date.getTime());

}

/**
Expand Down
15 changes: 15 additions & 0 deletions src/components/datepicker/dateUtil.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -303,4 +303,19 @@ describe('$$mdDateUtil', function() {
expect(dayAtMidnight.getSeconds()).toBe(0);
expect(dayAtMidnight.getMilliseconds()).toBe(0);
});

it('should determine whether dates are valid', function() {
expect(dateUtil.isValidDate(null)).toBeFalsy();
expect(dateUtil.isValidDate(undefined)).toBeFalsy();
expect(dateUtil.isValidDate('')).toBeFalsy();
expect(dateUtil.isValidDate(0)).toBeFalsy();
expect(dateUtil.isValidDate(NaN)).toBeFalsy();
expect(dateUtil.isValidDate(123456789)).toBeFalsy();
expect(dateUtil.isValidDate('123456789')).toBeFalsy();
expect(dateUtil.isValidDate(new Date(''))).toBeFalsy();
expect(dateUtil.isValidDate(new Date('Banjo'))).toBeFalsy();
expect(dateUtil.isValidDate(new Date(NaN))).toBeFalsy();

expect(dateUtil.isValidDate(new Date())).toBe(true);
});
});