Skip to content

Commit 1a1efa8

Browse files
committed
fix remaining tests
1 parent c901016 commit 1a1efa8

File tree

3 files changed

+103
-35
lines changed

3 files changed

+103
-35
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 & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -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();

src/lib/select/select.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,6 @@ export const SELECT_ITEM_HEIGHT_EM = 3;
9191
*/
9292
export const SELECT_MULTIPLE_PANEL_PADDING_X = SELECT_PANEL_PADDING_X * 1.5 + 20;
9393

94-
/**
95-
* The panel's padding on the y-axis. This padding indicates there are more
96-
* options available if you scroll.
97-
*/
98-
export const SELECT_PANEL_PADDING_Y = 16;
99-
10094
/**
10195
* The select panel will only "fit" inside the viewport if it is positioned at
10296
* this value or more away from the viewport boundary.
@@ -685,7 +679,7 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
685679

686680
/** Whether the select has a value. */
687681
get empty(): boolean {
688-
return this._selectionModel && this._selectionModel.isEmpty();
682+
return !this._selectionModel || this._selectionModel.isEmpty();
689683
}
690684

691685
/** Whether the select is in an error state. */
@@ -884,6 +878,7 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
884878
this._onChange(valueToEmit);
885879
this.change.emit(new MdSelectChange(this, valueToEmit));
886880
this.valueChange.emit(valueToEmit);
881+
this._changeDetectorRef.markForCheck();
887882
}
888883

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

0 commit comments

Comments
 (0)