diff --git a/src/aria/menu/BUILD.bazel b/src/aria/menu/BUILD.bazel
index 8d77abf0b0e3..64d3f2e60123 100644
--- a/src/aria/menu/BUILD.bazel
+++ b/src/aria/menu/BUILD.bazel
@@ -11,6 +11,7 @@ ng_project(
),
deps = [
"//:node_modules/@angular/core",
+ "//src/aria/deferred-content",
"//src/aria/private",
"//src/cdk/a11y",
"//src/cdk/bidi",
diff --git a/src/aria/menu/index.ts b/src/aria/menu/index.ts
index 135a0a300210..baacdeadfc02 100644
--- a/src/aria/menu/index.ts
+++ b/src/aria/menu/index.ts
@@ -6,4 +6,4 @@
* found in the LICENSE file at https://angular.dev/license
*/
-export {Menu, MenuBar, MenuItem, MenuTrigger} from './menu';
+export {Menu, MenuBar, MenuContent, MenuItem, MenuTrigger} from './menu';
diff --git a/src/aria/menu/menu.spec.ts b/src/aria/menu/menu.spec.ts
index 9ed1e34e958f..8e799347c103 100644
--- a/src/aria/menu/menu.spec.ts
+++ b/src/aria/menu/menu.spec.ts
@@ -151,49 +151,49 @@ describe('Standalone Menu Pattern', () => {
it('should select an item on click', () => {
const banana = getItem('Banana');
- spyOn(fixture.componentInstance, 'onSubmit');
+ spyOn(fixture.componentInstance, 'onSelect');
click(banana!);
- expect(fixture.componentInstance.onSubmit).toHaveBeenCalledWith('Banana');
+ expect(fixture.componentInstance.onSelect).toHaveBeenCalledWith('Banana');
});
it('should select an item on enter', () => {
const banana = getItem('Banana');
- spyOn(fixture.componentInstance, 'onSubmit');
+ spyOn(fixture.componentInstance, 'onSelect');
keydown(document.activeElement!, 'ArrowDown'); // Move focus to Banana
expect(document.activeElement).toBe(banana);
keydown(banana!, 'Enter');
- expect(fixture.componentInstance.onSubmit).toHaveBeenCalledWith('Banana');
+ expect(fixture.componentInstance.onSelect).toHaveBeenCalledWith('Banana');
});
it('should select an item on space', () => {
const banana = getItem('Banana');
- spyOn(fixture.componentInstance, 'onSubmit');
+ spyOn(fixture.componentInstance, 'onSelect');
keydown(document.activeElement!, 'ArrowDown'); // Move focus to Banana
expect(document.activeElement).toBe(banana);
keydown(banana!, ' ');
- expect(fixture.componentInstance.onSubmit).toHaveBeenCalledWith('Banana');
+ expect(fixture.componentInstance.onSelect).toHaveBeenCalledWith('Banana');
});
it('should not select a disabled item', () => {
const cherry = getItem('Cherry');
- spyOn(fixture.componentInstance, 'onSubmit');
+ spyOn(fixture.componentInstance, 'onSelect');
click(cherry!);
- expect(fixture.componentInstance.onSubmit).not.toHaveBeenCalled();
+ expect(fixture.componentInstance.onSelect).not.toHaveBeenCalled();
keydown(document.activeElement!, 'End');
expect(document.activeElement).toBe(cherry);
keydown(cherry!, 'Enter');
- expect(fixture.componentInstance.onSubmit).not.toHaveBeenCalled();
+ expect(fixture.componentInstance.onSelect).not.toHaveBeenCalled();
keydown(cherry!, ' ');
- expect(fixture.componentInstance.onSubmit).not.toHaveBeenCalled();
+ expect(fixture.componentInstance.onSelect).not.toHaveBeenCalled();
});
});
@@ -316,18 +316,18 @@ describe('Standalone Menu Pattern', () => {
}));
it('should close on selecting an item on click', () => {
- spyOn(fixture.componentInstance, 'onSubmit');
+ spyOn(fixture.componentInstance, 'onSelect');
click(getItem('Berries')!); // open submenu
expect(isSubmenuExpanded()).toBe(true);
click(getItem('Blueberry')!);
- expect(fixture.componentInstance.onSubmit).toHaveBeenCalledWith('Blueberry');
+ expect(fixture.componentInstance.onSelect).toHaveBeenCalledWith('Blueberry');
expect(isSubmenuExpanded()).toBe(false);
});
it('should close on selecting an item on enter', () => {
- spyOn(fixture.componentInstance, 'onSubmit');
+ spyOn(fixture.componentInstance, 'onSelect');
const apple = getItem('Apple');
const banana = getItem('Banana');
const berries = getItem('Berries');
@@ -341,12 +341,12 @@ describe('Standalone Menu Pattern', () => {
keydown(blueberry!, 'Enter');
- expect(fixture.componentInstance.onSubmit).toHaveBeenCalledWith('Blueberry');
+ expect(fixture.componentInstance.onSelect).toHaveBeenCalledWith('Blueberry');
expect(isSubmenuExpanded()).toBe(false);
});
it('should close on selecting an item on space', () => {
- spyOn(fixture.componentInstance, 'onSubmit');
+ spyOn(fixture.componentInstance, 'onSelect');
const apple = getItem('Apple');
const banana = getItem('Banana');
const berries = getItem('Berries');
@@ -360,7 +360,7 @@ describe('Standalone Menu Pattern', () => {
keydown(blueberry!, ' ');
- expect(fixture.componentInstance.onSubmit).toHaveBeenCalledWith('Blueberry');
+ expect(fixture.componentInstance.onSelect).toHaveBeenCalledWith('Blueberry');
expect(isSubmenuExpanded()).toBe(false);
});
@@ -877,12 +877,12 @@ describe('Menu Bar Pattern', () => {
@Component({
template: `
-
+
Apple
Banana
-
Berries
+
Berries
-
+
Blueberry
Blackberry
Strawberry
@@ -894,19 +894,19 @@ describe('Menu Bar Pattern', () => {
imports: [Menu, MenuItem],
})
class StandaloneMenuExample {
- onSubmit(value: string) {}
+ onSelect(value: string) {}
}
@Component({
template: `
-
+
-
+
Apple
Banana
-
Berries
+
Berries
-
+
Blueberry
Blackberry
Strawberry
@@ -923,24 +923,24 @@ class MenuTriggerExample {}
template: `
File
-
Edit
+
Edit
-
+
-
View
+
View
-
+
Zoom In
Zoom Out
Full Screen
-
Help
+
Help
-
+
diff --git a/src/aria/menu/menu.ts b/src/aria/menu/menu.ts
index 882206b6d8d6..b1ead192bec0 100644
--- a/src/aria/menu/menu.ts
+++ b/src/aria/menu/menu.ts
@@ -11,6 +11,7 @@ import {
computed,
contentChildren,
Directive,
+ effect,
ElementRef,
inject,
input,
@@ -29,6 +30,7 @@ import {
} from '@angular/aria/private';
import {toSignal} from '@angular/core/rxjs-interop';
import {Directionality} from '@angular/cdk/bidi';
+import {DeferredContent, DeferredContentAware} from '@angular/aria/deferred-content';
/**
* A trigger for a menu.
@@ -44,7 +46,7 @@ import {Directionality} from '@angular/cdk/bidi';
'[attr.tabindex]': '_pattern.tabindex()',
'[attr.aria-haspopup]': '_pattern.hasPopup()',
'[attr.aria-expanded]': '_pattern.expanded()',
- '[attr.aria-controls]': '_pattern.submenu()?.id()',
+ '[attr.aria-controls]': '_pattern.menu()?.id()',
'(click)': '_pattern.onClick()',
'(keydown)': '_pattern.onKeydown($event)',
'(focusout)': '_pattern.onFocusOut($event)',
@@ -59,18 +61,18 @@ export class MenuTrigger
{
// TODO(wagnermaciel): See we can remove the need to pass in a submenu.
- /** The submenu associated with the menu trigger. */
- submenu = input