Skip to content

Commit 3a10cf6

Browse files
committed
fix remaining tests
1 parent 784abb7 commit 3a10cf6

File tree

3 files changed

+103
-36
lines changed

3 files changed

+103
-36
lines changed

src/lib/select/select.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ $mat-select-item-height: 3em !default;
1212
$mat-select-placeholder-arrow-space: 2 * ($mat-select-arrow-size + $mat-select-arrow-margin);
1313

1414
.mat-select {
15-
//display: inline-block;
15+
display: inline-block;
1616
outline: none;
1717
}
1818

src/lib/select/select.spec.ts

Lines changed: 100 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,10 +1036,15 @@ describe('MdSelect', () => {
10361036
const options = overlayPane.querySelectorAll('md-option');
10371037
const optionTop = options[index].getBoundingClientRect().top;
10381038
const triggerFontSize = parseInt(window.getComputedStyle(trigger)['font-size']);
1039+
const triggerLineHeightEm = 1.125;
10391040

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}.`);
10431048

10441049
// For the animation to start at the option's center, its origin must be the distance
10451050
// from the top of the overlay to the option top + half the option height (48/2 = 24).
@@ -1166,12 +1171,44 @@ describe('MdSelect', () => {
11661171
formField.style.position = 'fixed';
11671172
formField.style.left = '20px';
11681173
});
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+
11711175
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`;
11751212

11761213
// Select an option in the middle of the list
11771214
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
11831220
const scrollContainer = document.querySelector('.cdk-overlay-pane .mat-select-panel')!;
11841221

11851222
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
11891223
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.`);
11911226

11921227
checkTriggerAlignedWithOption(4);
11931228
});
11941229
}));
11951230

11961231
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+
11971267
// Push the select to a position with not quite enough space on the bottom to open
11981268
// 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`;
12001270

12011271
// Select an option in the middle of the list
12021272
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
12151285
// (56px from the bottom of the screen - 8px padding = 48px)
12161286
// and the height of the panel below the option (113px).
12171287
// 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.`);
12201291

12211292
checkTriggerAlignedWithOption(4);
12221293
});
12231294
});
12241295
}));
1225-
*/
1296+
12261297
it('should fall back to "above" positioning if scroll adjustment will not help', () => {
12271298
// Push the select to a position with not enough space on the bottom to open
12281299
formField.style.bottom = '56px';
@@ -1637,9 +1708,16 @@ need to come up with good replacement tests, but I believe the actual logic work
16371708
// 16px is the default option padding
16381709
expect(Math.floor(selectedOptionLeft)).toEqual(Math.floor(triggerLeft - 16));
16391710
}));
1640-
/* TODO(mmalerba): Again, pretty sure this is right, just need to write a test that I can
1641-
* understand.
1711+
16421712
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+
16431721
trigger.click();
16441722
groupFixture.detectChanges();
16451723

@@ -1649,16 +1727,14 @@ need to come up with good replacement tests, but I believe the actual logic work
16491727
const option = overlayContainerElement.querySelector('.cdk-overlay-pane md-option');
16501728
const optionTop = option ? option.getBoundingClientRect().top : 0;
16511729

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.');
16541732
});
16551733
}));
1656-
*/
16571734
});
16581735
});
16591736

16601737
describe('accessibility', () => {
1661-
16621738
describe('for select', () => {
16631739
let fixture: ComponentFixture<BasicSelect>;
16641740
let select: HTMLElement;
@@ -2161,7 +2237,7 @@ need to come up with good replacement tests, but I believe the actual logic work
21612237
TestBed.createComponent(SelectEarlyAccessSibling).detectChanges();
21622238
}).not.toThrow();
21632239
}));
2164-
/* TODO(mmalerba): no idea whats up with this
2240+
21652241
it('should not throw selection model-related errors in addition to the errors from ngModel',
21662242
async(() => {
21672243
const fixture = TestBed.createComponent(InvalidSelectInForm);
@@ -2172,8 +2248,6 @@ need to come up with good replacement tests, but I believe the actual logic work
21722248
// The second run shouldn't throw selection-model related errors.
21732249
expect(() => fixture.detectChanges()).not.toThrow();
21742250
}));
2175-
*/
2176-
21772251

21782252
it('should not throw when the triggerValue is accessed when there is no selected value', () => {
21792253
const fixture = TestBed.createComponent(BasicSelect);
@@ -2360,8 +2434,6 @@ need to come up with good replacement tests, but I believe the actual logic work
23602434
expect(testInstance.control.value).toEqual([]);
23612435
});
23622436

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.
23652437
it('should update the label', async(() => {
23662438
trigger.click();
23672439
fixture.detectChanges();
@@ -2384,7 +2456,7 @@ need to come up with good replacement tests, but I believe the actual logic work
23842456
expect(trigger.textContent).toContain('Steak, Eggs');
23852457
});
23862458
});
2387-
}));*/
2459+
}));
23882460

23892461
it('should be able to set the selected value by taking an array', () => {
23902462
trigger.click();

src/lib/select/select.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,6 @@ export const SELECT_ITEM_HEIGHT_EM = 3;
9999
*/
100100
export const SELECT_MULTIPLE_PANEL_PADDING_X = SELECT_PANEL_PADDING_X * 1.5 + 20;
101101

102-
/**
103-
* The panel's padding on the y-axis. This padding indicates there are more
104-
* options available if you scroll.
105-
*/
106-
export const SELECT_PANEL_PADDING_Y = 16;
107-
108102
/**
109103
* The select panel will only "fit" inside the viewport if it is positioned at
110104
* this value or more away from the viewport boundary.
@@ -680,7 +674,7 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
680674

681675
/** Whether the select has a value. */
682676
get empty(): boolean {
683-
return this._selectionModel && this._selectionModel.isEmpty();
677+
return !this._selectionModel || this._selectionModel.isEmpty();
684678
}
685679

686680
/** Whether the select is in an error state. */
@@ -876,6 +870,7 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
876870
this._onChange(valueToEmit);
877871
this.change.emit(new MdSelectChange(this, valueToEmit));
878872
this.valueChange.emit(valueToEmit);
873+
this._changeDetectorRef.markForCheck();
879874
}
880875

881876
/** Records option IDs to pass to the aria-owns property. */

0 commit comments

Comments
 (0)