mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(menu): support typeahead focus (#7385)
This commit is contained in:
committed by
Kara
parent
31e97756dd
commit
f0d20ca6cf
@@ -150,7 +150,7 @@ export class MatMenu implements AfterContentInit, MatMenuPanel, OnDestroy {
|
||||
@Inject(MAT_MENU_DEFAULT_OPTIONS) private _defaultOptions: MatMenuDefaultOptions) { }
|
||||
|
||||
ngAfterContentInit() {
|
||||
this._keyManager = new FocusKeyManager<MatMenuItem>(this.items).withWrap();
|
||||
this._keyManager = new FocusKeyManager<MatMenuItem>(this.items).withWrap().withTypeAhead();
|
||||
this._tabSubscription = this._keyManager.tabOut.subscribe(() => this.close.emit('keydown'));
|
||||
}
|
||||
|
||||
|
||||
@@ -97,5 +97,26 @@ export class MatMenuItem extends _MatMenuItemMixinBase implements FocusableOptio
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the label to be used when determining whether the option should be focused. */
|
||||
getLabel(): string {
|
||||
const element: HTMLElement = this._elementRef.nativeElement;
|
||||
let output = '';
|
||||
|
||||
if (element.childNodes) {
|
||||
const length = element.childNodes.length;
|
||||
|
||||
// Go through all the top-level text nodes and extract their text.
|
||||
// We skip anything that's not a text node to prevent the text from
|
||||
// being thrown off by something like an icon.
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (element.childNodes[i].nodeType === Node.TEXT_NODE) {
|
||||
output += element.childNodes[i].textContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output.trim();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import {
|
||||
Output,
|
||||
TemplateRef,
|
||||
ViewChild,
|
||||
ViewChildren,
|
||||
QueryList,
|
||||
} from '@angular/core';
|
||||
import {Direction, Directionality} from '@angular/cdk/bidi';
|
||||
import {OverlayContainer} from '@angular/cdk/overlay';
|
||||
@@ -21,6 +23,7 @@ import {
|
||||
MatMenuTrigger,
|
||||
MenuPositionX,
|
||||
MenuPositionY,
|
||||
MatMenuItem,
|
||||
} from './index';
|
||||
import {MENU_PANEL_TOP_PADDING} from './menu-trigger';
|
||||
import {extendObject} from '@angular/material/core';
|
||||
@@ -49,7 +52,8 @@ describe('MatMenu', () => {
|
||||
CustomMenu,
|
||||
NestedMenu,
|
||||
NestedMenuCustomElevation,
|
||||
NestedMenuRepeater
|
||||
NestedMenuRepeater,
|
||||
FakeIcon
|
||||
],
|
||||
providers: [
|
||||
{provide: OverlayContainer, useFactory: () => {
|
||||
@@ -175,6 +179,18 @@ describe('MatMenu', () => {
|
||||
expect(fixture.destroy.bind(fixture)).not.toThrow();
|
||||
});
|
||||
|
||||
it('should be able to extract the menu item text', () => {
|
||||
const fixture = TestBed.createComponent(SimpleMenu);
|
||||
fixture.detectChanges();
|
||||
expect(fixture.componentInstance.items.first.getLabel()).toBe('Item');
|
||||
});
|
||||
|
||||
it('should filter out non-text nodes when figuring out the label', () => {
|
||||
const fixture = TestBed.createComponent(SimpleMenu);
|
||||
fixture.detectChanges();
|
||||
expect(fixture.componentInstance.items.last.getLabel()).toBe('Item with an icon');
|
||||
});
|
||||
|
||||
describe('positions', () => {
|
||||
let fixture: ComponentFixture<PositionedMenu>;
|
||||
let panel: HTMLElement;
|
||||
@@ -1070,7 +1086,7 @@ describe('MatMenu default overrides', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MatMenuModule, NoopAnimationsModule],
|
||||
declarations: [SimpleMenu],
|
||||
declarations: [SimpleMenu, FakeIcon],
|
||||
providers: [{
|
||||
provide: MAT_MENU_DEFAULT_OPTIONS,
|
||||
useValue: {overlapTrigger: false, xPosition: 'before', yPosition: 'above'},
|
||||
@@ -1095,6 +1111,10 @@ describe('MatMenu default overrides', () => {
|
||||
<mat-menu class="custom-one custom-two" #menu="matMenu" (close)="closeCallback($event)">
|
||||
<button mat-menu-item> Item </button>
|
||||
<button mat-menu-item disabled> Disabled </button>
|
||||
<button mat-menu-item>
|
||||
<fake-icon>unicorn</fake-icon>
|
||||
Item with an icon
|
||||
</button>
|
||||
</mat-menu>
|
||||
`
|
||||
})
|
||||
@@ -1102,6 +1122,7 @@ class SimpleMenu {
|
||||
@ViewChild(MatMenuTrigger) trigger: MatMenuTrigger;
|
||||
@ViewChild('triggerEl') triggerEl: ElementRef;
|
||||
@ViewChild(MatMenu) menu: MatMenu;
|
||||
@ViewChildren(MatMenuItem) items: QueryList<MatMenuItem>;
|
||||
closeCallback = jasmine.createSpy('menu closed callback');
|
||||
}
|
||||
|
||||
@@ -1284,3 +1305,9 @@ class NestedMenuRepeater {
|
||||
|
||||
items = ['one', 'two', 'three'];
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'fake-icon',
|
||||
template: '<ng-content></ng-content>'
|
||||
})
|
||||
class FakeIcon { }
|
||||
|
||||
Reference in New Issue
Block a user