Skip to content

Commit

Permalink
feat(cdk/menu): add support for passing in data to the menu template (#…
Browse files Browse the repository at this point in the history
…25778)

Adds the `cdkMenuTriggerData` and `cdkContextMenuTriggerData` inputs that allow the consumer of a CDK menu to pass context data to the menu template.

Fixes #25708.
  • Loading branch information
crisbeto committed Oct 12, 2022
1 parent a2b3968 commit a2cf3f6
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 5 deletions.
29 changes: 29 additions & 0 deletions src/cdk/menu/context-menu-trigger.spec.ts
Expand Up @@ -422,6 +422,21 @@ describe('CdkContextMenuTrigger', () => {
expect(fixture.componentInstance.menus.length).toBe(1);
});
});

it('should be able to pass data to the menu via the template context', () => {
TestBed.configureTestingModule({
imports: [CdkMenuModule],
declarations: [ContextTriggerWithData],
}).compileComponents();

const fixture = TestBed.createComponent(ContextTriggerWithData);
fixture.componentInstance.menuData = {message: 'Hello!'};
fixture.detectChanges();
dispatchMouseEvent(fixture.componentInstance.triggerElement.nativeElement, 'contextmenu');
fixture.detectChanges();

expect(document.querySelector('.test-menu')?.textContent).toBe('Hello!');
});
});

@Component({
Expand Down Expand Up @@ -552,3 +567,17 @@ class MenuBarAndContextTriggerShareMenu {
@ViewChild(CdkContextMenuTrigger) contextTrigger: CdkContextMenuTrigger;
@ViewChildren(CdkMenu) menus: QueryList<CdkMenu>;
}

@Component({
template: `
<div [cdkContextMenuTriggerFor]="context" [cdkContextMenuTriggerData]="menuData"></div>
<ng-template #context let-message="message">
<div cdkMenu class="test-menu">{{message}}</div>
</ng-template>
`,
})
class ContextTriggerWithData {
@ViewChild(CdkContextMenuTrigger, {read: ElementRef}) triggerElement: ElementRef<HTMLElement>;
menuData: unknown;
}
6 changes: 5 additions & 1 deletion src/cdk/menu/context-menu-trigger.ts
Expand Up @@ -61,7 +61,11 @@ export type ContextMenuCoordinates = {x: number; y: number};
'[attr.data-cdk-menu-stack-id]': 'null',
'(contextmenu)': '_openOnContextMenu($event)',
},
inputs: ['menuTemplateRef: cdkContextMenuTriggerFor', 'menuPosition: cdkContextMenuPosition'],
inputs: [
'menuTemplateRef: cdkContextMenuTriggerFor',
'menuPosition: cdkContextMenuPosition',
'menuData: cdkContextMenuTriggerData',
],
outputs: ['opened: cdkContextMenuOpened', 'closed: cdkContextMenuClosed'],
providers: [
{provide: MENU_TRIGGER, useExisting: CdkContextMenuTrigger},
Expand Down
5 changes: 4 additions & 1 deletion src/cdk/menu/menu-trigger-base.ts
Expand Up @@ -60,6 +60,9 @@ export abstract class CdkMenuTriggerBase implements OnDestroy {
/** Template reference variable to the menu this trigger opens */
menuTemplateRef: TemplateRef<unknown>;

/** Context data to be passed along to the menu template */
menuData: unknown;

/** A reference to the overlay which manages the triggered menu */
protected overlayRef: OverlayRef | null = null;

Expand Down Expand Up @@ -105,7 +108,7 @@ export abstract class CdkMenuTriggerBase implements OnDestroy {
this._menuPortal = new TemplatePortal(
this.menuTemplateRef,
this.viewContainerRef,
undefined,
this.menuData,
this._getChildMenuInjector(),
);
}
Expand Down
30 changes: 30 additions & 0 deletions src/cdk/menu/menu-trigger.spec.ts
Expand Up @@ -454,6 +454,21 @@ describe('MenuTrigger', () => {
expect(nativeMenus.length).toBe(1);
});
});

it('should be able to pass data to the menu via the template context', () => {
TestBed.configureTestingModule({
imports: [CdkMenuModule],
declarations: [TriggerWithData],
}).compileComponents();

const fixture = TestBed.createComponent(TriggerWithData);
fixture.componentInstance.menuData = {message: 'Hello!'};
fixture.detectChanges();
fixture.nativeElement.querySelector('button').click();
fixture.detectChanges();

expect(document.querySelector('.test-menu')?.textContent).toBe('Hello!');
});
});

@Component({
Expand Down Expand Up @@ -572,3 +587,18 @@ class StandaloneTriggerWithInlineMenu {
@ViewChild('inline_item', {read: ElementRef}) nativeInlineItem: ElementRef<HTMLElement>;
@ViewChildren(CdkMenu, {read: ElementRef}) nativeMenus: QueryList<ElementRef>;
}

@Component({
template: `
<button
[cdkMenuTriggerFor]="menu"
[cdkMenuTriggerData]="menuData">Click me!</button>
<ng-template #menu let-message="message">
<div cdkMenu class="test-menu">{{message}}</div>
</ng-template>
`,
})
class TriggerWithData {
menuData: unknown;
}
6 changes: 5 additions & 1 deletion src/cdk/menu/menu-trigger.ts
Expand Up @@ -51,7 +51,11 @@ import {CdkMenuTriggerBase, MENU_TRIGGER} from './menu-trigger-base';
'(keydown)': '_toggleOnKeydown($event)',
'(click)': 'toggle()',
},
inputs: ['menuTemplateRef: cdkMenuTriggerFor', 'menuPosition: cdkMenuPosition'],
inputs: [
'menuTemplateRef: cdkMenuTriggerFor',
'menuPosition: cdkMenuPosition',
'menuData: cdkMenuTriggerData',
],
outputs: ['opened: cdkMenuOpened', 'closed: cdkMenuClosed'],
providers: [
{provide: MENU_TRIGGER, useExisting: CdkMenuTrigger},
Expand Down
5 changes: 3 additions & 2 deletions tools/public_api_guard/cdk/menu.md
Expand Up @@ -40,7 +40,7 @@ export class CdkContextMenuTrigger extends CdkMenuTriggerBase implements OnDestr
open(coordinates: ContextMenuCoordinates): void;
_openOnContextMenu(event: MouseEvent): void;
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<CdkContextMenuTrigger, "[cdkContextMenuTriggerFor]", ["cdkContextMenuTriggerFor"], { "menuTemplateRef": "cdkContextMenuTriggerFor"; "menuPosition": "cdkContextMenuPosition"; "disabled": "cdkContextMenuDisabled"; }, { "opened": "cdkContextMenuOpened"; "closed": "cdkContextMenuClosed"; }, never, never, false, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<CdkContextMenuTrigger, "[cdkContextMenuTriggerFor]", ["cdkContextMenuTriggerFor"], { "menuTemplateRef": "cdkContextMenuTriggerFor"; "menuPosition": "cdkContextMenuPosition"; "menuData": "cdkContextMenuTriggerData"; "disabled": "cdkContextMenuDisabled"; }, { "opened": "cdkContextMenuOpened"; "closed": "cdkContextMenuClosed"; }, never, never, false, never>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<CdkContextMenuTrigger, never>;
}
Expand Down Expand Up @@ -203,7 +203,7 @@ export class CdkMenuTrigger extends CdkMenuTriggerBase implements OnDestroy {
toggle(): void;
_toggleOnKeydown(event: KeyboardEvent): void;
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<CdkMenuTrigger, "[cdkMenuTriggerFor]", ["cdkMenuTriggerFor"], { "menuTemplateRef": "cdkMenuTriggerFor"; "menuPosition": "cdkMenuPosition"; }, { "opened": "cdkMenuOpened"; "closed": "cdkMenuClosed"; }, never, never, false, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<CdkMenuTrigger, "[cdkMenuTriggerFor]", ["cdkMenuTriggerFor"], { "menuTemplateRef": "cdkMenuTriggerFor"; "menuPosition": "cdkMenuPosition"; "menuData": "cdkMenuTriggerData"; }, { "opened": "cdkMenuOpened"; "closed": "cdkMenuClosed"; }, never, never, false, never>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<CdkMenuTrigger, never>;
}
Expand All @@ -217,6 +217,7 @@ export abstract class CdkMenuTriggerBase implements OnDestroy {
readonly injector: Injector;
protected isElementInsideMenuStack(element: Element): boolean;
isOpen(): boolean;
menuData: unknown;
menuPosition: ConnectedPosition[];
protected readonly menuStack: MenuStack;
menuTemplateRef: TemplateRef<unknown>;
Expand Down

0 comments on commit a2cf3f6

Please sign in to comment.