@@ -1035,10 +1035,15 @@ describe('MdSelect', () => {
10351035 const options = overlayPane . querySelectorAll ( 'md-option' ) ;
10361036 const optionTop = options [ index ] . getBoundingClientRect ( ) . top ;
10371037 const triggerFontSize = parseInt ( window . getComputedStyle ( trigger ) [ 'font-size' ] ) ;
1038+ const triggerLineHeightEm = 1.125 ;
10381039
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 } .` ) ;
10421047
10431048 // For the animation to start at the option's center, its origin must be the distance
10441049 // from the top of the overlay to the option top + half the option height (48/2 = 24).
@@ -1165,12 +1170,44 @@ describe('MdSelect', () => {
11651170 formField . style . position = 'fixed' ;
11661171 formField . style . left = '20px' ;
11671172 } ) ;
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+
11701174 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` ;
11741211
11751212 // Select an option in the middle of the list
11761213 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
11821219 const scrollContainer = document . querySelector ( '.cdk-overlay-pane .mat-select-panel' ) ! ;
11831220
11841221 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
11881222 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.` ) ;
11901225
11911226 checkTriggerAlignedWithOption ( 4 ) ;
11921227 } ) ;
11931228 } ) ) ;
11941229
11951230 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+
11961266 // Push the select to a position with not quite enough space on the bottom to open
11971267 // 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` ;
11991269
12001270 // Select an option in the middle of the list
12011271 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
12141284 // (56px from the bottom of the screen - 8px padding = 48px)
12151285 // and the height of the panel below the option (113px).
12161286 // 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.` ) ;
12191290
12201291 checkTriggerAlignedWithOption ( 4 ) ;
12211292 } ) ;
12221293 } ) ;
12231294 } ) ) ;
1224- */
1295+
12251296 it ( 'should fall back to "above" positioning if scroll adjustment will not help' , ( ) => {
12261297 // Push the select to a position with not enough space on the bottom to open
12271298 formField . style . bottom = '56px' ;
@@ -1636,9 +1707,16 @@ need to come up with good replacement tests, but I believe the actual logic work
16361707 // 16px is the default option padding
16371708 expect ( Math . floor ( selectedOptionLeft ) ) . toEqual ( Math . floor ( triggerLeft - 16 ) ) ;
16381709 } ) ) ;
1639- /* TODO(mmalerba): Again, pretty sure this is right, just need to write a test that I can
1640- * understand.
1710+
16411711 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+
16421720 trigger . click ( ) ;
16431721 groupFixture . detectChanges ( ) ;
16441722
@@ -1648,16 +1726,14 @@ need to come up with good replacement tests, but I believe the actual logic work
16481726 const option = overlayContainerElement . querySelector ( '.cdk-overlay-pane md-option' ) ;
16491727 const optionTop = option ? option . getBoundingClientRect ( ) . top : 0 ;
16501728
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.' ) ;
16531731 } ) ;
16541732 } ) ) ;
1655- */
16561733 } ) ;
16571734 } ) ;
16581735
16591736 describe ( 'accessibility' , ( ) => {
1660-
16611737 describe ( 'for select' , ( ) => {
16621738 let fixture : ComponentFixture < BasicSelect > ;
16631739 let select : HTMLElement ;
@@ -2160,7 +2236,7 @@ need to come up with good replacement tests, but I believe the actual logic work
21602236 TestBed . createComponent ( SelectEarlyAccessSibling ) . detectChanges ( ) ;
21612237 } ) . not . toThrow ( ) ;
21622238 } ) ) ;
2163- /* TODO(mmalerba): no idea whats up with this
2239+
21642240 it ( 'should not throw selection model-related errors in addition to the errors from ngModel' ,
21652241 async ( ( ) => {
21662242 const fixture = TestBed . createComponent ( InvalidSelectInForm ) ;
@@ -2171,7 +2247,6 @@ need to come up with good replacement tests, but I believe the actual logic work
21712247 // The second run shouldn't throw selection-model related errors.
21722248 expect ( ( ) => fixture . detectChanges ( ) ) . not . toThrow ( ) ;
21732249 } ) ) ;
2174- */
21752250 } ) ;
21762251
21772252 describe ( 'change event' , ( ) => {
@@ -2351,8 +2426,6 @@ need to come up with good replacement tests, but I believe the actual logic work
23512426 expect ( testInstance . control . value ) . toEqual ( [ ] ) ;
23522427 } ) ;
23532428
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.
23562429 it ( 'should update the label' , async ( ( ) => {
23572430 trigger . click ( ) ;
23582431 fixture . detectChanges ( ) ;
@@ -2375,7 +2448,7 @@ need to come up with good replacement tests, but I believe the actual logic work
23752448 expect ( trigger . textContent ) . toContain ( 'Steak, Eggs' ) ;
23762449 } ) ;
23772450 } ) ;
2378- }));*/
2451+ } ) ) ;
23792452
23802453 it ( 'should be able to set the selected value by taking an array' , ( ) => {
23812454 trigger . click ( ) ;
0 commit comments