Skip to content

Commit

Permalink
feat(autocomplete): allow autocomplete panel to be disabled (#11142)
Browse files Browse the repository at this point in the history
  • Loading branch information
crisbeto authored and josephperrott committed May 4, 2018
1 parent ab1204d commit e8bc0e9
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 6 deletions.
1 change: 1 addition & 0 deletions src/lib/autocomplete/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ ng_module(
"//src/cdk/keycodes",
"//src/cdk/portal",
"//src/cdk/overlay",
"//src/cdk/coercion",
],
tsconfig = "//src/lib:tsconfig-build.json",
)
Expand Down
22 changes: 17 additions & 5 deletions src/lib/autocomplete/autocomplete-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';


/**
Expand Down Expand Up @@ -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()',
Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}

}
32 changes: 31 additions & 1 deletion src/lib/autocomplete/autocomplete.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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"
Expand All @@ -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');

Expand Down

0 comments on commit e8bc0e9

Please sign in to comment.