Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/lib/menu/menu-directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'));
}

Expand Down
21 changes: 21 additions & 0 deletions src/lib/menu/menu-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

}

31 changes: 29 additions & 2 deletions src/lib/menu/menu.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
Expand Down Expand Up @@ -49,7 +52,8 @@ describe('MatMenu', () => {
CustomMenu,
NestedMenu,
NestedMenuCustomElevation,
NestedMenuRepeater
NestedMenuRepeater,
FakeIcon
],
providers: [
{provide: OverlayContainer, useFactory: () => {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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'},
Expand All @@ -1095,13 +1111,18 @@ 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>
`
})
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');
}

Expand Down Expand Up @@ -1284,3 +1305,9 @@ class NestedMenuRepeater {

items = ['one', 'two', 'three'];
}

@Component({
selector: 'fake-icon',
template: '<ng-content></ng-content>'
})
class FakeIcon { }