From 2420398b5f0f11ad7c126e4e53e1a0a32b79a069 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Wed, 22 Feb 2017 20:43:22 +0100 Subject: [PATCH 1/5] feat(select): add md-select-header directive Adds a `md-select-header` component, which is a fixed header above the select's options. It allows for the user to project an input to be used for filtering long lists of options. **Note:** This component only handles the positioning, styling and exposes the panel id for a11y. The functionality is up to the user to handle. Fixes #2812. --- src/demo-app/select/select-demo.html | 15 +++ src/demo-app/select/select-demo.ts | 9 ++ .../select-header/select-header-example.css | 1 + .../select-header/select-header-example.html | 12 ++ .../select-header/select-header-example.ts | 30 +++++ src/lib/core/style/_menu-common.scss | 10 +- src/lib/select/_select-theme.scss | 4 + src/lib/select/index.ts | 6 +- src/lib/select/select-header.ts | 13 +++ src/lib/select/select.html | 7 +- src/lib/select/select.scss | 27 ++++- src/lib/select/select.spec.ts | 106 ++++++++++++++++-- src/lib/select/select.ts | 19 +++- src/material-examples/example-module.ts | 4 + 14 files changed, 242 insertions(+), 21 deletions(-) create mode 100644 src/examples/select-header/select-header-example.css create mode 100644 src/examples/select-header/select-header-example.html create mode 100644 src/examples/select-header/select-header-example.ts create mode 100644 src/lib/select/select-header.ts diff --git a/src/demo-app/select/select-demo.html b/src/demo-app/select/select-demo.html index fbb317a33f89..54508223a18b 100644 --- a/src/demo-app/select/select-demo.html +++ b/src/demo-app/select/select-demo.html @@ -113,5 +113,20 @@ + + + + + + + + {{ drink.viewValue }} + + + + + +
This div is for testing scrolled selects.
diff --git a/src/demo-app/select/select-demo.ts b/src/demo-app/select/select-demo.ts index 6318b2a6272c..b0eb9882b3b1 100644 --- a/src/demo-app/select/select-demo.ts +++ b/src/demo-app/select/select-demo.ts @@ -17,6 +17,7 @@ export class SelectDemo { currentDrink: string; currentPokemon: string[]; currentPokemonFromGroup: string; + searchTerm: string; latestChangeEvent: MdSelectChange; floatPlaceholder: string = 'auto'; foodControl = new FormControl('pizza-1'); @@ -43,6 +44,8 @@ export class SelectDemo { {value: 'milk-8', viewValue: 'Milk'}, ]; + filteredDrinks = this.drinks.slice(); + pokemon = [ {value: 'bulbasaur-0', viewValue: 'Bulbasaur'}, {value: 'charizard-1', viewValue: 'Charizard'}, @@ -101,4 +104,10 @@ export class SelectDemo { setPokemonValue() { this.currentPokemon = ['eevee-4', 'psyduck-6']; } + + filterDrinks() { + this.filteredDrinks = this.searchTerm ? this.drinks.filter(item => { + return item.viewValue.toLowerCase().indexOf(this.searchTerm.toLowerCase()) > -1; + }) : this.drinks.slice(); + } } diff --git a/src/examples/select-header/select-header-example.css b/src/examples/select-header/select-header-example.css new file mode 100644 index 000000000000..7432308753e6 --- /dev/null +++ b/src/examples/select-header/select-header-example.css @@ -0,0 +1 @@ +/** No CSS for this example */ diff --git a/src/examples/select-header/select-header-example.html b/src/examples/select-header/select-header-example.html new file mode 100644 index 000000000000..08636df97eff --- /dev/null +++ b/src/examples/select-header/select-header-example.html @@ -0,0 +1,12 @@ + + + + + + + {{food.viewValue}} + + + +

Selected value: {{selectedValue}}

diff --git a/src/examples/select-header/select-header-example.ts b/src/examples/select-header/select-header-example.ts new file mode 100644 index 000000000000..5ed5747d1522 --- /dev/null +++ b/src/examples/select-header/select-header-example.ts @@ -0,0 +1,30 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'select-form-example', + templateUrl: './select-form-example.html', +}) +export class SelectHeaderExample { + selectedValue: string; + searchString: string; + + initialFoods = [ + { value: 'steak-0', viewValue: 'Steak' }, + { value: 'pizza-1', viewValue: 'Pizza' }, + { value: 'tacos-2', viewValue: 'Tacos' }, + { value: 'sandwich-3', viewValue: 'Sandwich' }, + { value: 'chips-4', viewValue: 'Chips' }, + { value: 'eggs-5', viewValue: 'Eggs' }, + { value: 'pasta-6', viewValue: 'Pasta' }, + { value: 'sushi-7', viewValue: 'Sushi' }, + ]; + + foods = this.initialFoods.slice(); + + filterFoods() { + this.foods = this.searchString ? this.initialFoods.filter(item => { + return item.viewValue.toLowerCase().indexOf(this.searchString.toLowerCase()) > -1; + }) : this.initialFoods.slice(); + } +} diff --git a/src/lib/core/style/_menu-common.scss b/src/lib/core/style/_menu-common.scss index 6eea82eb1f96..336bb687906d 100644 --- a/src/lib/core/style/_menu-common.scss +++ b/src/lib/core/style/_menu-common.scss @@ -15,11 +15,10 @@ $mat-menu-icon-margin: 16px !default; @mixin mat-menu-base() { @include mat-elevation(8); + @include mat-menu-scrollable(); + min-width: $mat-menu-overlay-min-width; max-width: $mat-menu-overlay-max-width; - - overflow: auto; - -webkit-overflow-scrolling: touch; // for momentum scroll on mobile } @mixin mat-menu-item-base() { @@ -91,3 +90,8 @@ $mat-menu-icon-margin: 16px !default; } } } + +@mixin mat-menu-scrollable() { + overflow: auto; + -webkit-overflow-scrolling: touch; // for momentum scroll on mobile +} diff --git a/src/lib/select/_select-theme.scss b/src/lib/select/_select-theme.scss index 4033a4716b97..5610237cedfb 100644 --- a/src/lib/select/_select-theme.scss +++ b/src/lib/select/_select-theme.scss @@ -26,6 +26,10 @@ color: mat-color($foreground, hint-text); } + .mat-select-header { + color: mat-color($foreground, divider); + } + .mat-select-underline { background-color: mat-color($foreground, divider); } diff --git a/src/lib/select/index.ts b/src/lib/select/index.ts index de0337e3a297..6d4b4486cc5b 100644 --- a/src/lib/select/index.ts +++ b/src/lib/select/index.ts @@ -9,6 +9,7 @@ import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import {MdSelect} from './select'; +import {MdSelectHeader} from './select-header'; import {MdCommonModule, OverlayModule, MdOptionModule} from '../core'; @@ -19,11 +20,12 @@ import {MdCommonModule, OverlayModule, MdOptionModule} from '../core'; MdOptionModule, MdCommonModule, ], - exports: [MdSelect, MdOptionModule, MdCommonModule], - declarations: [MdSelect], + exports: [MdSelect, MdSelectHeader, MdOptionModule, MdCommonModule], + declarations: [MdSelect, MdSelectHeader], }) export class MdSelectModule {} export * from './select'; +export * from './select-header'; export {fadeInContent, transformPanel, transformPlaceholder} from './select-animations'; diff --git a/src/lib/select/select-header.ts b/src/lib/select/select-header.ts new file mode 100644 index 000000000000..0a53c7db8033 --- /dev/null +++ b/src/lib/select/select-header.ts @@ -0,0 +1,13 @@ +import {Directive} from '@angular/core'; + + +/** + * Fixed header that will be rendered above a select's options. + */ +@Directive({ + selector: 'md-select-header, mat-select-header', + host: { + 'class': 'mat-select-header', + } +}) +export class MdSelectHeader { } diff --git a/src/lib/select/select.html b/src/lib/select/select.html index 0eb861f9c22f..4082da2cda8b 100644 --- a/src/lib/select/select.html +++ b/src/lib/select/select.html @@ -35,7 +35,12 @@ [style.transformOrigin]="_transformOrigin" [class.mat-select-panel-done-animating]="_panelDoneAnimating"> -
+
+ +
+ +
diff --git a/src/lib/select/select.scss b/src/lib/select/select.scss index d7ca055905d9..0380d5b9aca0 100644 --- a/src/lib/select/select.scss +++ b/src/lib/select/select.scss @@ -117,10 +117,8 @@ $mat-select-panel-max-height: 256px !default; margin: 0 $mat-select-arrow-margin; } -.mat-select-panel { - @include mat-menu-base(); - padding-top: 0; - padding-bottom: 0; +.mat-select-content { + @include mat-menu-scrollable(); max-height: $mat-select-panel-max-height; min-width: 100%; // prevents some animation twitching and test inconsistencies in IE11 @@ -128,3 +126,24 @@ $mat-select-panel-max-height: 256px !default; outline: solid 1px; } } + +.mat-select-panel { + @include mat-menu-base(); + border: none; +} + +.mat-select-header { + @include mat-menu-item-base(); + border-bottom: solid 1px; + box-sizing: border-box; + + input { + display: block; + width: 100%; + height: 100%; + border: none; + outline: none; + padding: 0; + background: transparent; + } +} diff --git a/src/lib/select/select.spec.ts b/src/lib/select/select.spec.ts index 2811bb1c9854..1a8f9d799125 100644 --- a/src/lib/select/select.spec.ts +++ b/src/lib/select/select.spec.ts @@ -63,7 +63,8 @@ describe('MdSelect', () => { BasicSelectWithTheming, ResetValuesSelect, FalsyValueSelect, - SelectWithGroups + SelectWithGroups, + BasicSelectWithHeader ], providers: [ {provide: OverlayContainer, useFactory: () => { @@ -277,6 +278,17 @@ describe('MdSelect', () => { expect(panel.classList).toContain('custom-two'); }); + it('should set an id on the select panel', () => { + trigger.click(); + fixture.detectChanges(); + + const panel = document.querySelector('.cdk-overlay-pane .mat-select-content'); + const instance = fixture.componentInstance.select; + + expect(instance.panelId).toBeTruthy(); + expect(panel.getAttribute('id')).toBe(instance.panelId); + }); + }); describe('selection logic', () => { @@ -890,7 +902,7 @@ describe('MdSelect', () => { trigger.click(); fixture.detectChanges(); - const scrollContainer = document.querySelector('.cdk-overlay-pane .mat-select-panel'); + const scrollContainer = document.querySelector('.cdk-overlay-pane .mat-select-content'); // The panel should be scrolled to 0 because centering the option is not possible. expect(scrollContainer.scrollTop).toEqual(0, `Expected panel not to be scrolled.`); @@ -906,7 +918,7 @@ describe('MdSelect', () => { trigger.click(); fixture.detectChanges(); - const scrollContainer = document.querySelector('.cdk-overlay-pane .mat-select-panel'); + const scrollContainer = document.querySelector('.cdk-overlay-pane .mat-select-content'); // The panel should be scrolled to 0 because centering the option is not possible. expect(scrollContainer.scrollTop).toEqual(0, `Expected panel not to be scrolled.`); @@ -922,7 +934,7 @@ describe('MdSelect', () => { trigger.click(); fixture.detectChanges(); - const scrollContainer = document.querySelector('.cdk-overlay-pane .mat-select-panel'); + const scrollContainer = document.querySelector('.cdk-overlay-pane .mat-select-content'); // The selected option should be scrolled to the center of the panel. // This will be its original offset from the scrollTop - half the panel height + half the @@ -942,7 +954,7 @@ describe('MdSelect', () => { trigger.click(); fixture.detectChanges(); - const scrollContainer = document.querySelector('.cdk-overlay-pane .mat-select-panel'); + const scrollContainer = document.querySelector('.cdk-overlay-pane .mat-select-content'); // The selected option should be scrolled to the max scroll position. // This will be the height of the scrollContainer - the panel height. @@ -1005,7 +1017,7 @@ describe('MdSelect', () => { trigger.click(); fixture.detectChanges(); - const scrollContainer = document.querySelector('.cdk-overlay-pane .mat-select-panel'); + const scrollContainer = document.querySelector('.cdk-overlay-pane .mat-select-content'); // Scroll should adjust by the difference between the top space available (85px + 8px // viewport padding = 77px) and the height of the panel above the option (113px). @@ -1028,7 +1040,7 @@ describe('MdSelect', () => { trigger.click(); fixture.detectChanges(); - const scrollContainer = document.querySelector('.cdk-overlay-pane .mat-select-panel'); + const scrollContainer = document.querySelector('.cdk-overlay-pane .mat-select-content'); // Scroll should adjust by the difference between the bottom space available // (56px from the bottom of the screen - 8px padding = 48px) @@ -1055,7 +1067,7 @@ describe('MdSelect', () => { const overlayPane = document.querySelector('.cdk-overlay-pane'); const triggerBottom = trigger.getBoundingClientRect().bottom; const overlayBottom = overlayPane.getBoundingClientRect().bottom; - const scrollContainer = overlayPane.querySelector('.mat-select-panel'); + const scrollContainer = overlayPane.querySelector('.mat-select-content'); // Expect no scroll to be attempted expect(scrollContainer.scrollTop).toEqual(0, `Expected panel not to be scrolled.`); @@ -1082,7 +1094,7 @@ describe('MdSelect', () => { const overlayPane = document.querySelector('.cdk-overlay-pane'); const triggerTop = trigger.getBoundingClientRect().top; const overlayTop = overlayPane.getBoundingClientRect().top; - const scrollContainer = overlayPane.querySelector('.mat-select-panel'); + const scrollContainer = overlayPane.querySelector('.mat-select-content'); // Expect scroll to remain at the max scroll position expect(scrollContainer.scrollTop).toEqual(128, `Expected panel to be at max scroll.`); @@ -1459,6 +1471,51 @@ describe('MdSelect', () => { }); + describe('with header', () => { + let headerFixture: ComponentFixture; + + beforeEach(() => { + headerFixture = TestBed.createComponent(BasicSelectWithHeader); + headerFixture.detectChanges(); + trigger = headerFixture.debugElement.query(By.css('.mat-select-trigger')).nativeElement; + select = headerFixture.debugElement.query(By.css('md-select')).nativeElement; + + select.style.marginTop = '300px'; + select.style.marginLeft = '20px'; + select.style.marginRight = '20px'; + }); + + it('should account for the header when there is no value', () => { + trigger.click(); + headerFixture.detectChanges(); + + const scrollContainer = document.querySelector('.cdk-overlay-pane .mat-select-content'); + + expect(scrollContainer.scrollTop).toEqual(0, `Expected panel not to be scrolled.`); + checkTriggerAlignedWithOption(0, headerFixture.componentInstance.select); + }); + + it('should align a selected option in the middle with the trigger text', () => { + // Select the fifth option, which has enough space to scroll to the center + headerFixture.componentInstance.control.setValue('chips-4'); + headerFixture.detectChanges(); + + trigger.click(); + headerFixture.detectChanges(); + + const scrollContainer = document.querySelector('.cdk-overlay-pane .mat-select-content'); + + // The selected option should be scrolled to the center of the panel. + // This will be its original offset from the scrollTop - half the panel height + half the + // option height. 4 (index) * 48 (option height) = 192px offset from scrollTop + // 192 - 256/2 + 48/2 = 88px + expect(scrollContainer.scrollTop) + .toEqual(88, `Expected overlay panel to be scrolled to center the selected option.`); + + checkTriggerAlignedWithOption(4, headerFixture.componentInstance.select); + }); + }); + }); describe('accessibility', () => { @@ -2801,3 +2858,34 @@ class SelectWithGroups { @ViewChild(MdSelect) select: MdSelect; @ViewChildren(MdOption) options: QueryList; } + +@Component({ + selector: 'basic-select-with-header', + template: ` + + + + + + + {{ food.viewValue }} + + + ` +}) +class BasicSelectWithHeader { + foods: any[] = [ + { value: 'steak-0', viewValue: 'Steak' }, + { value: 'pizza-1', viewValue: 'Pizza' }, + { value: 'tacos-2', viewValue: 'Tacos' }, + { value: 'sandwich-3', viewValue: 'Sandwich' }, + { value: 'chips-4', viewValue: 'Chips' }, + { value: 'eggs-5', viewValue: 'Eggs' }, + { value: 'pasta-6', viewValue: 'Pasta' }, + { value: 'sushi-7', viewValue: 'Sushi' }, + ]; + control = new FormControl(); + + @ViewChild(MdSelect) select: MdSelect; + @ViewChildren(MdOption) options: QueryList; +} diff --git a/src/lib/select/select.ts b/src/lib/select/select.ts index 7be1cf9f60e8..ec7999cd2ca6 100644 --- a/src/lib/select/select.ts +++ b/src/lib/select/select.ts @@ -9,6 +9,7 @@ import { AfterContentInit, Component, + ContentChild, ContentChildren, ElementRef, EventEmitter, @@ -28,6 +29,7 @@ import { } from '@angular/core'; import {MdOption, MdOptionSelectionChange, MdOptgroup} from '../core/option/index'; import {ENTER, SPACE, UP_ARROW, DOWN_ARROW, HOME, END} from '../core/keyboard/keycodes'; +import {MdSelectHeader} from './select-header'; import {FocusKeyManager} from '../core/a11y/focus-key-manager'; import {Dir} from '../core/rtl/dir'; import {Observable} from 'rxjs/Observable'; @@ -116,6 +118,9 @@ export class MdSelectBase { } export const _MdSelectMixinBase = mixinColor(MdSelectBase, 'primary'); +/** Counter for unique panel IDs. */ +let panelIds = 0; + @Component({ moduleId: module.id, selector: 'md-select, mat-select', @@ -209,6 +214,9 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On /** The IDs of child options to be passed to the aria-owns attribute. */ _optionIds: string = ''; + /** Unique ID for the panel element. Useful for a11y in projected content (e.g. the header). */ + panelId: string = 'md-select-panel-' + panelIds++; + /** The value of the select panel's transform-origin property. */ _transformOrigin: string = 'top'; @@ -258,6 +266,9 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On /** Classes to be passed to the select panel. Supports the same syntax as `ngClass`. */ @Input() panelClass: string|string[]|Set|{[key: string]: any}; + /** The select's header, if specified. */ + @ContentChild(MdSelectHeader) header: MdSelectHeader; + /** Placeholder to be shown if no value has been selected. */ @Input() get placeholder() { return this._placeholder; } @@ -566,7 +577,7 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On */ private _setScrollTop(): void { const scrollContainer = - this.overlayDir.overlayRef.overlayElement.querySelector('.mat-select-panel'); + this.overlayDir.overlayRef.overlayElement.querySelector('.mat-select-content'); scrollContainer.scrollTop = this._scrollTop; } @@ -800,6 +811,8 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On // and the trigger element, then multiply it by -1 to ensure the panel moves // in the correct direction up the page. this._offsetY = (SELECT_ITEM_HEIGHT - SELECT_TRIGGER_HEIGHT) / 2 * -1; + // this._offsetY = (SELECT_OPTION_HEIGHT - SELECT_TRIGGER_HEIGHT) / 2 * -1 - + // (this.header ? SELECT_OPTION_HEIGHT : 0); } this._checkOverlayWithinViewport(maxScroll); @@ -932,7 +945,8 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On // The final offset is the option's offset from the top, adjusted for the height // difference, multiplied by -1 to ensure that the overlay moves in the correct // direction up the page. - return optionOffsetFromPanelTop * -1 - SELECT_OPTION_HEIGHT_ADJUSTMENT; + return optionOffsetFromPanelTop * -1 - SELECT_OPTION_HEIGHT_ADJUSTMENT - + (this.header ? SELECT_ITEM_HEIGHT : 0); } /** @@ -952,6 +966,7 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On const panelHeightTop = Math.abs(this._offsetY); const totalPanelHeight = Math.min(this._getItemCount() * SELECT_ITEM_HEIGHT, SELECT_PANEL_MAX_HEIGHT); + // Math.min(this.options.length * SELECT_OPTION_HEIGHT, SELECT_PANEL_MAX_HEIGHT); const panelHeightBottom = totalPanelHeight - panelHeightTop - triggerRect.height; if (panelHeightBottom > bottomSpaceAvailable) { diff --git a/src/material-examples/example-module.ts b/src/material-examples/example-module.ts index 4af008e27335..98261a3b0e21 100644 --- a/src/material-examples/example-module.ts +++ b/src/material-examples/example-module.ts @@ -70,6 +70,7 @@ import {SelectOverviewExample} from './select-overview/select-overview-example'; import {ChipsOverviewExample} from './chips-overview/chips-overview-example'; import {ChipsStackedExample} from './chips-stacked/chips-stacked-example'; import {SelectFormExample} from './select-form/select-form-example'; +import {SelectHeaderExample} from './select-header/select-header-example'; import {DatepickerOverviewExample} from './datepicker-overview/datepicker-overview-example'; import { MdAutocompleteModule, MdButtonModule, MdButtonToggleModule, MdCardModule, MdCheckboxModule, @@ -79,6 +80,7 @@ import { MdToolbarModule, MdTooltipModule } from '@angular/material'; + export interface LiveExample { title: string; component: any; @@ -152,6 +154,7 @@ export const EXAMPLE_COMPONENTS = { 'radio-overview': {title: 'Basic radios', component: RadioOverviewExample}, 'select-overview': {title: 'Basic select', component: SelectOverviewExample}, 'select-form': {title: 'Select in a form', component: SelectFormExample}, + 'select-header': {title: 'Select header', component: SelectHeaderExample}, 'sidenav-fab': {title: 'Sidenav with a FAB', component: SidenavFabExample}, 'sidenav-overview': {title: 'Basic sidenav', component: SidenavOverviewExample}, 'slider-configurable': {title: 'Configurable slider', component: SliderConfigurableExample}, @@ -249,6 +252,7 @@ export const EXAMPLE_LIST = [ SidenavFabExample, SelectOverviewExample, SelectFormExample, + SelectHeaderExample, SidenavOverviewExample, SliderConfigurableExample, SliderOverviewExample, From 6df224b7f25998c31008f6c583bc163a1ebe72fd Mon Sep 17 00:00:00 2001 From: crisbeto Date: Thu, 23 Feb 2017 22:37:28 +0100 Subject: [PATCH 2/5] chore: try to fix edge tests --- src/lib/select/select.spec.ts | 42 ++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/lib/select/select.spec.ts b/src/lib/select/select.spec.ts index 1a8f9d799125..64a7e4e1badd 100644 --- a/src/lib/select/select.spec.ts +++ b/src/lib/select/select.spec.ts @@ -32,7 +32,7 @@ import { import {map} from 'rxjs/operator/map'; -describe('MdSelect', () => { +fdescribe('MdSelect', () => { let overlayContainerElement: HTMLElement; let dir: {value: 'ltr'|'rtl'}; let scrolledSubject = new Subject(); @@ -1320,15 +1320,23 @@ describe('MdSelect', () => { describe('x-axis positioning', () => { beforeEach(() => { +<<<<<<< HEAD select.style.position = 'fixed'; select.style.left = '30px'; }); it('should align the trigger and the selected option on the x-axis in ltr', fakeAsync(() => { +======= + select.style.marginLeft = select.style.marginRight = '50px'; + }); + + it('should align the trigger and the selected option on the x-axis in ltr', async(() => { +>>>>>>> chore: try to fix edge tests trigger.click(); tick(400); fixture.detectChanges(); +<<<<<<< HEAD const triggerLeft = trigger.getBoundingClientRect().left; const firstOptionLeft = document.querySelector('.cdk-overlay-pane md-option') .getBoundingClientRect().left; @@ -1340,6 +1348,22 @@ describe('MdSelect', () => { })); it('should align the trigger and the selected option on the x-axis in rtl', fakeAsync(() => { +======= + fixture.whenStable().then(() => { + const triggerLeft = trigger.getBoundingClientRect().left; + const firstOptionLeft = + document.querySelector('.cdk-overlay-pane md-option').getBoundingClientRect().left; + + // Each option is 32px wider than the trigger, so it must be adjusted 16px + // to ensure the text overlaps correctly. + expect(firstOptionLeft.toFixed(2)) + .toEqual((triggerLeft - 16).toFixed(2), + `Expected trigger to align with the selected option on the x-axis in LTR.`); + }); + })); + + it('should align the trigger and the selected option on the x-axis in rtl', async(() => { +>>>>>>> chore: try to fix edge tests dir.value = 'rtl'; fixture.detectChanges(); @@ -1347,6 +1371,7 @@ describe('MdSelect', () => { tick(400); fixture.detectChanges(); +<<<<<<< HEAD const triggerRight = trigger.getBoundingClientRect().right; const firstOptionRight = document.querySelector('.cdk-overlay-pane md-option').getBoundingClientRect().right; @@ -1469,6 +1494,21 @@ describe('MdSelect', () => { expect(Math.floor(selectedOptionLeft)).toEqual(Math.floor(triggerLeft - 16)); })); +======= + fixture.whenStable().then(() => { + const triggerRight = trigger.getBoundingClientRect().right; + const firstOptionRight = + document.querySelector('.cdk-overlay-pane md-option').getBoundingClientRect().right; + + // Each option is 32px wider than the trigger, so it must be adjusted 16px + // to ensure the text overlaps correctly. + expect(firstOptionRight.toFixed(2)) + .toEqual((triggerRight + 16).toFixed(2), + `Expected trigger to align with the selected option on the x-axis in RTL.`); + }); + })); + +>>>>>>> chore: try to fix edge tests }); describe('with header', () => { From 57534aee7a284ca5bea5c53c4bca33ae4e6d4e0c Mon Sep 17 00:00:00 2001 From: crisbeto Date: Tue, 13 Jun 2017 22:08:14 +0200 Subject: [PATCH 3/5] chore: leftover merge conflicts --- src/lib/select/select.spec.ts | 40 ----------------------------------- 1 file changed, 40 deletions(-) diff --git a/src/lib/select/select.spec.ts b/src/lib/select/select.spec.ts index 64a7e4e1badd..e93f59b69c38 100644 --- a/src/lib/select/select.spec.ts +++ b/src/lib/select/select.spec.ts @@ -1320,23 +1320,15 @@ fdescribe('MdSelect', () => { describe('x-axis positioning', () => { beforeEach(() => { -<<<<<<< HEAD select.style.position = 'fixed'; select.style.left = '30px'; }); it('should align the trigger and the selected option on the x-axis in ltr', fakeAsync(() => { -======= - select.style.marginLeft = select.style.marginRight = '50px'; - }); - - it('should align the trigger and the selected option on the x-axis in ltr', async(() => { ->>>>>>> chore: try to fix edge tests trigger.click(); tick(400); fixture.detectChanges(); -<<<<<<< HEAD const triggerLeft = trigger.getBoundingClientRect().left; const firstOptionLeft = document.querySelector('.cdk-overlay-pane md-option') .getBoundingClientRect().left; @@ -1348,22 +1340,6 @@ fdescribe('MdSelect', () => { })); it('should align the trigger and the selected option on the x-axis in rtl', fakeAsync(() => { -======= - fixture.whenStable().then(() => { - const triggerLeft = trigger.getBoundingClientRect().left; - const firstOptionLeft = - document.querySelector('.cdk-overlay-pane md-option').getBoundingClientRect().left; - - // Each option is 32px wider than the trigger, so it must be adjusted 16px - // to ensure the text overlaps correctly. - expect(firstOptionLeft.toFixed(2)) - .toEqual((triggerLeft - 16).toFixed(2), - `Expected trigger to align with the selected option on the x-axis in LTR.`); - }); - })); - - it('should align the trigger and the selected option on the x-axis in rtl', async(() => { ->>>>>>> chore: try to fix edge tests dir.value = 'rtl'; fixture.detectChanges(); @@ -1371,7 +1347,6 @@ fdescribe('MdSelect', () => { tick(400); fixture.detectChanges(); -<<<<<<< HEAD const triggerRight = trigger.getBoundingClientRect().right; const firstOptionRight = document.querySelector('.cdk-overlay-pane md-option').getBoundingClientRect().right; @@ -1494,21 +1469,6 @@ fdescribe('MdSelect', () => { expect(Math.floor(selectedOptionLeft)).toEqual(Math.floor(triggerLeft - 16)); })); -======= - fixture.whenStable().then(() => { - const triggerRight = trigger.getBoundingClientRect().right; - const firstOptionRight = - document.querySelector('.cdk-overlay-pane md-option').getBoundingClientRect().right; - - // Each option is 32px wider than the trigger, so it must be adjusted 16px - // to ensure the text overlaps correctly. - expect(firstOptionRight.toFixed(2)) - .toEqual((triggerRight + 16).toFixed(2), - `Expected trigger to align with the selected option on the x-axis in RTL.`); - }); - })); - ->>>>>>> chore: try to fix edge tests }); describe('with header', () => { From 498c569ef4ea2fe408d2d0fdaf87ae03f8b35f9e Mon Sep 17 00:00:00 2001 From: crisbeto Date: Wed, 14 Jun 2017 23:28:52 +0200 Subject: [PATCH 4/5] fix: resolve unit test failures --- src/examples/select-header/select-header-example.html | 2 +- src/lib/select/select.spec.ts | 4 ++-- src/lib/select/select.ts | 6 ++---- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/examples/select-header/select-header-example.html b/src/examples/select-header/select-header-example.html index 08636df97eff..943ca3ea29ec 100644 --- a/src/examples/select-header/select-header-example.html +++ b/src/examples/select-header/select-header-example.html @@ -1,4 +1,4 @@ - + diff --git a/src/lib/select/select.spec.ts b/src/lib/select/select.spec.ts index e93f59b69c38..add1e95c8fb2 100644 --- a/src/lib/select/select.spec.ts +++ b/src/lib/select/select.spec.ts @@ -32,7 +32,7 @@ import { import {map} from 'rxjs/operator/map'; -fdescribe('MdSelect', () => { +describe('MdSelect', () => { let overlayContainerElement: HTMLElement; let dir: {value: 'ltr'|'rtl'}; let scrolledSubject = new Subject(); @@ -984,7 +984,7 @@ fdescribe('MdSelect', () => { trigger.click(); groupFixture.detectChanges(); - const scrollContainer = document.querySelector('.cdk-overlay-pane .mat-select-panel'); + const scrollContainer = document.querySelector('.cdk-overlay-pane .mat-select-content'); // The selected option should be scrolled to the center of the panel. // This will be its original offset from the scrollTop - half the panel height + half the diff --git a/src/lib/select/select.ts b/src/lib/select/select.ts index ec7999cd2ca6..f64cffd086fa 100644 --- a/src/lib/select/select.ts +++ b/src/lib/select/select.ts @@ -810,9 +810,8 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On // we must only adjust for the height difference between the option element // and the trigger element, then multiply it by -1 to ensure the panel moves // in the correct direction up the page. - this._offsetY = (SELECT_ITEM_HEIGHT - SELECT_TRIGGER_HEIGHT) / 2 * -1; - // this._offsetY = (SELECT_OPTION_HEIGHT - SELECT_TRIGGER_HEIGHT) / 2 * -1 - - // (this.header ? SELECT_OPTION_HEIGHT : 0); + this._offsetY = (SELECT_ITEM_HEIGHT - SELECT_TRIGGER_HEIGHT) / 2 * -1 - + (this.header ? SELECT_ITEM_HEIGHT : 0); } this._checkOverlayWithinViewport(maxScroll); @@ -966,7 +965,6 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On const panelHeightTop = Math.abs(this._offsetY); const totalPanelHeight = Math.min(this._getItemCount() * SELECT_ITEM_HEIGHT, SELECT_PANEL_MAX_HEIGHT); - // Math.min(this.options.length * SELECT_OPTION_HEIGHT, SELECT_PANEL_MAX_HEIGHT); const panelHeightBottom = totalPanelHeight - panelHeightTop - triggerRect.height; if (panelHeightBottom > bottomSpaceAvailable) { From 6f6297e31f36966731736b129d348c38e31c2e6a Mon Sep 17 00:00:00 2001 From: crisbeto Date: Wed, 14 Jun 2017 23:38:07 +0200 Subject: [PATCH 5/5] chore: add license header --- src/lib/select/select-header.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lib/select/select-header.ts b/src/lib/select/select-header.ts index 0a53c7db8033..dff8e915b9c0 100644 --- a/src/lib/select/select-header.ts +++ b/src/lib/select/select-header.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Directive} from '@angular/core';