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

Commit b3b8fab

Browse files
crisbetojelbourn
authored andcommitted
fix(datepicker): ensure that all month/year elements have the expected height (#9893)
The datepicker's calendar uses `md-virtual-repeat` to render itself. The virtual repeater expects that all elements that it has rendered will have a certain height, however this may not be the case if it renders an empty month/year element in the calendar. If this happens (it's usually if the current value and the maximum date are in the same month), the calendar can start jumping while scrolling. This change adds an empty `<tr>` that will ensure that even empty elements have a height. When the element is compiled, the empty `<tr>` will be overwritten. Fixes #9863.
1 parent a090079 commit b3b8fab

File tree

3 files changed

+67
-5
lines changed

3 files changed

+67
-5
lines changed

src/components/datepicker/js/calendar.spec.js

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ describe('md-calendar', function() {
7070

7171
/** Find the `tbody` for a year in the calendar. */
7272
function findYearElement(year) {
73-
var years = element.querySelectorAll('[md-calendar-year-body]');
73+
var node = element[0] || element;
74+
var years = node.querySelectorAll('[md-calendar-year-body]');
7475
var yearHeader = year.toString();
7576
var target;
7677

@@ -313,6 +314,31 @@ describe('md-calendar', function() {
313314
expect(pageScope.myDate).toBeSameDayAs(initialDate);
314315
});
315316

317+
it('should ensure that all month elements have a height when the max ' +
318+
'date is in the same month as the current date', function() {
319+
320+
ngElement.remove();
321+
var newScope = $rootScope.$new();
322+
newScope.myDate = new Date(2016, JUN, 15);
323+
newScope.maxDate = new Date(2016, JUN, 20);
324+
element = createElement(newScope)[0];
325+
applyDateChange();
326+
327+
var monthWrapper = angular.element(element.querySelector('md-calendar-month'));
328+
var scroller = monthWrapper.controller('mdCalendarMonth').calendarScroller;
329+
330+
scroller.scrollTop -= 50;
331+
angular.element(scroller).triggerHandler('scroll');
332+
333+
var monthElements = $mdUtil.nodesToArray(
334+
element.querySelectorAll('[md-calendar-month-body]')
335+
);
336+
337+
expect(monthElements.every(function(currentMonthElement) {
338+
return currentMonthElement.offsetHeight > 0;
339+
})).toBe(true);
340+
});
341+
316342
describe('weeks header', function() {
317343
it('should display the weeks header in the first row', function() {
318344
var header = element.querySelector('.md-calendar-day-header tr');
@@ -480,13 +506,39 @@ describe('md-calendar', function() {
480506
var newScope = $rootScope.$new();
481507
newScope.minDate = new Date(2014, JUN, 5);
482508
newScope.myDate = new Date(2014, JUL, 15);
483-
element = createElement(newScope)[0];
484-
angular.element(element).controller('mdCalendar').setCurrentView('year');
509+
element = createElement(newScope);
510+
element.controller('mdCalendar').setCurrentView('year');
485511
applyDateChange();
486512

487513
expect(findYearElement(2014)).not.toBeNull();
488514
expect(findYearElement(2013)).toBeNull();
489515
});
516+
517+
it('should ensure that all year elements have a height when the ' +
518+
'current date is in the same month as the max date', function() {
519+
520+
ngElement.remove();
521+
var newScope = $rootScope.$new();
522+
newScope.myDate = new Date(2016, JUN, 15);
523+
newScope.maxDate = new Date(2016, JUN, 20);
524+
element = createElement(newScope);
525+
element.controller('mdCalendar').setCurrentView('year');
526+
applyDateChange();
527+
528+
var yearWrapper = angular.element(element[0].querySelector('md-calendar-year'));
529+
var scroller = yearWrapper.controller('mdCalendarYear').calendarScroller;
530+
531+
scroller.scrollTop -= 50;
532+
angular.element(scroller).triggerHandler('scroll');
533+
534+
var yearElements = $mdUtil.nodesToArray(
535+
element[0].querySelectorAll('[md-calendar-year-body]')
536+
);
537+
538+
expect(yearElements.every(function(currentYearElement) {
539+
return currentYearElement.offsetHeight > 0;
540+
})).toBe(true);
541+
});
490542
});
491543

492544
it('should have ids for date elements unique to the directive instance', function() {

src/components/datepicker/js/calendarMonth.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,13 @@
3232
'md-month-offset="$index" ' +
3333
'class="md-calendar-month" ' +
3434
'md-start-index="monthCtrl.getSelectedMonthIndex()" ' +
35-
'md-item-size="' + TBODY_HEIGHT + '"></tbody>' +
35+
'md-item-size="' + TBODY_HEIGHT + '">' +
36+
37+
// The <tr> ensures that the <tbody> will always have the
38+
// proper height, even if it's empty. If it's content is
39+
// compiled, the <tr> will be overwritten.
40+
'<tr aria-hidden="true" style="height:' + TBODY_HEIGHT + 'px;"></tr>' +
41+
'</tbody>' +
3642
'</table>' +
3743
'</md-virtual-repeat-container>' +
3844
'</div>',

src/components/datepicker/js/calendarYear.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@
2323
'md-virtual-repeat="i in yearCtrl.items" ' +
2424
'md-year-offset="$index" class="md-calendar-year" ' +
2525
'md-start-index="yearCtrl.getFocusedYearIndex()" ' +
26-
'md-item-size="' + TBODY_HEIGHT + '"></tbody>' +
26+
'md-item-size="' + TBODY_HEIGHT + '">' +
27+
// The <tr> ensures that the <tbody> will have the proper
28+
// height, even though it may be empty.
29+
'<tr aria-hidden="true" style="height:' + TBODY_HEIGHT + 'px;"></tr>' +
30+
'</tbody>' +
2731
'</table>' +
2832
'</md-virtual-repeat-container>' +
2933
'</div>',

0 commit comments

Comments
 (0)