@@ -1035,10 +1035,15 @@ describe('MdSelect', () => {
1035
1035
const options = overlayPane . querySelectorAll ( 'md-option' ) ;
1036
1036
const optionTop = options [ index ] . getBoundingClientRect ( ) . top ;
1037
1037
const triggerFontSize = parseInt ( window . getComputedStyle ( trigger ) [ 'font-size' ] ) ;
1038
+ const triggerLineHeightEm = 1.125 ;
1038
1039
1039
- /** TODO(mmalerba): not really sure where the "+1" is coming from here... */
1040
- expect ( Math . floor ( optionTop ) ) . toBe ( Math . floor ( triggerTop - triggerFontSize + 1 ) ,
1041
- `Expected trigger to align with option ${ index } .` ) ;
1040
+ // Extra trigger height beyond the font size caused by the fact that the line-height is
1041
+ // greater than 1em.
1042
+ const triggerExtraLineSpaceAbove = ( 1 - triggerLineHeightEm ) * triggerFontSize / 2 ;
1043
+
1044
+ expect ( Math . floor ( optionTop ) )
1045
+ . toBe ( Math . floor ( triggerTop - triggerFontSize - triggerExtraLineSpaceAbove ) ,
1046
+ `Expected trigger to align with option ${ index } .` ) ;
1042
1047
1043
1048
// For the animation to start at the option's center, its origin must be the distance
1044
1049
// from the top of the overlay to the option top + half the option height (48/2 = 24).
@@ -1165,12 +1170,44 @@ describe('MdSelect', () => {
1165
1170
formField . style . position = 'fixed' ;
1166
1171
formField . style . left = '20px' ;
1167
1172
} ) ;
1168
- /* TODO(mmalerba): The numbers in these tests are really hard to follow,
1169
- need to come up with good replacement tests, but I believe the actual logic works correctly now.
1173
+
1170
1174
it ( 'should adjust position of centered option if there is little space above' , async ( ( ) => {
1171
- // Push the select to a position with not quite enough space on the top to open
1172
- // with the option completely centered (needs 112px at least: 256/2 - (48 - 16)/2)
1173
- formField.style.top = '85px';
1175
+ const formField = fixture . debugElement . query ( By . css ( '.mat-form-field' ) ) . nativeElement ;
1176
+ const trigger = fixture . debugElement . query ( By . css ( '.mat-select-trigger' ) ) . nativeElement ;
1177
+
1178
+ const selectMenuHeight = 256 ;
1179
+ const selectMenuViewportPadding = 8 ;
1180
+ const selectItemHeight = 48 ;
1181
+ const selectedIndex = 4 ;
1182
+ const fontSize = 16 ;
1183
+ const lineHeightEm = 1.125 ;
1184
+ const expectedExtraScroll = 5 ;
1185
+
1186
+ // Trigger element height.
1187
+ const triggerHeight = fontSize * lineHeightEm ;
1188
+
1189
+ // Ideal space above selected item in order to center it.
1190
+ const idealSpaceAboveSelectedItem = ( selectMenuHeight - selectItemHeight ) / 2 ;
1191
+
1192
+ // Actual space above selected item.
1193
+ const actualSpaceAboveSelectedItem = selectItemHeight * selectedIndex ;
1194
+
1195
+ // Ideal scroll position to center.
1196
+ const idealScrollTop = actualSpaceAboveSelectedItem - idealSpaceAboveSelectedItem ;
1197
+
1198
+ // Top-most select-position that allows for perfect centering.
1199
+ const topMostPositionForPerfectCentering =
1200
+ idealSpaceAboveSelectedItem + selectMenuViewportPadding +
1201
+ ( selectItemHeight - triggerHeight ) / 2 ;
1202
+
1203
+ // Position of select relative to top edge of md-form-field.
1204
+ const formFieldTopSpace =
1205
+ trigger . getBoundingClientRect ( ) . top - formField . getBoundingClientRect ( ) . top ;
1206
+
1207
+ const formFieldTop =
1208
+ topMostPositionForPerfectCentering - formFieldTopSpace - expectedExtraScroll ;
1209
+
1210
+ formField . style . top = `${ formFieldTop } px` ;
1174
1211
1175
1212
// Select an option in the middle of the list
1176
1213
fixture . componentInstance . control . setValue ( 'chips-4' ) ;
@@ -1182,20 +1219,53 @@ need to come up with good replacement tests, but I believe the actual logic work
1182
1219
const scrollContainer = document . querySelector ( '.cdk-overlay-pane .mat-select-panel' ) ! ;
1183
1220
1184
1221
fixture . whenStable ( ) . then ( ( ) => {
1185
- // Scroll should adjust by the difference between the top space available (85px + 8px
1186
- // viewport padding = 77px) and the height of the panel above the option (112px).
1187
- // 112px - 93px = 20px difference + original scrollTop 88px = 108px
1188
1222
expect ( scrollContainer . scrollTop )
1189
- .toEqual(108, `Expected panel to adjust scroll position to fit in viewport.`);
1223
+ . toEqual ( idealScrollTop + 5 ,
1224
+ `Expected panel to adjust scroll position to fit in viewport.` ) ;
1190
1225
1191
1226
checkTriggerAlignedWithOption ( 4 ) ;
1192
1227
} ) ;
1193
1228
} ) ) ;
1194
1229
1195
1230
it ( 'should adjust position of centered option if there is little space below' , async ( ( ) => {
1231
+ const formField = fixture . debugElement . query ( By . css ( '.mat-form-field' ) ) . nativeElement ;
1232
+ const trigger = fixture . debugElement . query ( By . css ( '.mat-select-trigger' ) ) . nativeElement ;
1233
+
1234
+ const selectMenuHeight = 256 ;
1235
+ const selectMenuViewportPadding = 8 ;
1236
+ const selectItemHeight = 48 ;
1237
+ const selectedIndex = 4 ;
1238
+ const fontSize = 16 ;
1239
+ const lineHeightEm = 1.125 ;
1240
+ const expectedExtraScroll = 5 ;
1241
+
1242
+ // Trigger element height.
1243
+ const triggerHeight = fontSize * lineHeightEm ;
1244
+
1245
+ // Ideal space above selected item in order to center it.
1246
+ const idealSpaceAboveSelectedItem = ( selectMenuHeight - selectItemHeight ) / 2 ;
1247
+
1248
+ // Actual space above selected item.
1249
+ const actualSpaceAboveSelectedItem = selectItemHeight * selectedIndex ;
1250
+
1251
+ // Ideal scroll position to center.
1252
+ const idealScrollTop = actualSpaceAboveSelectedItem - idealSpaceAboveSelectedItem ;
1253
+
1254
+ // Bottom-most select-position that allows for perfect centering.
1255
+ const bottomMostPositionForPerfectCentering =
1256
+ idealSpaceAboveSelectedItem + selectMenuViewportPadding +
1257
+ ( selectItemHeight - triggerHeight ) / 2 ;
1258
+
1259
+ // Position of select relative to bottom edge of md-form-field:
1260
+ const formFieldBottomSpace =
1261
+ formField . getBoundingClientRect ( ) . bottom - trigger . getBoundingClientRect ( ) . bottom ;
1262
+
1263
+ const formFieldBottom =
1264
+ bottomMostPositionForPerfectCentering - formFieldBottomSpace - expectedExtraScroll ;
1265
+
1196
1266
// Push the select to a position with not quite enough space on the bottom to open
1197
1267
// with the option completely centered (needs 113px at least: 256/2 - 48/2 + 9)
1198
- formField.style.bottom = '56px' ;
1268
+ formField . style . bottom = ` ${ formFieldBottom } px` ;
1199
1269
1200
1270
// Select an option in the middle of the list
1201
1271
fixture . componentInstance . control . setValue ( 'chips-4' ) ;
@@ -1214,14 +1284,15 @@ need to come up with good replacement tests, but I believe the actual logic work
1214
1284
// (56px from the bottom of the screen - 8px padding = 48px)
1215
1285
// and the height of the panel below the option (113px).
1216
1286
// 113px - 48px = 75px difference. Original scrollTop 88px - 75px = 23px
1217
- expect(Math.ceil(scrollContainer.scrollTop))
1218
- .toEqual(23, `Expected panel to adjust scroll position to fit in viewport.`);
1287
+ expect ( scrollContainer . scrollTop )
1288
+ . toEqual ( idealScrollTop - expectedExtraScroll ,
1289
+ `Expected panel to adjust scroll position to fit in viewport.` ) ;
1219
1290
1220
1291
checkTriggerAlignedWithOption ( 4 ) ;
1221
1292
} ) ;
1222
1293
} ) ;
1223
1294
} ) ) ;
1224
- */
1295
+
1225
1296
it ( 'should fall back to "above" positioning if scroll adjustment will not help' , ( ) => {
1226
1297
// Push the select to a position with not enough space on the bottom to open
1227
1298
formField . style . bottom = '56px' ;
@@ -1636,9 +1707,16 @@ need to come up with good replacement tests, but I believe the actual logic work
1636
1707
// 16px is the default option padding
1637
1708
expect ( Math . floor ( selectedOptionLeft ) ) . toEqual ( Math . floor ( triggerLeft - 16 ) ) ;
1638
1709
} ) ) ;
1639
- /* TODO(mmalerba): Again, pretty sure this is right, just need to write a test that I can
1640
- * understand.
1710
+
1641
1711
it ( 'should align the first option to the trigger, if nothing is selected' , async ( ( ) => {
1712
+ // Push down the form field so there is space for the item to completely align.
1713
+ formField . style . top = '100px' ;
1714
+
1715
+ const menuItemHeight = 48 ;
1716
+ const triggerFontSize = 16 ;
1717
+ const triggerLineHeightEm = 1.125 ;
1718
+ const triggerHeight = triggerFontSize * triggerLineHeightEm ;
1719
+
1642
1720
trigger . click ( ) ;
1643
1721
groupFixture . detectChanges ( ) ;
1644
1722
@@ -1648,16 +1726,14 @@ need to come up with good replacement tests, but I believe the actual logic work
1648
1726
const option = overlayContainerElement . querySelector ( '.cdk-overlay-pane md-option' ) ;
1649
1727
const optionTop = option ? option . getBoundingClientRect ( ) . top : 0 ;
1650
1728
1651
- expect(Math.floor( optionTop) )
1652
- .toBe(Math.floor( triggerTop) , 'Expected trigger to align with the first option.');
1729
+ expect ( optionTop + ( menuItemHeight - triggerHeight ) / 2 )
1730
+ . toBe ( triggerTop , 'Expected trigger to align with the first option.' ) ;
1653
1731
} ) ;
1654
1732
} ) ) ;
1655
- */
1656
1733
} ) ;
1657
1734
} ) ;
1658
1735
1659
1736
describe ( 'accessibility' , ( ) => {
1660
-
1661
1737
describe ( 'for select' , ( ) => {
1662
1738
let fixture : ComponentFixture < BasicSelect > ;
1663
1739
let select : HTMLElement ;
@@ -2160,7 +2236,7 @@ need to come up with good replacement tests, but I believe the actual logic work
2160
2236
TestBed . createComponent ( SelectEarlyAccessSibling ) . detectChanges ( ) ;
2161
2237
} ) . not . toThrow ( ) ;
2162
2238
} ) ) ;
2163
- /* TODO(mmalerba): no idea whats up with this
2239
+
2164
2240
it ( 'should not throw selection model-related errors in addition to the errors from ngModel' ,
2165
2241
async ( ( ) => {
2166
2242
const fixture = TestBed . createComponent ( InvalidSelectInForm ) ;
@@ -2171,7 +2247,6 @@ need to come up with good replacement tests, but I believe the actual logic work
2171
2247
// The second run shouldn't throw selection-model related errors.
2172
2248
expect ( ( ) => fixture . detectChanges ( ) ) . not . toThrow ( ) ;
2173
2249
} ) ) ;
2174
- */
2175
2250
} ) ;
2176
2251
2177
2252
describe ( 'change event' , ( ) => {
@@ -2351,8 +2426,6 @@ need to come up with good replacement tests, but I believe the actual logic work
2351
2426
expect ( testInstance . control . value ) . toEqual ( [ ] ) ;
2352
2427
} ) ;
2353
2428
2354
- /* TODO(mmalerba): not sure why this fails... It doesn't register the update when option 2 is
2355
- deselected, but it works fine in the demo app.
2356
2429
it ( 'should update the label' , async ( ( ) => {
2357
2430
trigger . click ( ) ;
2358
2431
fixture . detectChanges ( ) ;
@@ -2375,7 +2448,7 @@ need to come up with good replacement tests, but I believe the actual logic work
2375
2448
expect ( trigger . textContent ) . toContain ( 'Steak, Eggs' ) ;
2376
2449
} ) ;
2377
2450
} ) ;
2378
- }));*/
2451
+ } ) ) ;
2379
2452
2380
2453
it ( 'should be able to set the selected value by taking an array' , ( ) => {
2381
2454
trigger . click ( ) ;
0 commit comments