feat(autocomplete): allow autocomplete panel to be disabled (#11142)

This commit is contained in:
Kristiyan Kostadinov
2018-05-04 21:08:56 +02:00
committed by Joey Perrott
parent ab1204d268
commit e8bc0e9186
3 changed files with 49 additions and 6 deletions
+1
View File
@@ -17,6 +17,7 @@ ng_module(
"//src/cdk/keycodes",
"//src/cdk/portal",
"//src/cdk/overlay",
"//src/cdk/coercion",
],
tsconfig = "//src/lib:tsconfig-build.json",
)
+17 -5
View File
@@ -44,6 +44,7 @@ import {
import {MatFormField} from '@angular/material/form-field';
import {Subscription, defer, fromEvent, merge, of as observableOf, Subject, Observable} from 'rxjs';
import {MatAutocomplete} from './autocomplete';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
/**
@@ -93,12 +94,12 @@ export function getMatAutocompleteMissingPanelError(): Error {
@Directive({
selector: `input[matAutocomplete], textarea[matAutocomplete]`,
host: {
'role': 'combobox',
'autocomplete': 'off',
'aria-autocomplete': 'list',
'[attr.role]': 'autocompleteDisabled ? null : "combobox"',
'[attr.aria-autocomplete]': 'autocompleteDisabled ? null : "list"',
'[attr.aria-activedescendant]': 'activeOption?.id',
'[attr.aria-expanded]': 'panelOpen.toString()',
'[attr.aria-owns]': 'autocomplete?.id',
'[attr.aria-expanded]': 'autocompleteDisabled ? null : panelOpen.toString()',
'[attr.aria-owns]': 'autocompleteDisabled ? null : autocomplete?.id',
// Note: we use `focusin`, as opposed to `focus`, in order to open the panel
// a little earlier. This avoids issues where IE delays the focusing of the input.
'(focusin)': '_handleFocus()',
@@ -113,6 +114,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
private _overlayRef: OverlayRef | null;
private _portal: TemplatePortal;
private _componentDestroyed = false;
private _autocompleteDisabled = false;
/** Old value of the native input. Used to work around issues with the `input` event on IE. */
private _previousValue: string | number | null;
@@ -141,6 +143,16 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
/** The autocomplete panel to be attached to this trigger. */
@Input('matAutocomplete') autocomplete: MatAutocomplete;
/**
* Whether the autocomplete is disabled. When disabled, the element will
* act as a regular input and the user won't be able to open the panel.
*/
@Input('matAutocompleteDisabled')
get autocompleteDisabled(): boolean { return this._autocompleteDisabled; }
set autocompleteDisabled(value: boolean) {
this._autocompleteDisabled = coerceBooleanProperty(value);
}
constructor(private _element: ElementRef, private _overlay: Overlay,
private _viewContainerRef: ViewContainerRef,
private _zone: NgZone,
@@ -569,7 +581,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
/** Determines whether the panel can be opened. */
private _canOpen(): boolean {
const element: HTMLInputElement = this._element.nativeElement;
return !element.readOnly && !element.disabled;
return !element.readOnly && !element.disabled && !this._autocompleteDisabled;
}
}
+31 -1
View File
@@ -455,6 +455,20 @@ describe('MatAutocomplete', () => {
expect(fixture.componentInstance.closedSpy).not.toHaveBeenCalled();
});
it('should not be able to open the panel if the autocomplete is disabled', () => {
expect(fixture.componentInstance.trigger.panelOpen)
.toBe(false, `Expected panel state to start out closed.`);
fixture.componentInstance.autocompleteDisabled = true;
fixture.detectChanges();
dispatchFakeEvent(input, 'focusin');
fixture.detectChanges();
expect(fixture.componentInstance.trigger.panelOpen)
.toBe(false, `Expected panel to remain closed.`);
});
});
it('should have the correct text direction in RTL', () => {
@@ -1300,6 +1314,16 @@ describe('MatAutocomplete', () => {
expect(document.activeElement).toBe(input, 'Expected focus to be restored to the input.');
}));
it('should remove autocomplete-specific aria attributes when autocomplete is disabled', () => {
fixture.componentInstance.autocompleteDisabled = true;
fixture.detectChanges();
expect(input.getAttribute('role')).toBeFalsy();
expect(input.getAttribute('aria-autocomplete')).toBeFalsy();
expect(input.getAttribute('aria-expanded')).toBeFalsy();
expect(input.getAttribute('aria-owns')).toBeFalsy();
});
});
describe('Fallback positions', () => {
@@ -1959,7 +1983,12 @@ describe('MatAutocomplete', () => {
@Component({
template: `
<mat-form-field [floatLabel]="floatLabel" [style.width.px]="width">
<input matInput placeholder="State" [matAutocomplete]="auto" [formControl]="stateCtrl">
<input
matInput
placeholder="State"
[matAutocomplete]="auto"
[matAutocompleteDisabled]="autocompleteDisabled"
[formControl]="stateCtrl">
</mat-form-field>
<mat-autocomplete class="class-one class-two" #auto="matAutocomplete" [displayWith]="displayFn"
@@ -1977,6 +2006,7 @@ class SimpleAutocomplete implements OnDestroy {
floatLabel = 'auto';
width: number;
disableRipple = false;
autocompleteDisabled = false;
openedSpy = jasmine.createSpy('autocomplete opened spy');
closedSpy = jasmine.createSpy('autocomplete closed spy');