fix(autocomplete): placeholder should float while panel is open (#2730)

This commit is contained in:
Kara
2017-02-02 18:31:37 -08:00
parent 18969f4698
commit eec4dc60db
2 changed files with 44 additions and 7 deletions
+17 -1
View File
@@ -3,6 +3,7 @@ import {
Directive, Directive,
ElementRef, ElementRef,
forwardRef, forwardRef,
Host,
Input, Input,
NgZone, NgZone,
Optional, Optional,
@@ -26,6 +27,7 @@ import 'rxjs/add/observable/of';
import 'rxjs/add/observable/merge'; import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/startWith'; import 'rxjs/add/operator/startWith';
import 'rxjs/add/operator/switchMap'; import 'rxjs/add/operator/switchMap';
import {MdInputContainer, FloatPlaceholderType} from '../input/input-container';
/** /**
* The following style constants are necessary to save here in order * The following style constants are necessary to save here in order
@@ -92,7 +94,8 @@ export class MdAutocompleteTrigger implements AfterContentInit, ControlValueAcce
constructor(private _element: ElementRef, private _overlay: Overlay, constructor(private _element: ElementRef, private _overlay: Overlay,
private _viewContainerRef: ViewContainerRef, private _viewContainerRef: ViewContainerRef,
@Optional() private _dir: Dir, private _zone: NgZone) {} @Optional() private _dir: Dir, private _zone: NgZone,
@Optional() @Host() private _inputContainer: MdInputContainer) {}
ngAfterContentInit() { ngAfterContentInit() {
this._keyManager = new ActiveDescendantKeyManager(this.autocomplete.options).withWrap(); this._keyManager = new ActiveDescendantKeyManager(this.autocomplete.options).withWrap();
@@ -123,6 +126,7 @@ export class MdAutocompleteTrigger implements AfterContentInit, ControlValueAcce
} }
this._panelOpen = true; this._panelOpen = true;
this._floatPlaceholder('always');
} }
/** Closes the autocomplete suggestion panel. */ /** Closes the autocomplete suggestion panel. */
@@ -132,6 +136,7 @@ export class MdAutocompleteTrigger implements AfterContentInit, ControlValueAcce
} }
this._panelOpen = false; this._panelOpen = false;
this._floatPlaceholder('auto');
} }
/** /**
@@ -214,6 +219,17 @@ export class MdAutocompleteTrigger implements AfterContentInit, ControlValueAcce
} }
} }
/**
* In "auto" mode, the placeholder will animate down as soon as focus is lost.
* This causes the value to jump when selecting an option with the mouse.
* This method manually floats the placeholder until the panel can be closed.
*/
private _floatPlaceholder(state: FloatPlaceholderType): void {
if (this._inputContainer) {
this._inputContainer.floatPlaceholder = state;
}
}
/** /**
* Given that we are not actually focusing active options, we must manually adjust scroll * Given that we are not actually focusing active options, we must manually adjust scroll
* to reveal options below the fold. First, we find the offset of the option from the top * to reveal options below the fold. First, we find the offset of the option from the top
+27 -6
View File
@@ -12,7 +12,7 @@ import {MdOption} from '../core/option/option';
import {ViewportRuler} from '../core/overlay/position/viewport-ruler'; import {ViewportRuler} from '../core/overlay/position/viewport-ruler';
import {FakeViewportRuler} from '../core/overlay/position/fake-viewport-ruler'; import {FakeViewportRuler} from '../core/overlay/position/fake-viewport-ruler';
import {MdAutocomplete} from './autocomplete'; import {MdAutocomplete} from './autocomplete';
import {MdInputContainer} from '../input/input-container';
describe('MdAutocomplete', () => { describe('MdAutocomplete', () => {
let overlayContainerElement: HTMLElement; let overlayContainerElement: HTMLElement;
@@ -181,16 +181,36 @@ describe('MdAutocomplete', () => {
.toEqual('', `Expected panel to close when options list is empty.`); .toEqual('', `Expected panel to close when options list is empty.`);
}); });
})); }));
it('should keep the label floating until the panel closes', () => {
fixture.componentInstance.trigger.openPanel();
fixture.detectChanges();
dispatchEvent('blur', input);
fixture.detectChanges();
expect(fixture.componentInstance.inputContainer.floatPlaceholder)
.toEqual('always', 'Expected placeholder to keep floating on blur.');
const backdrop =
overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement;
backdrop.click();
fixture.detectChanges();
expect(fixture.componentInstance.inputContainer.floatPlaceholder)
.toEqual('auto', 'Expected placeholder to return to auto state after panel closes.');
});
}); });
it('should have the correct text direction in RTL', () => { it('should have the correct text direction in RTL', () => {
dir = 'rtl'; dir = 'rtl';
const fixture = TestBed.createComponent(SimpleAutocomplete); const rtlFixture = TestBed.createComponent(SimpleAutocomplete);
fixture.detectChanges(); rtlFixture.detectChanges();
fixture.componentInstance.trigger.openPanel(); rtlFixture.componentInstance.trigger.openPanel();
fixture.detectChanges(); rtlFixture.detectChanges();
const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane'); const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane');
expect(overlayPane.getAttribute('dir')).toEqual('rtl'); expect(overlayPane.getAttribute('dir')).toEqual('rtl');
@@ -603,8 +623,8 @@ describe('MdAutocomplete', () => {
// Expect option bottom minus the panel height (288 - 256 = 32) // Expect option bottom minus the panel height (288 - 256 = 32)
expect(scrollContainer.scrollTop).toEqual(32, `Expected panel to reveal the sixth option.`); expect(scrollContainer.scrollTop).toEqual(32, `Expected panel to reveal the sixth option.`);
}); });
}));
}));
}); });
describe('aria', () => { describe('aria', () => {
@@ -793,6 +813,7 @@ class SimpleAutocomplete implements OnDestroy {
@ViewChild(MdAutocompleteTrigger) trigger: MdAutocompleteTrigger; @ViewChild(MdAutocompleteTrigger) trigger: MdAutocompleteTrigger;
@ViewChild(MdAutocomplete) panel: MdAutocomplete; @ViewChild(MdAutocomplete) panel: MdAutocomplete;
@ViewChild(MdInputContainer) inputContainer: MdInputContainer;
@ViewChildren(MdOption) options: QueryList<MdOption>; @ViewChildren(MdOption) options: QueryList<MdOption>;
states = [ states = [