diff --git a/src/lib/select/select-animations.ts b/src/lib/select/select-animations.ts index 238341138c39..a2607dc34c21 100644 --- a/src/lib/select/select-animations.ts +++ b/src/lib/select/select-animations.ts @@ -20,14 +20,15 @@ import { * depending on the text direction of the application. */ export const transformPlaceholder: AnimationEntryMetadata = trigger('transformPlaceholder', [ - state('normal', style({ - transform: `translate3d(0, 0, 0) scale(1)` - })), state('floating-ltr', style({ - transform: `translate3d(-2px, -22px, 0) scale(0.75)` + top: '-22px', + left: '-2px', + transform: `scale(0.75)` })), state('floating-rtl', style({ - transform: `translate3d(2px, -22px, 0) scale(0.75)` + top: '-22px', + left: '2px', + transform: `scale(0.75)` })), transition('* => *', animate(`400ms cubic-bezier(0.25, 0.8, 0.25, 1)`)) ]); diff --git a/src/lib/select/select.html b/src/lib/select/select.html index f95dc6b1c6e8..b5efc703e8b5 100644 --- a/src/lib/select/select.html +++ b/src/lib/select/select.html @@ -1,5 +1,6 @@
- {{ placeholder }} + {{ placeholder }} {{ selected?.viewValue }}
diff --git a/src/lib/select/select.scss b/src/lib/select/select.scss index 60f1a1e49e68..2dbe50a743d0 100644 --- a/src/lib/select/select.scss +++ b/src/lib/select/select.scss @@ -30,11 +30,22 @@ md-select { } .md-select-placeholder { + position: relative; padding: 0 2px; transform-origin: left top; + &.md-floating-placeholder { + top: -22px; + left: -2px; + transform: scale(0.75); + } + [dir='rtl'] & { transform-origin: right top; + + &.md-floating-placeholder { + left: 2px; + } } // TODO: Double-check accessibility of this style diff --git a/src/lib/select/select.spec.ts b/src/lib/select/select.spec.ts index 1c0dddabc878..da8742635899 100644 --- a/src/lib/select/select.spec.ts +++ b/src/lib/select/select.spec.ts @@ -456,30 +456,35 @@ describe('MdSelect', () => { trigger = fixture.debugElement.query(By.css('.md-select-trigger')).nativeElement; }); - it('should float the placeholder when the panel is open', () => { - expect(fixture.componentInstance.select._getPlaceholderState()).toEqual('normal'); + it('should float the placeholder when the panel is open and unselected', () => { + expect(fixture.componentInstance.select._placeholderState) + .toEqual('', 'Expected placeholder to initially have a normal position.'); trigger.click(); fixture.detectChanges(); - expect(fixture.componentInstance.select._getPlaceholderState()).toEqual('floating-ltr'); + expect(fixture.componentInstance.select._placeholderState) + .toEqual('floating-ltr', 'Expected placeholder to animate up to floating position.'); const backdrop = overlayContainerElement.querySelector('.md-overlay-backdrop') as HTMLElement; backdrop.click(); fixture.detectChanges(); - expect(fixture.componentInstance.select._getPlaceholderState()).toEqual('normal'); + expect(fixture.componentInstance.select._placeholderState) + .toEqual('', 'Expected placeholder to animate back down to normal position.'); }); - it('should float the placeholder when there is a selection', () => { - trigger.click(); + it('should float the placeholder without animation when value is set', () => { + fixture.componentInstance.control.setValue('pizza-1'); fixture.detectChanges(); - const option = overlayContainerElement.querySelector('md-option') as HTMLElement; - option.click(); - fixture.detectChanges(); + const placeholderEl = + fixture.debugElement.query(By.css('.md-select-placeholder')).nativeElement; - expect(fixture.componentInstance.select._getPlaceholderState()).toEqual('floating-ltr'); + expect(placeholderEl.classList) + .toContain('md-floating-placeholder', 'Expected placeholder to display as floating.'); + expect(fixture.componentInstance.select._placeholderState) + .toEqual('', 'Expected animation state to be empty to avoid animation.'); }); it('should use the floating-rtl state when the dir is rtl', () => { @@ -487,7 +492,7 @@ describe('MdSelect', () => { trigger.click(); fixture.detectChanges(); - expect(fixture.componentInstance.select._getPlaceholderState()).toEqual('floating-rtl'); + expect(fixture.componentInstance.select._placeholderState).toEqual('floating-rtl'); }); it('should use the ltr panel state when the dir is ltr', () => { diff --git a/src/lib/select/select.ts b/src/lib/select/select.ts index 90af646afe68..60757fac126b 100644 --- a/src/lib/select/select.ts +++ b/src/lib/select/select.ts @@ -71,6 +71,9 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr /** Whether the select is disabled. */ private _disabled: boolean = false; + /** The animation state of the placeholder. */ + _placeholderState = ''; + /** Manages keyboard events for options in the panel. */ _keyManager: ListKeyManager; @@ -163,12 +166,16 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr if (this.disabled) { return; } + this._placeholderState = this._isRtl() ? 'floating-rtl' : 'floating-ltr'; this._panelOpen = true; } /** Closes the overlay panel and focuses the host element. */ close(): void { this._panelOpen = false; + if (!this._selected) { + this._placeholderState = ''; + } this._focusHost(); } @@ -182,7 +189,7 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr // the select's child options have been created. It's necessary to call // writeValue() again after the options have been created to ensure any // initial view value is set. - this._ngZone.onStable.first().subscribe(() => this.writeValue(value)); + this._ngZone.onMicrotaskEmpty.first().subscribe(() => this.writeValue(value)); return; } @@ -240,15 +247,6 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr return this.trigger.nativeElement.getBoundingClientRect().width; } - /** The animation state of the placeholder. */ - _getPlaceholderState(): string { - if (this.panelOpen || this.selected) { - return this._isRtl() ? 'floating-rtl' : 'floating-ltr'; - } else { - return 'normal'; - } - } - /** The animation state of the overlay panel. */ _getPanelState(): string { return this._isRtl() ? `${this._transformOrigin}-rtl` : `${this._transformOrigin}-ltr`; @@ -340,7 +338,9 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr private _onSelect(option: MdOption): void { this._selected = option; this._updateOptions(); - this.close(); + if (this.panelOpen) { + this.close(); + } } /** Deselect each option that doesn't match the current selection. */