Skip to content

Commit

Permalink
fix(autocomplete): placeholder should float while panel is open (#2730)
Browse files Browse the repository at this point in the history
  • Loading branch information
kara authored Feb 3, 2017
1 parent 00df845 commit 1eebca9
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 7 deletions.
18 changes: 17 additions & 1 deletion src/lib/autocomplete/autocomplete-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
Directive,
ElementRef,
forwardRef,
Host,
Input,
NgZone,
Optional,
Expand All @@ -26,6 +27,7 @@ import 'rxjs/add/observable/of';
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/operator/switchMap';
import {MdInputContainer, FloatPlaceholderType} from '../input/input-container';

/**
* The following style constants are necessary to save here in order
Expand Down Expand Up @@ -92,7 +94,8 @@ export class MdAutocompleteTrigger implements AfterContentInit, ControlValueAcce

constructor(private _element: ElementRef, private _overlay: Overlay,
private _viewContainerRef: ViewContainerRef,
@Optional() private _dir: Dir, private _zone: NgZone) {}
@Optional() private _dir: Dir, private _zone: NgZone,
@Optional() @Host() private _inputContainer: MdInputContainer) {}

ngAfterContentInit() {
this._keyManager = new ActiveDescendantKeyManager(this.autocomplete.options).withWrap();
Expand Down Expand Up @@ -123,6 +126,7 @@ export class MdAutocompleteTrigger implements AfterContentInit, ControlValueAcce
}

this._panelOpen = true;
this._floatPlaceholder('always');
}

/** Closes the autocomplete suggestion panel. */
Expand All @@ -132,6 +136,7 @@ export class MdAutocompleteTrigger implements AfterContentInit, ControlValueAcce
}

this._panelOpen = false;
this._floatPlaceholder('auto');
}

/**
Expand Down Expand Up @@ -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
* to reveal options below the fold. First, we find the offset of the option from the top
Expand Down
33 changes: 27 additions & 6 deletions src/lib/autocomplete/autocomplete.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {MdOption} from '../core/option/option';
import {ViewportRuler} from '../core/overlay/position/viewport-ruler';
import {FakeViewportRuler} from '../core/overlay/position/fake-viewport-ruler';
import {MdAutocomplete} from './autocomplete';

import {MdInputContainer} from '../input/input-container';

describe('MdAutocomplete', () => {
let overlayContainerElement: HTMLElement;
Expand Down Expand Up @@ -181,16 +181,36 @@ describe('MdAutocomplete', () => {
.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', () => {
dir = 'rtl';

const fixture = TestBed.createComponent(SimpleAutocomplete);
fixture.detectChanges();
const rtlFixture = TestBed.createComponent(SimpleAutocomplete);
rtlFixture.detectChanges();

fixture.componentInstance.trigger.openPanel();
fixture.detectChanges();
rtlFixture.componentInstance.trigger.openPanel();
rtlFixture.detectChanges();

const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane');
expect(overlayPane.getAttribute('dir')).toEqual('rtl');
Expand Down Expand Up @@ -603,8 +623,8 @@ describe('MdAutocomplete', () => {
// Expect option bottom minus the panel height (288 - 256 = 32)
expect(scrollContainer.scrollTop).toEqual(32, `Expected panel to reveal the sixth option.`);
});
}));

}));
});

describe('aria', () => {
Expand Down Expand Up @@ -793,6 +813,7 @@ class SimpleAutocomplete implements OnDestroy {

@ViewChild(MdAutocompleteTrigger) trigger: MdAutocompleteTrigger;
@ViewChild(MdAutocomplete) panel: MdAutocomplete;
@ViewChild(MdInputContainer) inputContainer: MdInputContainer;
@ViewChildren(MdOption) options: QueryList<MdOption>;

states = [
Expand Down

0 comments on commit 1eebca9

Please sign in to comment.