@@ -480,6 +480,89 @@ describe('AccordionGroup', () => {
480480 } ) ;
481481 } ) ;
482482 } ) ;
483+
484+ describe ( 'structural validations' , ( ) => {
485+ let consoleSpy : jasmine . Spy ;
486+
487+ beforeEach ( ( ) => {
488+ consoleSpy = spyOn ( console , 'error' ) ;
489+ } ) ;
490+
491+ afterEach ( ( ) => {
492+ TestBed . resetTestingModule ( ) ;
493+ TestBed . configureTestingModule ( {
494+ imports : [ AccordionGroupWithLoop ] ,
495+ providers : [ provideFakeDirectionality ( 'ltr' ) , _IdGenerator ] ,
496+ } ) ;
497+ fixture = TestBed . createComponent ( AccordionGroupWithLoop ) ;
498+ setupAccordionGroup ( ) ;
499+ } ) ;
500+
501+ it ( 'should warn when multiple triggers control the same panel' , ( ) => {
502+ TestBed . resetTestingModule ( ) ;
503+ TestBed . configureTestingModule ( {
504+ imports : [ AccordionWithDuplicateTriggers ] ,
505+ } ) ;
506+ const duplicateFixture = TestBed . createComponent ( AccordionWithDuplicateTriggers ) ;
507+ duplicateFixture . detectChanges ( ) ;
508+
509+ expect ( consoleSpy ) . toHaveBeenCalledWith (
510+ 'ngAccordionPanel is already controlled by another ngAccordionTrigger.' ,
511+ ) ;
512+ } ) ;
513+
514+ it ( 'should warn when trigger is nested inside its controlled panel' , ( ) => {
515+ TestBed . resetTestingModule ( ) ;
516+ TestBed . configureTestingModule ( {
517+ imports : [ AccordionWithNestedTrigger ] ,
518+ } ) ;
519+ const nestedFixture = TestBed . createComponent ( AccordionWithNestedTrigger ) ;
520+ nestedFixture . detectChanges ( ) ;
521+
522+ expect ( consoleSpy ) . toHaveBeenCalledWith (
523+ 'ngAccordionTrigger must not be nested inside its controlled ngAccordionPanel, otherwise it will become unreachable when collapsed.' ,
524+ ) ;
525+ } ) ;
526+
527+ it ( 'should warn when ngAccordionPanel is missing ngAccordionContent' , ( ) => {
528+ TestBed . resetTestingModule ( ) ;
529+ TestBed . configureTestingModule ( {
530+ imports : [ AccordionPanelWithoutContent ] ,
531+ } ) ;
532+ const noContentFixture = TestBed . createComponent ( AccordionPanelWithoutContent ) ;
533+ noContentFixture . detectChanges ( ) ;
534+
535+ expect ( consoleSpy ) . toHaveBeenCalledWith (
536+ 'ngAccordionPanel must have an ngAccordionContent to render.' ,
537+ ) ;
538+ } ) ;
539+
540+ it ( 'should warn when ngAccordionPanel is missing controlling trigger' , ( ) => {
541+ TestBed . resetTestingModule ( ) ;
542+ TestBed . configureTestingModule ( {
543+ imports : [ AccordionPanelWithoutTrigger ] ,
544+ } ) ;
545+ const noTriggerFixture = TestBed . createComponent ( AccordionPanelWithoutTrigger ) ;
546+ noTriggerFixture . detectChanges ( ) ;
547+
548+ expect ( consoleSpy ) . toHaveBeenCalledWith (
549+ 'ngAccordionPanel must have an ngAccordionTrigger to control it.' ,
550+ ) ;
551+ } ) ;
552+
553+ it ( 'should warn when multiple items are expanded in single-expand mode' , ( ) => {
554+ TestBed . resetTestingModule ( ) ;
555+ TestBed . configureTestingModule ( {
556+ imports : [ AccordionWithMultipleExpandedItems ] ,
557+ } ) ;
558+ const multipleExpandedFixture = TestBed . createComponent ( AccordionWithMultipleExpandedItems ) ;
559+ multipleExpandedFixture . detectChanges ( ) ;
560+
561+ expect ( consoleSpy ) . toHaveBeenCalledWith (
562+ 'ngAccordionGroup has multiExpandable set to false, but multiple ngAccordionTrigger panels are initially expanded.' ,
563+ ) ;
564+ } ) ;
565+ } ) ;
483566} ) ;
484567
485568@Component ( {
@@ -606,3 +689,81 @@ class AccordionGroupWithIfs extends AccordionGroupWithLoop {
606689 includeSecond = signal ( true ) ;
607690 includeThird = signal ( true ) ;
608691}
692+
693+ @Component ( {
694+ template : `
695+ <div ngAccordionGroup>
696+ <button ngAccordionTrigger [panel]="panel1">Trigger 1</button>
697+ <button ngAccordionTrigger [panel]="panel1">Trigger 2</button>
698+ <div ngAccordionPanel #panel1="ngAccordionPanel">
699+ <ng-template ngAccordionContent>Content</ng-template>
700+ </div>
701+ </div>
702+ ` ,
703+ imports : [ AccordionGroup , AccordionTrigger , AccordionPanel , AccordionContent ] ,
704+ changeDetection : ChangeDetectionStrategy . Eager ,
705+ } )
706+ class AccordionWithDuplicateTriggers { }
707+
708+ @Component ( {
709+ template : `
710+ <div ngAccordionGroup>
711+ <div ngAccordionPanel #panel1="ngAccordionPanel">
712+ <button ngAccordionTrigger [panel]="panel1">Nested Trigger</button>
713+ <ng-template ngAccordionContent>Content</ng-template>
714+ </div>
715+ </div>
716+ ` ,
717+ imports : [ AccordionGroup , AccordionTrigger , AccordionPanel , AccordionContent ] ,
718+ changeDetection : ChangeDetectionStrategy . Eager ,
719+ } )
720+ class AccordionWithNestedTrigger { }
721+
722+ @Component ( {
723+ template : `
724+ <div ngAccordionGroup>
725+ <button ngAccordionTrigger [panel]="panel1">Trigger</button>
726+ <div ngAccordionPanel #panel1="ngAccordionPanel">
727+ Content
728+ </div>
729+ </div>
730+ ` ,
731+ imports : [ AccordionGroup , AccordionTrigger , AccordionPanel ] ,
732+ changeDetection : ChangeDetectionStrategy . Eager ,
733+ } )
734+ class AccordionPanelWithoutContent { }
735+
736+ @Component ( {
737+ template : `
738+ <div ngAccordionGroup>
739+ <div ngAccordionPanel>
740+ <ng-template ngAccordionContent>Content</ng-template>
741+ </div>
742+ </div>
743+ ` ,
744+ imports : [ AccordionGroup , AccordionPanel , AccordionContent ] ,
745+ changeDetection : ChangeDetectionStrategy . Eager ,
746+ } )
747+ class AccordionPanelWithoutTrigger { }
748+
749+ @Component ( {
750+ template : `
751+ <div ngAccordionGroup [multiExpandable]="false">
752+ <div>
753+ <button ngAccordionTrigger [panel]="panel1" [expanded]="true">Trigger 1</button>
754+ <div ngAccordionPanel #panel1="ngAccordionPanel">
755+ <ng-template ngAccordionContent>Content 1</ng-template>
756+ </div>
757+ </div>
758+ <div>
759+ <button ngAccordionTrigger [panel]="panel2" [expanded]="true">Trigger 2</button>
760+ <div ngAccordionPanel #panel2="ngAccordionPanel">
761+ <ng-template ngAccordionContent>Content 2</ng-template>
762+ </div>
763+ </div>
764+ </div>
765+ ` ,
766+ imports : [ AccordionGroup , AccordionTrigger , AccordionPanel , AccordionContent ] ,
767+ changeDetection : ChangeDetectionStrategy . Eager ,
768+ } )
769+ class AccordionWithMultipleExpandedItems { }
0 commit comments