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

fix(datepicker): prevent calendar pane being in wrong position after body scrolling is disabled. Fixes #5201 #5918

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
9 changes: 5 additions & 4 deletions src/components/datepicker/datePicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -502,8 +502,6 @@
if (!this.isCalendarOpen && !this.isDisabled) {
this.isCalendarOpen = true;
this.calendarPaneOpenedFrom = event.target;
this.attachCalendarPane();
this.focusCalendar();

// Because the calendar pane is attached directly to the body, it is possible that the
// rest of the component (input, etc) is in a different scrolling container, such as
Expand All @@ -512,11 +510,14 @@
// also matches the native behavior for things like `<select>` on Mac and Windows.
this.$mdUtil.disableScrollAround(this.calendarPane);

this.attachCalendarPane();
this.focusCalendar();

// Attach click listener inside of a timeout because, if this open call was triggered by a
// click, we don't want it to be immediately propogated up to the body and handled.
var self = this;
this.$mdUtil.nextTick(function() {
document.body.addEventListener('click', self.bodyClickHandler);
document.documentElement.addEventListener('click', self.bodyClickHandler);
}, false);

window.addEventListener('resize', this.windowResizeHandler);
Expand All @@ -532,7 +533,7 @@
this.calendarPaneOpenedFrom = null;
this.$mdUtil.enableScrolling();

document.body.removeEventListener('click', this.bodyClickHandler);
document.documentElement.removeEventListener('click', this.bodyClickHandler);
window.removeEventListener('resize', this.windowResizeHandler);
}
};
Expand Down
36 changes: 33 additions & 3 deletions src/components/datepicker/datePicker.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -333,14 +333,14 @@ describe('md-date-picker', function() {
// Expect that the pane is on-screen.
var paneRect = controller.calendarPane.getBoundingClientRect();
expect(paneRect.bottom).toBeLessThan(window.innerHeight + 1);
document.body.removeChild(superLongElement);

document.body.removeChild(element);
document.body.removeChild(superLongElement);
});

it('should adjust the pane position if it would go off-screen if body is not scrollable',
function() {
// Make the body super huge and scroll halfway down.
// Make the body super huge and scroll down a bunch.
var body = document.body;
var superLongElement = document.createElement('div');
superLongElement.style.height = '10000px';
Expand All @@ -365,13 +365,43 @@ describe('md-date-picker', function() {
// Expect that the pane is on-screen.
var paneRect = controller.calendarPane.getBoundingClientRect();
expect(paneRect.bottom).toBeLessThan(window.innerHeight + 1);
body.removeChild(superLongElement);

// Restore body to pre-test state.
body.removeChild(element);
body.removeChild(superLongElement);
body.style.overflow = previousBodyOverflow;
});

it('should keep the calendar pane in the right place with body scrolling disabled', function() {
// Make the body super huge and scroll down a bunch.
var body = document.body;
var superLongElement = document.createElement('div');
superLongElement.style.height = '10000px';
superLongElement.style.width = '1px';
body.appendChild(superLongElement);
body.scrollTop = 700;

// Absolutely position the picker such that the pane position doesn't need to be adjusted.
// (1/10 of the way down the screen).
element.style.position = 'absolute';
element.style.top = (document.body.scrollTop + (window.innerHeight * 0.10)) + 'px';
element.style.left = '0';
body.appendChild(element);

// Open the pane.
element.querySelector('md-button').click();
$timeout.flush();

// Expect that the calendar pane is in the same position as the inline datepicker.
var paneRect = controller.calendarPane.getBoundingClientRect();
var triggerRect = controller.inputContainer.getBoundingClientRect();
expect(paneRect.top).toBe(triggerRect.top);

// Restore body to pre-test state.
body.removeChild(superLongElement);
body.removeChild(element);
});

it('should shink the calendar pane when it would otherwise not fit on the screen', function() {
// Fake the window being very narrow so that the calendar pane won't fit on-screen.
controller.$window = {innerWidth: 200, innherHeight: 800};
Expand Down