@@ -480,6 +480,76 @@ 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+ } ) ;
483553} ) ;
484554
485555@Component ( {
@@ -606,3 +676,59 @@ class AccordionGroupWithIfs extends AccordionGroupWithLoop {
606676 includeSecond = signal ( true ) ;
607677 includeThird = signal ( true ) ;
608678}
679+
680+ @Component ( {
681+ template : `
682+ <div ngAccordionGroup>
683+ <button ngAccordionTrigger [panel]="panel1">Trigger 1</button>
684+ <button ngAccordionTrigger [panel]="panel1">Trigger 2</button>
685+ <div ngAccordionPanel #panel1="ngAccordionPanel">
686+ <ng-template ngAccordionContent>Content</ng-template>
687+ </div>
688+ </div>
689+ ` ,
690+ imports : [ AccordionGroup , AccordionTrigger , AccordionPanel , AccordionContent ] ,
691+ changeDetection : ChangeDetectionStrategy . Eager ,
692+ } )
693+ class AccordionWithDuplicateTriggers { }
694+
695+ @Component ( {
696+ template : `
697+ <div ngAccordionGroup>
698+ <div ngAccordionPanel #panel1="ngAccordionPanel">
699+ <button ngAccordionTrigger [panel]="panel1">Nested Trigger</button>
700+ <ng-template ngAccordionContent>Content</ng-template>
701+ </div>
702+ </div>
703+ ` ,
704+ imports : [ AccordionGroup , AccordionTrigger , AccordionPanel , AccordionContent ] ,
705+ changeDetection : ChangeDetectionStrategy . Eager ,
706+ } )
707+ class AccordionWithNestedTrigger { }
708+
709+ @Component ( {
710+ template : `
711+ <div ngAccordionGroup>
712+ <button ngAccordionTrigger [panel]="panel1">Trigger</button>
713+ <div ngAccordionPanel #panel1="ngAccordionPanel">
714+ Content
715+ </div>
716+ </div>
717+ ` ,
718+ imports : [ AccordionGroup , AccordionTrigger , AccordionPanel ] ,
719+ changeDetection : ChangeDetectionStrategy . Eager ,
720+ } )
721+ class AccordionPanelWithoutContent { }
722+
723+ @Component ( {
724+ template : `
725+ <div ngAccordionGroup>
726+ <div ngAccordionPanel>
727+ <ng-template ngAccordionContent>Content</ng-template>
728+ </div>
729+ </div>
730+ ` ,
731+ imports : [ AccordionGroup , AccordionPanel , AccordionContent ] ,
732+ changeDetection : ChangeDetectionStrategy . Eager ,
733+ } )
734+ class AccordionPanelWithoutTrigger { }
0 commit comments