fix(autocomplete): prevent opening on load in IE (#3190)

Prevents the autocomplete being opened on load in IE. This was due to a bug where IE fires the `input` event on `load`, `focus` and `blur`, if the input element has a placeholder.

Fixes #3183.
This commit is contained in:
Kristiyan Kostadinov
2017-02-23 01:59:09 +01:00
committed by mmalerba
parent d744a5f0d5
commit a4da08b065
2 changed files with 50 additions and 33 deletions
+9 -4
View File
@@ -61,7 +61,7 @@ export const MD_AUTOCOMPLETE_VALUE_ACCESSOR: any = {
'[attr.aria-owns]': 'autocomplete?.id',
'(focus)': 'openPanel()',
'(blur)': '_handleBlur($event.relatedTarget?.tagName)',
'(input)': '_handleInput($event.target.value)',
'(input)': '_handleInput($event)',
'(keydown)': '_handleKeydown($event)',
},
providers: [MD_AUTOCOMPLETE_VALUE_ACCESSOR]
@@ -213,9 +213,14 @@ export class MdAutocompleteTrigger implements AfterContentInit, ControlValueAcce
}
}
_handleInput(value: string): void {
this._onChange(value);
this.openPanel();
_handleInput(event: KeyboardEvent): void {
// We need to ensure that the input is focused, because IE will fire the `input`
// event on focus/blur/load if the input has a placeholder. See:
// https://connect.microsoft.com/IE/feedback/details/885747/
if (document.activeElement === event.target) {
this._onChange((event.target as HTMLInputElement).value);
this.openPanel();
}
}
_handleBlur(newlyFocusedTag: string): void {
+41 -29
View File
@@ -124,8 +124,7 @@ describe('MdAutocomplete', () => {
fixture.whenStable().then(() => {
// Filter down the option list to a subset of original options ('Alabama', 'California')
input.value = 'al';
dispatchEvent('input', input);
typeInElement('al', input);
fixture.detectChanges();
let options =
@@ -134,8 +133,7 @@ describe('MdAutocomplete', () => {
// Changing value from 'Alabama' to 'al' to re-populate the option list,
// ensuring that 'California' is created new.
input.value = 'al';
dispatchEvent('input', input);
typeInElement('al', input);
fixture.detectChanges();
fixture.whenStable().then(() => {
@@ -177,8 +175,7 @@ describe('MdAutocomplete', () => {
.toContain('mat-autocomplete-visible', `Expected panel to start out visible.`);
// Filter down the option list such that no options match the value
input.value = 'af';
dispatchEvent('input', input);
typeInElement('af', input);
fixture.detectChanges();
fixture.whenStable().then(() => {
@@ -210,6 +207,18 @@ describe('MdAutocomplete', () => {
});
}));
it('should not open the panel when the `input` event is invoked on a non-focused input', () => {
expect(fixture.componentInstance.trigger.panelOpen)
.toBe(false, `Expected panel state to start out closed.`);
input.value = 'Alabama';
dispatchEvent('input', input);
fixture.detectChanges();
expect(fixture.componentInstance.trigger.panelOpen)
.toBe(false, `Expected panel state to stay closed.`);
});
});
it('should have the correct text direction in RTL', () => {
@@ -241,15 +250,13 @@ describe('MdAutocomplete', () => {
fixture.componentInstance.trigger.openPanel();
fixture.detectChanges();
input.value = 'a';
dispatchEvent('input', input);
typeInElement('a', input);
fixture.detectChanges();
expect(fixture.componentInstance.stateCtrl.value)
.toEqual('a', 'Expected control value to be updated as user types.');
input.value = 'al';
dispatchEvent('input', input);
typeInElement('al', input);
fixture.detectChanges();
expect(fixture.componentInstance.stateCtrl.value)
@@ -282,8 +289,7 @@ describe('MdAutocomplete', () => {
options[1].click();
fixture.detectChanges();
input.value = 'Californi';
dispatchEvent('input', input);
typeInElement('Californi', input);
fixture.detectChanges();
expect(fixture.componentInstance.stateCtrl.value)
@@ -339,8 +345,7 @@ describe('MdAutocomplete', () => {
}));
it('should clear the text field if value is reset programmatically', async(() => {
input.value = 'Alabama';
dispatchEvent('input', input);
typeInElement('Alabama', input);
fixture.detectChanges();
fixture.whenStable().then(() => {
@@ -376,8 +381,7 @@ describe('MdAutocomplete', () => {
expect(fixture.componentInstance.stateCtrl.dirty)
.toBe(false, `Expected control to start out pristine.`);
input.value = 'a';
dispatchEvent('input', input);
typeInElement('a', input);
fixture.detectChanges();
expect(fixture.componentInstance.stateCtrl.dirty)
@@ -531,8 +535,7 @@ describe('MdAutocomplete', () => {
fixture.detectChanges();
fixture.whenStable().then(() => {
input.value = 'o';
dispatchEvent('input', input);
typeInElement('o', input);
fixture.detectChanges();
fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT);
@@ -565,8 +568,7 @@ describe('MdAutocomplete', () => {
it('should fill the text field, not select an option, when SPACE is entered', async(() => {
fixture.whenStable().then(() => {
input.value = 'New';
dispatchEvent('input', input);
typeInElement('New', input);
fixture.detectChanges();
const SPACE_EVENT = new FakeKeyboardEvent(SPACE) as KeyboardEvent;
@@ -604,8 +606,7 @@ describe('MdAutocomplete', () => {
expect(overlayContainerElement.textContent)
.toEqual('', `Expected panel to close after ENTER key.`);
input.value = 'Alabam';
dispatchEvent('input', input);
typeInElement('Alabama', input);
fixture.detectChanges();
expect(fixture.componentInstance.trigger.panelOpen)
@@ -782,8 +783,7 @@ describe('MdAutocomplete', () => {
fixture.detectChanges();
fixture.whenStable().then(() => {
input.value = 'f';
dispatchEvent('input', input);
typeInElement('f', input);
fixture.detectChanges();
const inputTop = input.getBoundingClientRect().top;
@@ -808,8 +808,7 @@ describe('MdAutocomplete', () => {
fixture.detectChanges();
const input = fixture.debugElement.query(By.css('input')).nativeElement;
input.value = 'd';
dispatchEvent('input', input);
typeInElement('d', input);
fixture.detectChanges();
const options =
@@ -826,7 +825,7 @@ describe('MdAutocomplete', () => {
<md-input-container>
<input mdInput placeholder="State" [mdAutocomplete]="auto" [formControl]="stateCtrl">
</md-input-container>
<md-autocomplete #auto="mdAutocomplete" [displayWith]="displayFn">
<md-option *ngFor="let state of filteredStates" [value]="state">
<span> {{ state.code }}: {{ state.name }} </span>
@@ -881,10 +880,10 @@ class SimpleAutocomplete implements OnDestroy {
@Component({
template: `
<md-input-container>
<input mdInput placeholder="State" [mdAutocomplete]="auto"
<input mdInput placeholder="State" [mdAutocomplete]="auto"
(input)="onInput($event.target?.value)">
</md-input-container>
<md-autocomplete #auto="mdAutocomplete">
<md-option *ngFor="let state of filteredStates" [value]="state">
<span> {{ state }} </span>
@@ -920,6 +919,19 @@ function dispatchEvent(eventName: string, element: HTMLElement): void {
element.dispatchEvent(event);
}
/**
* Focuses an input, sets its value and dispatches
* the `input` event, simulating the user typing.
* @param value Value to be set on the input.
* @param element Element onto which to set the value.
*/
function typeInElement(value: string, element: HTMLInputElement) {
element.focus();
element.value = value;
dispatchEvent('input', element);
}
/** This is a mock keyboard event to test keyboard events in the autocomplete. */
class FakeKeyboardEvent {
constructor(public keyCode: number) {}