1- import { Component , DebugElement , ChangeDetectionStrategy } from '@angular/core' ;
1+ import { Component , DebugElement , ChangeDetectionStrategy , signal } from '@angular/core' ;
22import { ComponentFixture , TestBed } from '@angular/core/testing' ;
33import { By } from '@angular/platform-browser' ;
44import { provideFakeDirectionality } from '@angular/cdk/testing/private' ;
@@ -7,6 +7,7 @@ import {MenuBar} from './menu-bar';
77import { MenuContent } from './menu-content' ;
88import { MenuItem } from './menu-item' ;
99import { MenuTrigger } from './menu-trigger' ;
10+ import { waitForMicrotasks } from '../private/testing/test-helpers' ;
1011
1112describe ( 'Standalone Menu Pattern' , ( ) => {
1213 let fixture : ComponentFixture < StandaloneMenuExample > ;
@@ -63,6 +64,33 @@ describe('Standalone Menu Pattern', () => {
6364 return items . find ( item => item . textContent ?. trim ( ) === text ) || null ;
6465 }
6566
67+ describe ( 'dynamic updates' , ( ) => {
68+ it ( 'should update item order correctly after items are shuffled' , async ( ) => {
69+ TestBed . configureTestingModule ( { imports : [ ShuffledMenuExample ] } ) ;
70+ const shuffledFixture = TestBed . createComponent ( ShuffledMenuExample ) ;
71+ shuffledFixture . detectChanges ( ) ;
72+ const menuDirective = shuffledFixture . debugElement
73+ . query ( By . directive ( Menu ) )
74+ . injector . get ( Menu ) ;
75+
76+ const itemsBefore = menuDirective . _pattern . inputs . items ( ) ;
77+ expect ( itemsBefore . length ) . toBe ( 3 ) ;
78+ expect ( itemsBefore [ 0 ] . element ( ) ?. textContent ?. trim ( ) ) . toBe ( 'Apple' ) ;
79+
80+ // Shuffle items: move first item to the end
81+ const items = ( shuffledFixture . componentInstance as unknown as ShuffledMenuExample ) . items ( ) ;
82+ const firstItem = items . shift ( ) ! ;
83+ items . push ( firstItem ) ;
84+ ( shuffledFixture . componentInstance as unknown as ShuffledMenuExample ) . items . set ( [ ...items ] ) ;
85+ shuffledFixture . detectChanges ( ) ;
86+ await waitForMicrotasks ( ) ;
87+
88+ const itemsAfter = menuDirective . _pattern . inputs . items ( ) ;
89+ expect ( itemsAfter . length ) . toBe ( 3 ) ;
90+ expect ( itemsAfter [ 0 ] . element ( ) ?. textContent ?. trim ( ) ) . toBe ( 'Banana' ) ;
91+ } ) ;
92+ } ) ;
93+
6694 describe ( 'Navigation' , ( ) => {
6795 beforeEach ( ( ) => setupMenu ( ) ) ;
6896
@@ -702,6 +730,37 @@ describe('Menu Bar Pattern', () => {
702730 return getMenuBarItem ( menuBarItemText ) ?. getAttribute ( 'aria-expanded' ) === 'true' ;
703731 }
704732
733+ describe ( 'dynamic updates' , ( ) => {
734+ it ( 'should update item order correctly after items are shuffled' , async ( ) => {
735+ TestBed . configureTestingModule ( { imports : [ ShuffledMenuBarExample ] } ) ;
736+ const shuffledFixture = TestBed . createComponent ( ShuffledMenuBarExample ) ;
737+ shuffledFixture . detectChanges ( ) ;
738+ const menuBarDirective = shuffledFixture . debugElement
739+ . query ( By . directive ( MenuBar ) )
740+ . injector . get ( MenuBar ) ;
741+
742+ const itemsBefore = menuBarDirective . _pattern . inputs . items ( ) ;
743+ expect ( itemsBefore . length ) . toBe ( 3 ) ;
744+ expect ( itemsBefore [ 0 ] . element ( ) ?. textContent ?. trim ( ) ) . toBe ( 'File' ) ;
745+
746+ // Shuffle items: move first item to the end
747+ const items = (
748+ shuffledFixture . componentInstance as unknown as ShuffledMenuBarExample
749+ ) . items ( ) ;
750+ const firstItem = items . shift ( ) ! ;
751+ items . push ( firstItem ) ;
752+ ( shuffledFixture . componentInstance as unknown as ShuffledMenuBarExample ) . items . set ( [
753+ ...items ,
754+ ] ) ;
755+ shuffledFixture . detectChanges ( ) ;
756+ await waitForMicrotasks ( ) ;
757+
758+ const itemsAfter = menuBarDirective . _pattern . inputs . items ( ) ;
759+ expect ( itemsAfter . length ) . toBe ( 3 ) ;
760+ expect ( itemsAfter [ 0 ] . element ( ) ?. textContent ?. trim ( ) ) . toBe ( 'Edit' ) ;
761+ } ) ;
762+ } ) ;
763+
705764 describe ( 'Navigation' , ( ) => {
706765 beforeEach ( ( ) => setupMenu ( ) ) ;
707766
@@ -1061,3 +1120,33 @@ class MenuTriggerExample {
10611120 changeDetection : ChangeDetectionStrategy . Eager ,
10621121} )
10631122class MenuBarExample { }
1123+
1124+ @Component ( {
1125+ template : `
1126+ <div ngMenu>
1127+ @for (item of items(); track item) {
1128+ <div ngMenuItem [value]="item.value">{{item.value}}</div>
1129+ }
1130+ </div>
1131+ ` ,
1132+ imports : [ Menu , MenuItem ] ,
1133+ changeDetection : ChangeDetectionStrategy . Eager ,
1134+ } )
1135+ class ShuffledMenuExample {
1136+ items = signal ( [ { value : 'Apple' } , { value : 'Banana' } , { value : 'Cherry' } ] ) ;
1137+ }
1138+
1139+ @Component ( {
1140+ template : `
1141+ <div ngMenuBar>
1142+ @for (item of items(); track item) {
1143+ <div ngMenuItem [value]="item.value">{{item.value}}</div>
1144+ }
1145+ </div>
1146+ ` ,
1147+ imports : [ MenuBar , MenuItem ] ,
1148+ changeDetection : ChangeDetectionStrategy . Eager ,
1149+ } )
1150+ class ShuffledMenuBarExample {
1151+ items = signal ( [ { value : 'File' } , { value : 'Edit' } , { value : 'View' } ] ) ;
1152+ }
0 commit comments