Skip to content

Commit 8b95fb2

Browse files
committed
fix remaining tests
1 parent 7e3434a commit 8b95fb2

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
@@ -1027,10 +1027,15 @@ describe('MdSelect', () => {
10271027
const options = overlayPane.querySelectorAll('md-option');
10281028
const optionTop = options[index].getBoundingClientRect().top;
10291029
const triggerFontSize = parseInt(window.getComputedStyle(trigger)['font-size']);
1030+
const triggerLineHeightEm = 1.125;
10301031

1031-
/** TODO(mmalerba): not really sure where the "+1" is coming from here... */
1032-
expect(Math.floor(optionTop)).toBe(Math.floor(triggerTop - triggerFontSize + 1),
1033-
`Expected trigger to align with option ${index}.`);
1032+
// Extra trigger height beyond the font size caused by the fact that the line-height is
1033+
// greater than 1em.
1034+
const triggerExtraLineSpaceAbove = (1 - triggerLineHeightEm) * triggerFontSize / 2;
1035+
1036+
expect(Math.floor(optionTop))
1037+
.toBe(Math.floor(triggerTop - triggerFontSize - triggerExtraLineSpaceAbove),
1038+
`Expected trigger to align with option ${index}.`);
10341039

10351040
// For the animation to start at the option's center, its origin must be the distance
10361041
// from the top of the overlay to the option top + half the option height (48/2 = 24).
@@ -1157,12 +1162,44 @@ describe('MdSelect', () => {
11571162
formField.style.position = 'fixed';
11581163
formField.style.left = '20px';
11591164
});
1160-
/* TODO(mmalerba): The numbers in these tests are really hard to follow,
1161-
need to come up with good replacement tests, but I believe the actual logic works correctly now.
1165+
11621166
it('should adjust position of centered option if there is little space above', async(() => {
1163-
// Push the select to a position with not quite enough space on the top to open
1164-
// with the option completely centered (needs 112px at least: 256/2 - (48 - 16)/2)
1165-
formField.style.top = '85px';
1167+
const formField = fixture.debugElement.query(By.css('.mat-form-field')).nativeElement;
1168+
const trigger = fixture.debugElement.query(By.css('.mat-select-trigger')).nativeElement;
1169+
1170+
const selectMenuHeight = 256;
1171+
const selectMenuViewportPadding = 8;
1172+
const selectItemHeight = 48;
1173+
const selectedIndex = 4;
1174+
const fontSize = 16;
1175+
const lineHeightEm = 1.125;
1176+
const expectedExtraScroll = 5;
1177+
1178+
// Trigger element height.
1179+
const triggerHeight = fontSize * lineHeightEm;
1180+
1181+
// Ideal space above selected item in order to center it.
1182+
const idealSpaceAboveSelectedItem = (selectMenuHeight - selectItemHeight) / 2;
1183+
1184+
// Actual space above selected item.
1185+
const actualSpaceAboveSelectedItem = selectItemHeight * selectedIndex;
1186+
1187+
// Ideal scroll position to center.
1188+
const idealScrollTop = actualSpaceAboveSelectedItem - idealSpaceAboveSelectedItem;
1189+
1190+
// Top-most select-position that allows for perfect centering.
1191+
const topMostPositionForPerfectCentering =
1192+
idealSpaceAboveSelectedItem + selectMenuViewportPadding +
1193+
(selectItemHeight - triggerHeight) / 2;
1194+
1195+
// Position of select relative to top edge of md-form-field.
1196+
const formFieldTopSpace =
1197+
trigger.getBoundingClientRect().top - formField.getBoundingClientRect().top;
1198+
1199+
const formFieldTop =
1200+
topMostPositionForPerfectCentering - formFieldTopSpace - expectedExtraScroll;
1201+
1202+
formField.style.top = `${formFieldTop}px`;
11661203

11671204
// Select an option in the middle of the list
11681205
fixture.componentInstance.control.setValue('chips-4');
@@ -1174,20 +1211,53 @@ need to come up with good replacement tests, but I believe the actual logic work
11741211
const scrollContainer = document.querySelector('.cdk-overlay-pane .mat-select-panel')!;
11751212

11761213
fixture.whenStable().then(() => {
1177-
// Scroll should adjust by the difference between the top space available (85px + 8px
1178-
// viewport padding = 77px) and the height of the panel above the option (112px).
1179-
// 112px - 93px = 20px difference + original scrollTop 88px = 108px
11801214
expect(scrollContainer.scrollTop)
1181-
.toEqual(108, `Expected panel to adjust scroll position to fit in viewport.`);
1215+
.toEqual(idealScrollTop + 5,
1216+
`Expected panel to adjust scroll position to fit in viewport.`);
11821217

11831218
checkTriggerAlignedWithOption(4);
11841219
});
11851220
}));
11861221

11871222
it('should adjust position of centered option if there is little space below', async(() => {
1223+
const formField = fixture.debugElement.query(By.css('.mat-form-field')).nativeElement;
1224+
const trigger = fixture.debugElement.query(By.css('.mat-select-trigger')).nativeElement;
1225+
1226+
const selectMenuHeight = 256;
1227+
const selectMenuViewportPadding = 8;
1228+
const selectItemHeight = 48;
1229+
const selectedIndex = 4;
1230+
const fontSize = 16;
1231+
const lineHeightEm = 1.125;
1232+
const expectedExtraScroll = 5;
1233+
1234+
// Trigger element height.
1235+
const triggerHeight = fontSize * lineHeightEm;
1236+
1237+
// Ideal space above selected item in order to center it.
1238+
const idealSpaceAboveSelectedItem = (selectMenuHeight - selectItemHeight) / 2;
1239+
1240+
// Actual space above selected item.
1241+
const actualSpaceAboveSelectedItem = selectItemHeight * selectedIndex;
1242+
1243+
// Ideal scroll position to center.
1244+
const idealScrollTop = actualSpaceAboveSelectedItem - idealSpaceAboveSelectedItem;
1245+
1246+
// Bottom-most select-position that allows for perfect centering.
1247+
const bottomMostPositionForPerfectCentering =
1248+
idealSpaceAboveSelectedItem + selectMenuViewportPadding +
1249+
(selectItemHeight - triggerHeight) / 2;
1250+
1251+
// Position of select relative to bottom edge of md-form-field:
1252+
const formFieldBottomSpace =
1253+
formField.getBoundingClientRect().bottom - trigger.getBoundingClientRect().bottom;
1254+
1255+
const formFieldBottom =
1256+
bottomMostPositionForPerfectCentering - formFieldBottomSpace - expectedExtraScroll;
1257+
11881258
// Push the select to a position with not quite enough space on the bottom to open
11891259
// with the option completely centered (needs 113px at least: 256/2 - 48/2 + 9)
1190-
formField.style.bottom = '56px';
1260+
formField.style.bottom = `${formFieldBottom}px`;
11911261

11921262
// Select an option in the middle of the list
11931263
fixture.componentInstance.control.setValue('chips-4');
@@ -1206,14 +1276,15 @@ need to come up with good replacement tests, but I believe the actual logic work
12061276
// (56px from the bottom of the screen - 8px padding = 48px)
12071277
// and the height of the panel below the option (113px).
12081278
// 113px - 48px = 75px difference. Original scrollTop 88px - 75px = 23px
1209-
expect(Math.ceil(scrollContainer.scrollTop))
1210-
.toEqual(23, `Expected panel to adjust scroll position to fit in viewport.`);
1279+
expect(scrollContainer.scrollTop)
1280+
.toEqual(idealScrollTop - expectedExtraScroll,
1281+
`Expected panel to adjust scroll position to fit in viewport.`);
12111282

12121283
checkTriggerAlignedWithOption(4);
12131284
});
12141285
});
12151286
}));
1216-
*/
1287+
12171288
it('should fall back to "above" positioning if scroll adjustment will not help', () => {
12181289
// Push the select to a position with not enough space on the bottom to open
12191290
formField.style.bottom = '56px';
@@ -1628,9 +1699,16 @@ need to come up with good replacement tests, but I believe the actual logic work
16281699
// 16px is the default option padding
16291700
expect(Math.floor(selectedOptionLeft)).toEqual(Math.floor(triggerLeft - 16));
16301701
}));
1631-
/* TODO(mmalerba): Again, pretty sure this is right, just need to write a test that I can
1632-
* understand.
1702+
16331703
it('should align the first option to the trigger, if nothing is selected', async(() => {
1704+
// Push down the form field so there is space for the item to completely align.
1705+
formField.style.top = '100px';
1706+
1707+
const menuItemHeight = 48;
1708+
const triggerFontSize = 16;
1709+
const triggerLineHeightEm = 1.125;
1710+
const triggerHeight = triggerFontSize * triggerLineHeightEm;
1711+
16341712
trigger.click();
16351713
groupFixture.detectChanges();
16361714

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

1643-
expect(Math.floor(optionTop))
1644-
.toBe(Math.floor(triggerTop), 'Expected trigger to align with the first option.');
1721+
expect(optionTop + (menuItemHeight - triggerHeight) / 2)
1722+
.toBe(triggerTop, 'Expected trigger to align with the first option.');
16451723
});
16461724
}));
1647-
*/
16481725
});
16491726
});
16501727

16511728
describe('accessibility', () => {
1652-
16531729
describe('for select', () => {
16541730
let fixture: ComponentFixture<BasicSelect>;
16551731
let select: HTMLElement;
@@ -2152,7 +2228,7 @@ need to come up with good replacement tests, but I believe the actual logic work
21522228
TestBed.createComponent(SelectEarlyAccessSibling).detectChanges();
21532229
}).not.toThrow();
21542230
}));
2155-
/* TODO(mmalerba): no idea whats up with this
2231+
21562232
it('should not throw selection model-related errors in addition to the errors from ngModel',
21572233
async(() => {
21582234
const fixture = TestBed.createComponent(InvalidSelectInForm);
@@ -2163,7 +2239,6 @@ need to come up with good replacement tests, but I believe the actual logic work
21632239
// The second run shouldn't throw selection-model related errors.
21642240
expect(() => fixture.detectChanges()).not.toThrow();
21652241
}));
2166-
*/
21672242
});
21682243

21692244
describe('change event', () => {
@@ -2343,8 +2418,6 @@ need to come up with good replacement tests, but I believe the actual logic work
23432418
expect(testInstance.control.value).toEqual([]);
23442419
});
23452420

2346-
/* TODO(mmalerba): not sure why this fails... It doesn't register the update when option 2 is
2347-
deselected, but it works fine in the demo app.
23482421
it('should update the label', async(() => {
23492422
trigger.click();
23502423
fixture.detectChanges();
@@ -2367,7 +2440,7 @@ need to come up with good replacement tests, but I believe the actual logic work
23672440
expect(trigger.textContent).toContain('Steak, Eggs');
23682441
});
23692442
});
2370-
}));*/
2443+
}));
23712444

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

src/lib/select/select.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,6 @@ export const SELECT_ITEM_HEIGHT_EM = 3;
8686
*/
8787
export const SELECT_MULTIPLE_PANEL_PADDING_X = SELECT_PANEL_PADDING_X * 1.5 + 20;
8888

89-
/**
90-
* The panel's padding on the y-axis. This padding indicates there are more
91-
* options available if you scroll.
92-
*/
93-
export const SELECT_PANEL_PADDING_Y = 16;
94-
9589
/**
9690
* The select panel will only "fit" inside the viewport if it is positioned at
9791
* this value or more away from the viewport boundary.
@@ -664,7 +658,7 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
664658

665659
/** Whether the select has a value. */
666660
get empty(): boolean {
667-
return this._selectionModel && this._selectionModel.isEmpty();
661+
return !this._selectionModel || this._selectionModel.isEmpty();
668662
}
669663

670664
/** Whether the select is in an error state. */
@@ -845,6 +839,7 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
845839
this._onChange(valueToEmit);
846840
this.change.emit(new MdSelectChange(this, valueToEmit));
847841
this.valueChange.emit(valueToEmit);
842+
this._changeDetectorRef.markForCheck();
848843
}
849844

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

0 commit comments

Comments
 (0)