@@ -1027,10 +1027,15 @@ describe('MdSelect', () => {
1027
1027
const options = overlayPane . querySelectorAll ( 'md-option' ) ;
1028
1028
const optionTop = options [ index ] . getBoundingClientRect ( ) . top ;
1029
1029
const triggerFontSize = parseInt ( window . getComputedStyle ( trigger ) [ 'font-size' ] ) ;
1030
+ const triggerLineHeightEm = 1.125 ;
1030
1031
1031
- /** TODO(mmalerba): not really sure where the "+1" is coming from here... */
1032
- expect ( Math . floor ( optionTop ) ) . toBe ( Math . floor ( triggerTop - triggerFontSize + 1 ) ,
1033
- `Expected trigger to align with option ${ index } .` ) ;
1032
+ // Extra trigger height beyond the font size caused by the fact that the line-height is
1033
+ // greater than 1em.
1034
+ const triggerExtraLineSpaceAbove = ( 1 - triggerLineHeightEm ) * triggerFontSize / 2 ;
1035
+
1036
+ expect ( Math . floor ( optionTop ) )
1037
+ . toBe ( Math . floor ( triggerTop - triggerFontSize - triggerExtraLineSpaceAbove ) ,
1038
+ `Expected trigger to align with option ${ index } .` ) ;
1034
1039
1035
1040
// For the animation to start at the option's center, its origin must be the distance
1036
1041
// from the top of the overlay to the option top + half the option height (48/2 = 24).
@@ -1157,12 +1162,44 @@ describe('MdSelect', () => {
1157
1162
formField . style . position = 'fixed' ;
1158
1163
formField . style . left = '20px' ;
1159
1164
} ) ;
1160
- /* TODO(mmalerba): The numbers in these tests are really hard to follow,
1161
- need to come up with good replacement tests, but I believe the actual logic works correctly now.
1165
+
1162
1166
it ( 'should adjust position of centered option if there is little space above' , async ( ( ) => {
1163
- // Push the select to a position with not quite enough space on the top to open
1164
- // with the option completely centered (needs 112px at least: 256/2 - (48 - 16)/2)
1165
- formField.style.top = '85px';
1167
+ const formField = fixture . debugElement . query ( By . css ( '.mat-form-field' ) ) . nativeElement ;
1168
+ const trigger = fixture . debugElement . query ( By . css ( '.mat-select-trigger' ) ) . nativeElement ;
1169
+
1170
+ const selectMenuHeight = 256 ;
1171
+ const selectMenuViewportPadding = 8 ;
1172
+ const selectItemHeight = 48 ;
1173
+ const selectedIndex = 4 ;
1174
+ const fontSize = 16 ;
1175
+ const lineHeightEm = 1.125 ;
1176
+ const expectedExtraScroll = 5 ;
1177
+
1178
+ // Trigger element height.
1179
+ const triggerHeight = fontSize * lineHeightEm ;
1180
+
1181
+ // Ideal space above selected item in order to center it.
1182
+ const idealSpaceAboveSelectedItem = ( selectMenuHeight - selectItemHeight ) / 2 ;
1183
+
1184
+ // Actual space above selected item.
1185
+ const actualSpaceAboveSelectedItem = selectItemHeight * selectedIndex ;
1186
+
1187
+ // Ideal scroll position to center.
1188
+ const idealScrollTop = actualSpaceAboveSelectedItem - idealSpaceAboveSelectedItem ;
1189
+
1190
+ // Top-most select-position that allows for perfect centering.
1191
+ const topMostPositionForPerfectCentering =
1192
+ idealSpaceAboveSelectedItem + selectMenuViewportPadding +
1193
+ ( selectItemHeight - triggerHeight ) / 2 ;
1194
+
1195
+ // Position of select relative to top edge of md-form-field.
1196
+ const formFieldTopSpace =
1197
+ trigger . getBoundingClientRect ( ) . top - formField . getBoundingClientRect ( ) . top ;
1198
+
1199
+ const formFieldTop =
1200
+ topMostPositionForPerfectCentering - formFieldTopSpace - expectedExtraScroll ;
1201
+
1202
+ formField . style . top = `${ formFieldTop } px` ;
1166
1203
1167
1204
// Select an option in the middle of the list
1168
1205
fixture . componentInstance . control . setValue ( 'chips-4' ) ;
@@ -1174,20 +1211,53 @@ need to come up with good replacement tests, but I believe the actual logic work
1174
1211
const scrollContainer = document . querySelector ( '.cdk-overlay-pane .mat-select-panel' ) ! ;
1175
1212
1176
1213
fixture . whenStable ( ) . then ( ( ) => {
1177
- // Scroll should adjust by the difference between the top space available (85px + 8px
1178
- // viewport padding = 77px) and the height of the panel above the option (112px).
1179
- // 112px - 93px = 20px difference + original scrollTop 88px = 108px
1180
1214
expect ( scrollContainer . scrollTop )
1181
- .toEqual(108, `Expected panel to adjust scroll position to fit in viewport.`);
1215
+ . toEqual ( idealScrollTop + 5 ,
1216
+ `Expected panel to adjust scroll position to fit in viewport.` ) ;
1182
1217
1183
1218
checkTriggerAlignedWithOption ( 4 ) ;
1184
1219
} ) ;
1185
1220
} ) ) ;
1186
1221
1187
1222
it ( 'should adjust position of centered option if there is little space below' , async ( ( ) => {
1223
+ const formField = fixture . debugElement . query ( By . css ( '.mat-form-field' ) ) . nativeElement ;
1224
+ const trigger = fixture . debugElement . query ( By . css ( '.mat-select-trigger' ) ) . nativeElement ;
1225
+
1226
+ const selectMenuHeight = 256 ;
1227
+ const selectMenuViewportPadding = 8 ;
1228
+ const selectItemHeight = 48 ;
1229
+ const selectedIndex = 4 ;
1230
+ const fontSize = 16 ;
1231
+ const lineHeightEm = 1.125 ;
1232
+ const expectedExtraScroll = 5 ;
1233
+
1234
+ // Trigger element height.
1235
+ const triggerHeight = fontSize * lineHeightEm ;
1236
+
1237
+ // Ideal space above selected item in order to center it.
1238
+ const idealSpaceAboveSelectedItem = ( selectMenuHeight - selectItemHeight ) / 2 ;
1239
+
1240
+ // Actual space above selected item.
1241
+ const actualSpaceAboveSelectedItem = selectItemHeight * selectedIndex ;
1242
+
1243
+ // Ideal scroll position to center.
1244
+ const idealScrollTop = actualSpaceAboveSelectedItem - idealSpaceAboveSelectedItem ;
1245
+
1246
+ // Bottom-most select-position that allows for perfect centering.
1247
+ const bottomMostPositionForPerfectCentering =
1248
+ idealSpaceAboveSelectedItem + selectMenuViewportPadding +
1249
+ ( selectItemHeight - triggerHeight ) / 2 ;
1250
+
1251
+ // Position of select relative to bottom edge of md-form-field:
1252
+ const formFieldBottomSpace =
1253
+ formField . getBoundingClientRect ( ) . bottom - trigger . getBoundingClientRect ( ) . bottom ;
1254
+
1255
+ const formFieldBottom =
1256
+ bottomMostPositionForPerfectCentering - formFieldBottomSpace - expectedExtraScroll ;
1257
+
1188
1258
// Push the select to a position with not quite enough space on the bottom to open
1189
1259
// with the option completely centered (needs 113px at least: 256/2 - 48/2 + 9)
1190
- formField.style.bottom = '56px' ;
1260
+ formField . style . bottom = ` ${ formFieldBottom } px` ;
1191
1261
1192
1262
// Select an option in the middle of the list
1193
1263
fixture . componentInstance . control . setValue ( 'chips-4' ) ;
@@ -1206,14 +1276,15 @@ need to come up with good replacement tests, but I believe the actual logic work
1206
1276
// (56px from the bottom of the screen - 8px padding = 48px)
1207
1277
// and the height of the panel below the option (113px).
1208
1278
// 113px - 48px = 75px difference. Original scrollTop 88px - 75px = 23px
1209
- expect(Math.ceil(scrollContainer.scrollTop))
1210
- .toEqual(23, `Expected panel to adjust scroll position to fit in viewport.`);
1279
+ expect ( scrollContainer . scrollTop )
1280
+ . toEqual ( idealScrollTop - expectedExtraScroll ,
1281
+ `Expected panel to adjust scroll position to fit in viewport.` ) ;
1211
1282
1212
1283
checkTriggerAlignedWithOption ( 4 ) ;
1213
1284
} ) ;
1214
1285
} ) ;
1215
1286
} ) ) ;
1216
- */
1287
+
1217
1288
it ( 'should fall back to "above" positioning if scroll adjustment will not help' , ( ) => {
1218
1289
// Push the select to a position with not enough space on the bottom to open
1219
1290
formField . style . bottom = '56px' ;
@@ -1628,9 +1699,16 @@ need to come up with good replacement tests, but I believe the actual logic work
1628
1699
// 16px is the default option padding
1629
1700
expect ( Math . floor ( selectedOptionLeft ) ) . toEqual ( Math . floor ( triggerLeft - 16 ) ) ;
1630
1701
} ) ) ;
1631
- /* TODO(mmalerba): Again, pretty sure this is right, just need to write a test that I can
1632
- * understand.
1702
+
1633
1703
it ( 'should align the first option to the trigger, if nothing is selected' , async ( ( ) => {
1704
+ // Push down the form field so there is space for the item to completely align.
1705
+ formField . style . top = '100px' ;
1706
+
1707
+ const menuItemHeight = 48 ;
1708
+ const triggerFontSize = 16 ;
1709
+ const triggerLineHeightEm = 1.125 ;
1710
+ const triggerHeight = triggerFontSize * triggerLineHeightEm ;
1711
+
1634
1712
trigger . click ( ) ;
1635
1713
groupFixture . detectChanges ( ) ;
1636
1714
@@ -1640,16 +1718,14 @@ need to come up with good replacement tests, but I believe the actual logic work
1640
1718
const option = overlayContainerElement . querySelector ( '.cdk-overlay-pane md-option' ) ;
1641
1719
const optionTop = option ? option . getBoundingClientRect ( ) . top : 0 ;
1642
1720
1643
- expect(Math.floor( optionTop) )
1644
- .toBe(Math.floor( triggerTop) , 'Expected trigger to align with the first option.');
1721
+ expect ( optionTop + ( menuItemHeight - triggerHeight ) / 2 )
1722
+ . toBe ( triggerTop , 'Expected trigger to align with the first option.' ) ;
1645
1723
} ) ;
1646
1724
} ) ) ;
1647
- */
1648
1725
} ) ;
1649
1726
} ) ;
1650
1727
1651
1728
describe ( 'accessibility' , ( ) => {
1652
-
1653
1729
describe ( 'for select' , ( ) => {
1654
1730
let fixture : ComponentFixture < BasicSelect > ;
1655
1731
let select : HTMLElement ;
@@ -2152,7 +2228,7 @@ need to come up with good replacement tests, but I believe the actual logic work
2152
2228
TestBed . createComponent ( SelectEarlyAccessSibling ) . detectChanges ( ) ;
2153
2229
} ) . not . toThrow ( ) ;
2154
2230
} ) ) ;
2155
- /* TODO(mmalerba): no idea whats up with this
2231
+
2156
2232
it ( 'should not throw selection model-related errors in addition to the errors from ngModel' ,
2157
2233
async ( ( ) => {
2158
2234
const fixture = TestBed . createComponent ( InvalidSelectInForm ) ;
@@ -2163,7 +2239,6 @@ need to come up with good replacement tests, but I believe the actual logic work
2163
2239
// The second run shouldn't throw selection-model related errors.
2164
2240
expect ( ( ) => fixture . detectChanges ( ) ) . not . toThrow ( ) ;
2165
2241
} ) ) ;
2166
- */
2167
2242
} ) ;
2168
2243
2169
2244
describe ( 'change event' , ( ) => {
@@ -2343,8 +2418,6 @@ need to come up with good replacement tests, but I believe the actual logic work
2343
2418
expect ( testInstance . control . value ) . toEqual ( [ ] ) ;
2344
2419
} ) ;
2345
2420
2346
- /* TODO(mmalerba): not sure why this fails... It doesn't register the update when option 2 is
2347
- deselected, but it works fine in the demo app.
2348
2421
it ( 'should update the label' , async ( ( ) => {
2349
2422
trigger . click ( ) ;
2350
2423
fixture . detectChanges ( ) ;
@@ -2367,7 +2440,7 @@ need to come up with good replacement tests, but I believe the actual logic work
2367
2440
expect ( trigger . textContent ) . toContain ( 'Steak, Eggs' ) ;
2368
2441
} ) ;
2369
2442
} ) ;
2370
- }));*/
2443
+ } ) ) ;
2371
2444
2372
2445
it ( 'should be able to set the selected value by taking an array' , ( ) => {
2373
2446
trigger . click ( ) ;
0 commit comments