@@ -745,6 +745,111 @@ describe('MdSelect', () => {
745745
746746 } ) ;
747747
748+ describe ( 'when scrolled' , ( ) => {
749+
750+ // Need to set the scrollTop two different ways to support
751+ // both Chrome and Firefox.
752+ function setScrollTop ( num : number ) {
753+ document . body . scrollTop = num ;
754+ document . documentElement . scrollTop = num ;
755+ }
756+
757+ beforeEach ( ( ) => {
758+ // Make the div above the select very tall, so the page will scroll
759+ fixture . componentInstance . heightAbove = 2000 ;
760+
761+ // Give the select enough horizontal space to open
762+ select . style . marginLeft = '20px' ;
763+ select . style . marginRight = '20px' ;
764+ } ) ;
765+
766+ afterEach ( ( ) => {
767+ document . body . scrollTop = 0 ;
768+ document . documentElement . scrollTop = 0 ;
769+ } ) ;
770+
771+ it ( 'should align the first option properly when scrolled' , ( ) => {
772+ // Give the select enough space to open
773+ fixture . componentInstance . heightBelow = 400 ;
774+ fixture . detectChanges ( ) ;
775+
776+ // Scroll the select into view
777+ setScrollTop ( 1700 ) ;
778+
779+ trigger . click ( ) ;
780+ fixture . detectChanges ( ) ;
781+
782+ checkTriggerAlignedWithOption ( 0 ) ;
783+ } ) ;
784+
785+
786+ it ( 'should align a centered option properly when scrolled' , ( ) => {
787+ // Give the select enough space to open
788+ fixture . componentInstance . heightBelow = 400 ;
789+ fixture . detectChanges ( ) ;
790+
791+ fixture . componentInstance . control . setValue ( 'chips-4' ) ;
792+ fixture . detectChanges ( ) ;
793+
794+ // Scroll the select into view
795+ setScrollTop ( 1700 ) ;
796+
797+ trigger . click ( ) ;
798+ fixture . detectChanges ( ) ;
799+
800+ checkTriggerAlignedWithOption ( 4 ) ;
801+ } ) ;
802+
803+ it ( 'should fall back to "above" positioning properly when scrolled' , ( ) => {
804+ // Give the select insufficient space to open below the trigger
805+ fixture . componentInstance . heightBelow = 100 ;
806+ fixture . detectChanges ( ) ;
807+
808+ // Scroll the select into view
809+ setScrollTop ( 1400 ) ;
810+
811+ trigger . click ( ) ;
812+ fixture . detectChanges ( ) ;
813+
814+ // CSS styles aren't in the tests, so position must be absolute to reflect top/left
815+ const overlayPane = overlayContainerElement . children [ 0 ] as HTMLElement ;
816+ overlayPane . style . position = 'absolute' ;
817+
818+ const triggerBottom = trigger . getBoundingClientRect ( ) . bottom ;
819+ const overlayBottom = overlayPane . getBoundingClientRect ( ) . bottom ;
820+
821+ expect ( overlayBottom . toFixed ( 2 ) )
822+ . toEqual ( triggerBottom . toFixed ( 2 ) ,
823+ `Expected trigger bottom to align with overlay bottom.` ) ;
824+ } ) ;
825+
826+ it ( 'should fall back to "below" positioning properly when scrolled' , ( ) => {
827+ // Give plenty of space for the select to open below the trigger
828+ fixture . componentInstance . heightBelow = 650 ;
829+ fixture . detectChanges ( ) ;
830+
831+ // Select an option too low in the list to fit in limited space above
832+ fixture . componentInstance . control . setValue ( 'sushi-7' ) ;
833+ fixture . detectChanges ( ) ;
834+
835+ // Scroll the select so that it has insufficient space to open above the trigger
836+ setScrollTop ( 1950 ) ;
837+
838+ trigger . click ( ) ;
839+ fixture . detectChanges ( ) ;
840+
841+ // CSS styles aren't in the tests, so position must be absolute to reflect top/left
842+ const overlayPane = overlayContainerElement . children [ 0 ] as HTMLElement ;
843+ overlayPane . style . position = 'absolute' ;
844+
845+ const triggerTop = trigger . getBoundingClientRect ( ) . top ;
846+ const overlayTop = overlayPane . getBoundingClientRect ( ) . top ;
847+
848+ expect ( overlayTop . toFixed ( 2 ) )
849+ . toEqual ( triggerTop . toFixed ( 2 ) , `Expected trigger top to align with overlay top.` ) ;
850+ } ) ;
851+ } ) ;
852+
748853 describe ( 'x-axis positioning' , ( ) => {
749854
750855 beforeEach ( ( ) => {
@@ -1037,11 +1142,13 @@ describe('MdSelect', () => {
10371142@Component ( {
10381143 selector : 'basic-select' ,
10391144 template : `
1145+ <div [style.height.px]="heightAbove"></div>
10401146 <md-select placeholder="Food" [formControl]="control" [required]="isRequired">
10411147 <md-option *ngFor="let food of foods" [value]="food.value" [disabled]="food.disabled">
10421148 {{ food.viewValue }}
10431149 </md-option>
10441150 </md-select>
1151+ <div [style.height.px]="heightBelow"></div>
10451152 `
10461153} )
10471154class BasicSelect {
@@ -1057,6 +1164,8 @@ class BasicSelect {
10571164 ] ;
10581165 control = new FormControl ( ) ;
10591166 isRequired : boolean ;
1167+ heightAbove = 0 ;
1168+ heightBelow = 0 ;
10601169
10611170 @ViewChild ( MdSelect ) select : MdSelect ;
10621171 @ViewChildren ( MdOption ) options : QueryList < MdOption > ;
0 commit comments