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