Skip to content

Commit

Permalink
fix(datepicker): prevent calendar pane being in wrong position after
Browse files Browse the repository at this point in the history
body scrolling is disabled. Fixes angular#5201
  • Loading branch information
jelbourn committed Nov 25, 2015
1 parent b8ffdfe commit 985f03c
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 7 deletions.
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/4 of the way down the screen).
element.style.position = 'absolute';
element.style.top = (document.body.scrollTop + (window.innerHeight * 0.25)) + '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

0 comments on commit 985f03c

Please sign in to comment.