@@ -27,10 +27,23 @@ import {TAB} from '../core/keyboard/keycodes';
27
27
import { ScrollDispatcher } from '../core/overlay/scroll/scroll-dispatcher' ;
28
28
29
29
30
+ class FakeViewportRuler {
31
+ getViewportRect ( ) {
32
+ return {
33
+ left : 0 , top : 0 , width : 1014 , height : 686 , bottom : 686 , right : 1014
34
+ } ;
35
+ }
36
+
37
+ getViewportScrollPosition ( ) {
38
+ return { top : 0 , left : 0 } ;
39
+ }
40
+ }
41
+
30
42
describe ( 'MdSelect' , ( ) => {
31
43
let overlayContainerElement : HTMLElement ;
32
44
let dir : { value : 'ltr' | 'rtl' } ;
33
45
let scrolledSubject = new Subject ( ) ;
46
+ let fakeViewportRuler = new FakeViewportRuler ( ) ;
34
47
35
48
beforeEach ( async ( ( ) => {
36
49
TestBed . configureTestingModule ( {
@@ -69,10 +82,10 @@ describe('MdSelect', () => {
69
82
70
83
return { getContainerElement : ( ) => overlayContainerElement } ;
71
84
} } ,
85
+ { provide : ViewportRuler , useValue : fakeViewportRuler } ,
72
86
{ provide : Dir , useFactory : ( ) => {
73
87
return dir = { value : 'ltr' } ;
74
88
} } ,
75
- { provide : ViewportRuler , useClass : FakeViewportRuler } ,
76
89
{ provide : ScrollDispatcher , useFactory : ( ) => {
77
90
return { scrolled : ( delay : number , callback : ( ) => any ) => {
78
91
return scrolledSubject . asObservable ( ) . subscribe ( callback ) ;
@@ -942,6 +955,91 @@ describe('MdSelect', () => {
942
955
943
956
} ) ;
944
957
958
+ describe ( 'limited space to open horizontally' , ( ) => {
959
+ beforeEach ( ( ) => {
960
+ select . style . position = 'absolute' ;
961
+ select . style . top = '200px' ;
962
+ } ) ;
963
+
964
+ it ( 'should stay within the viewport when overflowing on the left in ltr' , fakeAsync ( ( ) => {
965
+ select . style . left = '-100px' ;
966
+ trigger . click ( ) ;
967
+ tick ( 400 ) ;
968
+ fixture . detectChanges ( ) ;
969
+
970
+ const panelLeft = document . querySelector ( '.mat-select-panel' )
971
+ . getBoundingClientRect ( ) . left ;
972
+ expect ( panelLeft ) . toBeGreaterThan ( 0 ,
973
+ `Expected select panel to be inside the viewport in ltr.` ) ;
974
+ } ) ) ;
975
+
976
+ it ( 'should stay within the viewport when overflowing on the left in rtl' , fakeAsync ( ( ) => {
977
+ dir . value = 'rtl' ;
978
+ select . style . left = '-100px' ;
979
+ trigger . click ( ) ;
980
+ tick ( 400 ) ;
981
+ fixture . detectChanges ( ) ;
982
+
983
+ const panelLeft = document . querySelector ( '.mat-select-panel' )
984
+ . getBoundingClientRect ( ) . left ;
985
+
986
+ expect ( panelLeft ) . toBeGreaterThan ( 0 ,
987
+ `Expected select panel to be inside the viewport in rtl.` ) ;
988
+ } ) ) ;
989
+
990
+ it ( 'should stay within the viewport when overflowing on the right in ltr' , fakeAsync ( ( ) => {
991
+ select . style . right = '-100px' ;
992
+ trigger . click ( ) ;
993
+ tick ( 400 ) ;
994
+ fixture . detectChanges ( ) ;
995
+
996
+ const viewportRect = fakeViewportRuler . getViewportRect ( ) . right ;
997
+ const panelRight = document . querySelector ( '.mat-select-panel' )
998
+ . getBoundingClientRect ( ) . right ;
999
+
1000
+ expect ( viewportRect - panelRight ) . toBeGreaterThan ( 0 ,
1001
+ `Expected select panel to be inside the viewport in ltr.` ) ;
1002
+ } ) ) ;
1003
+
1004
+ it ( 'should stay within the viewport when overflowing on the right in rtl' , fakeAsync ( ( ) => {
1005
+ dir . value = 'rtl' ;
1006
+ select . style . right = '-100px' ;
1007
+ trigger . click ( ) ;
1008
+ tick ( 400 ) ;
1009
+ fixture . detectChanges ( ) ;
1010
+
1011
+ const viewportRect = fakeViewportRuler . getViewportRect ( ) . right ;
1012
+ const panelRight = document . querySelector ( '.mat-select-panel' )
1013
+ . getBoundingClientRect ( ) . right ;
1014
+
1015
+ expect ( viewportRect - panelRight ) . toBeGreaterThan ( 0 ,
1016
+ `Expected select panel to be inside the viewport in rtl.` ) ;
1017
+ } ) ) ;
1018
+
1019
+ it ( 'should keep the position within the viewport on repeat openings' , async ( ( ) => {
1020
+ select . style . left = '-100px' ;
1021
+ trigger . click ( ) ;
1022
+ fixture . detectChanges ( ) ;
1023
+
1024
+ let panelLeft = document . querySelector ( '.mat-select-panel' ) . getBoundingClientRect ( ) . left ;
1025
+
1026
+ expect ( panelLeft ) . toBeGreaterThan ( 0 , `Expected select panel to be inside the viewport.` ) ;
1027
+
1028
+ fixture . componentInstance . select . close ( ) ;
1029
+ fixture . detectChanges ( ) ;
1030
+
1031
+ fixture . whenStable ( ) . then ( ( ) => {
1032
+ trigger . click ( ) ;
1033
+ fixture . detectChanges ( ) ;
1034
+ panelLeft = document . querySelector ( '.mat-select-panel' ) . getBoundingClientRect ( ) . left ;
1035
+
1036
+ expect ( panelLeft ) . toBeGreaterThan ( 0 ,
1037
+ `Expected select panel continue being inside the viewport.` ) ;
1038
+ } ) ;
1039
+ } ) ) ;
1040
+
1041
+ } ) ;
1042
+
945
1043
describe ( 'when scrolled' , ( ) => {
946
1044
947
1045
// Need to set the scrollTop two different ways to support
@@ -1063,42 +1161,38 @@ describe('MdSelect', () => {
1063
1161
select . style . marginRight = '30px' ;
1064
1162
} ) ;
1065
1163
1066
- it ( 'should align the trigger and the selected option on the x-axis in ltr' , async ( ( ) => {
1164
+ it ( 'should align the trigger and the selected option on the x-axis in ltr' , fakeAsync ( ( ) => {
1067
1165
trigger . click ( ) ;
1166
+ tick ( 400 ) ;
1068
1167
fixture . detectChanges ( ) ;
1069
1168
1070
- fixture . whenStable ( ) . then ( ( ) => {
1071
- const triggerLeft = trigger . getBoundingClientRect ( ) . left ;
1072
- const firstOptionLeft = document . querySelector ( '.cdk-overlay-pane md-option' )
1073
- . getBoundingClientRect ( ) . left ;
1169
+ const triggerLeft = trigger . getBoundingClientRect ( ) . left ;
1170
+ const firstOptionLeft = document . querySelector ( '.cdk-overlay-pane md-option' )
1171
+ . getBoundingClientRect ( ) . left ;
1074
1172
1075
- // Each option is 32px wider than the trigger, so it must be adjusted 16px
1076
- // to ensure the text overlaps correctly.
1077
- expect ( firstOptionLeft . toFixed ( 2 ) ) . toEqual ( ( triggerLeft - 16 ) . toFixed ( 2 ) ,
1078
- `Expected trigger to align with the selected option on the x-axis in LTR.` ) ;
1079
- } ) ;
1173
+ // Each option is 32px wider than the trigger, so it must be adjusted 16px
1174
+ // to ensure the text overlaps correctly.
1175
+ expect ( firstOptionLeft . toFixed ( 2 ) ) . toEqual ( ( triggerLeft - 16 ) . toFixed ( 2 ) ,
1176
+ `Expected trigger to align with the selected option on the x-axis in LTR.` ) ;
1080
1177
} ) ) ;
1081
1178
1082
- it ( 'should align the trigger and the selected option on the x-axis in rtl' , async ( ( ) => {
1179
+ it ( 'should align the trigger and the selected option on the x-axis in rtl' , fakeAsync ( ( ) => {
1083
1180
dir . value = 'rtl' ;
1084
- fixture . whenStable ( ) . then ( ( ) => {
1085
- fixture . detectChanges ( ) ;
1181
+ fixture . detectChanges ( ) ;
1086
1182
1087
- trigger . click ( ) ;
1088
- fixture . detectChanges ( ) ;
1183
+ trigger . click ( ) ;
1184
+ tick ( 400 ) ;
1185
+ fixture . detectChanges ( ) ;
1089
1186
1090
- fixture . whenStable ( ) . then ( ( ) => {
1091
- const triggerRight = trigger . getBoundingClientRect ( ) . right ;
1092
- const firstOptionRight =
1093
- document . querySelector ( '.cdk-overlay-pane md-option' ) . getBoundingClientRect ( ) . right ;
1094
-
1095
- // Each option is 32px wider than the trigger, so it must be adjusted 16px
1096
- // to ensure the text overlaps correctly.
1097
- expect ( firstOptionRight . toFixed ( 2 ) )
1098
- . toEqual ( ( triggerRight + 16 ) . toFixed ( 2 ) ,
1099
- `Expected trigger to align with the selected option on the x-axis in RTL.` ) ;
1100
- } ) ;
1101
- } ) ;
1187
+ const triggerRight = trigger . getBoundingClientRect ( ) . right ;
1188
+ const firstOptionRight =
1189
+ document . querySelector ( '.cdk-overlay-pane md-option' ) . getBoundingClientRect ( ) . right ;
1190
+
1191
+ // Each option is 32px wider than the trigger, so it must be adjusted 16px
1192
+ // to ensure the text overlaps correctly.
1193
+ expect ( firstOptionRight . toFixed ( 2 ) )
1194
+ . toEqual ( ( triggerRight + 16 ) . toFixed ( 2 ) ,
1195
+ `Expected trigger to align with the selected option on the x-axis in RTL.` ) ;
1102
1196
} ) ) ;
1103
1197
} ) ;
1104
1198
@@ -1111,8 +1205,8 @@ describe('MdSelect', () => {
1111
1205
trigger = multiFixture . debugElement . query ( By . css ( '.mat-select-trigger' ) ) . nativeElement ;
1112
1206
select = multiFixture . debugElement . query ( By . css ( 'md-select' ) ) . nativeElement ;
1113
1207
1114
- select . style . marginLeft = '20px ' ;
1115
- select . style . marginRight = '20px ' ;
1208
+ select . style . marginLeft = '60px ' ;
1209
+ select . style . marginRight = '60px ' ;
1116
1210
} ) ;
1117
1211
1118
1212
it ( 'should adjust for the checkbox in ltr' , async ( ( ) => {
@@ -1131,21 +1225,20 @@ describe('MdSelect', () => {
1131
1225
} ) ;
1132
1226
} ) ) ;
1133
1227
1134
- it ( 'should adjust for the checkbox in rtl' , async ( ( ) => {
1228
+ it ( 'should adjust for the checkbox in rtl' , fakeAsync ( ( ) => {
1135
1229
dir . value = 'rtl' ;
1136
1230
trigger . click ( ) ;
1231
+ tick ( 400 ) ;
1137
1232
multiFixture . detectChanges ( ) ;
1138
1233
1139
- multiFixture . whenStable ( ) . then ( ( ) => {
1140
- const triggerRight = trigger . getBoundingClientRect ( ) . right ;
1141
- const firstOptionRight =
1142
- document . querySelector ( '.cdk-overlay-pane md-option' ) . getBoundingClientRect ( ) . right ;
1234
+ const triggerRight = trigger . getBoundingClientRect ( ) . right ;
1235
+ const firstOptionRight =
1236
+ document . querySelector ( '.cdk-overlay-pane md-option' ) . getBoundingClientRect ( ) . right ;
1143
1237
1144
- // 48px accounts for the checkbox size, margin and the panel's padding.
1145
- expect ( firstOptionRight . toFixed ( 2 ) )
1146
- . toEqual ( ( triggerRight + 48 ) . toFixed ( 2 ) ,
1147
- `Expected trigger label to align along x-axis, accounting for the checkbox.` ) ;
1148
- } ) ;
1238
+ // 48px accounts for the checkbox size, margin and the panel's padding.
1239
+ expect ( firstOptionRight . toFixed ( 2 ) )
1240
+ . toEqual ( ( triggerRight + 48 ) . toFixed ( 2 ) ,
1241
+ `Expected trigger label to align along x-axis, accounting for the checkbox.` ) ;
1149
1242
} ) ) ;
1150
1243
} ) ;
1151
1244
@@ -2126,15 +2219,3 @@ class BasicSelectWithTheming {
2126
2219
@ViewChild ( MdSelect ) select : MdSelect ;
2127
2220
theme : string ;
2128
2221
}
2129
-
2130
- class FakeViewportRuler {
2131
- getViewportRect ( ) {
2132
- return {
2133
- left : 0 , top : 0 , width : 1014 , height : 686 , bottom : 686 , right : 1014
2134
- } ;
2135
- }
2136
-
2137
- getViewportScrollPosition ( ) {
2138
- return { top : 0 , left : 0 } ;
2139
- }
2140
- }
0 commit comments