Skip to content

Commit 2a5529a

Browse files
committed
feat(menu): Allow menu-trigger to take a menu interface.
This should enable menus to be more easily extended for custom menu implementations.
1 parent ea6c817 commit 2a5529a

File tree

5 files changed

+79
-11
lines changed

5 files changed

+79
-11
lines changed

src/lib/menu/menu-directive.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {MenuPositionX, MenuPositionY} from './menu-positions';
1616
import {MdMenuInvalidPositionX, MdMenuInvalidPositionY} from './menu-errors';
1717
import {MdMenuItem} from './menu-item';
1818
import {ListKeyManager} from '../core/a11y/list-key-manager';
19+
import {MdMenuPanel} from './menu-panel';
1920

2021
@Component({
2122
moduleId: module.id,
@@ -26,7 +27,7 @@ import {ListKeyManager} from '../core/a11y/list-key-manager';
2627
encapsulation: ViewEncapsulation.None,
2728
exportAs: 'mdMenu'
2829
})
29-
export class MdMenu {
30+
export class MdMenu implements MdMenuPanel {
3031
private _keyManager: ListKeyManager;
3132

3233
// config object to be passed into the menu's ngClass
@@ -70,7 +71,7 @@ export class MdMenu {
7071
* to focus the first item when the menu is opened by the ENTER key.
7172
* TODO: internal
7273
*/
73-
_focusFirstItem() {
74+
focusFirstItem() {
7475
// The menu always opens with the first item focused.
7576
this.items.first.focus();
7677
this._keyManager.focusedItemIndex = 0;

src/lib/menu/menu-panel.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import {EventEmitter, TemplateRef} from '@angular/core';
2+
import {MenuPositionX, MenuPositionY} from './menu-positions';
3+
4+
export interface MdMenuPanel {
5+
positionX: MenuPositionX;
6+
positionY: MenuPositionY;
7+
templateRef: TemplateRef<any>;
8+
close: EventEmitter<void>;
9+
focusFirstItem: () => void;
10+
}

src/lib/menu/menu-trigger.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
OnDestroy,
1111
Renderer
1212
} from '@angular/core';
13-
import {MdMenu} from './menu-directive';
13+
import {MdMenuPanel} from './menu-panel';
1414
import {MdMenuMissingError} from './menu-errors';
1515
import {
1616
ENTER,
@@ -47,7 +47,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
4747
// the first item of the list when the menu is opened via the keyboard
4848
private _openedFromKeyboard: boolean = false;
4949

50-
@Input('md-menu-trigger-for') menu: MdMenu;
50+
@Input('md-menu-trigger-for') menu: MdMenuPanel;
5151
@Output() onMenuOpen = new EventEmitter<void>();
5252
@Output() onMenuClose = new EventEmitter<void>();
5353

@@ -120,7 +120,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
120120
this._setIsMenuOpen(true);
121121

122122
if (this._openedFromKeyboard) {
123-
this.menu._focusFirstItem();
123+
this.menu.focusFirstItem();
124124
}
125125
};
126126

@@ -148,7 +148,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
148148
* md-menu-trigger-for. If not, an exception is thrown.
149149
*/
150150
private _checkMenu() {
151-
if (!this.menu || !(this.menu instanceof MdMenu)) {
151+
if (!this.menu) {
152152
throw new MdMenuMissingError();
153153
}
154154
}

src/lib/menu/menu.spec.ts

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
import {TestBed, async} from '@angular/core/testing';
2-
import {Component, ViewChild} from '@angular/core';
3-
import {MdMenuModule, MdMenuTrigger} from './menu';
2+
import {
3+
Component,
4+
EventEmitter,
5+
Output,
6+
TemplateRef,
7+
ViewChild
8+
} from '@angular/core';
9+
import {
10+
MdMenuModule,
11+
MdMenuTrigger,
12+
MdMenuPanel,
13+
MenuPositionX,
14+
MenuPositionY
15+
} from './menu';
416
import {OverlayContainer} from '../core/overlay/overlay-container';
517

618

@@ -10,7 +22,7 @@ describe('MdMenu', () => {
1022
beforeEach(async(() => {
1123
TestBed.configureTestingModule({
1224
imports: [MdMenuModule.forRoot()],
13-
declarations: [SimpleMenu],
25+
declarations: [CustomMenuPanel, CustomMenu, SimpleMenu],
1426
providers: [
1527
{provide: OverlayContainer, useFactory: () => {
1628
overlayContainerElement = document.createElement('div');
@@ -30,7 +42,7 @@ describe('MdMenu', () => {
3042
fixture.componentInstance.trigger.openMenu();
3143
fixture.componentInstance.trigger.openMenu();
3244

33-
expect(overlayContainerElement.textContent.trim()).toBe('Content');
45+
expect(overlayContainerElement.textContent.trim()).toBe('Simple Content');
3446
}).not.toThrowError();
3547
});
3648

@@ -46,16 +58,59 @@ describe('MdMenu', () => {
4658
expect(overlayContainerElement.textContent).toBe('');
4759
});
4860

61+
it('should open a custom menu', () => {
62+
const fixture = TestBed.createComponent(CustomMenu);
63+
fixture.detectChanges();
64+
expect(overlayContainerElement.textContent).toBe('');
65+
expect(() => {
66+
fixture.componentInstance.trigger.openMenu();
67+
fixture.componentInstance.trigger.openMenu();
68+
69+
expect(overlayContainerElement.textContent).toContain('Custom Menu header');
70+
expect(overlayContainerElement.textContent).toContain('Custom Content');
71+
}).not.toThrowError();
72+
});
73+
4974
});
5075

5176
@Component({
5277
template: `
5378
<button [md-menu-trigger-for]="menu">Toggle menu</button>
5479
<md-menu #menu="mdMenu">
55-
<button md-menu-item> Content </button>
80+
<button md-menu-item> Simple Content </button>
5681
</md-menu>
5782
`
5883
})
5984
class SimpleMenu {
6085
@ViewChild(MdMenuTrigger) trigger: MdMenuTrigger;
6186
}
87+
88+
@Component({
89+
selector: 'custom-menu',
90+
template: `
91+
<template>
92+
Custom Menu header
93+
<ng-content></ng-content>
94+
</template>
95+
`,
96+
exportAs: 'mdCustomMenu'
97+
})
98+
class CustomMenuPanel implements MdMenuPanel {
99+
positionX: MenuPositionX = 'after';
100+
positionY: MenuPositionY = 'below';
101+
@ViewChild(TemplateRef) templateRef: TemplateRef<any>;
102+
@Output() close = new EventEmitter<void>();
103+
focusFirstItem: () => void;
104+
}
105+
106+
@Component({
107+
template: `
108+
<button [md-menu-trigger-for]="menu">Toggle menu</button>
109+
<custom-menu #menu="mdCustomMenu">
110+
<button md-menu-item> Custom Content </button>
111+
</custom-menu>
112+
`
113+
})
114+
class CustomMenu {
115+
@ViewChild(MdMenuTrigger) trigger: MdMenuTrigger;
116+
}

src/lib/menu/menu.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {MdMenuTrigger} from './menu-trigger';
77
export {MdMenu} from './menu-directive';
88
export {MdMenuItem} from './menu-item';
99
export {MdMenuTrigger} from './menu-trigger';
10+
export {MdMenuPanel} from './menu-panel';
11+
export {MenuPositionX, MenuPositionY} from './menu-positions';
1012

1113

1214
@NgModule({

0 commit comments

Comments
 (0)