@@ -740,6 +740,99 @@ describe('Tabs', () => {
740740 expect ( tabPanelElements [ 2 ] . hasAttribute ( 'inert' ) ) . toBe ( true ) ;
741741 } ) ;
742742 } ) ;
743+
744+ describe ( 'Dynamic tabs' , ( ) => {
745+ beforeEach ( ( ) => {
746+ setupTestTabs ( ) ;
747+ updateTabs ( {
748+ initialTabs : [
749+ { value : 'tab1' , label : 'Tab 1' , content : 'Content 1' } ,
750+ { value : 'tab2' , label : 'Tab 2' , content : 'Content 2' } ,
751+ { value : 'tab3' , label : 'Tab 3' , content : 'Content 3' } ,
752+ ] ,
753+ selectedTab : 'tab2' ,
754+ } ) ;
755+ } ) ;
756+
757+ it ( 'should update selection when active tab is removed' , ( ) => {
758+ expect ( testComponent . selectedTab ( ) ) . toBe ( 'tab2' ) ;
759+
760+ testComponent . tabsData . set ( [
761+ { value : 'tab1' , label : 'Tab 1' , content : 'Content 1' } ,
762+ { value : 'tab3' , label : 'Tab 3' , content : 'Content 3' } ,
763+ ] ) ;
764+ fixture . detectChanges ( ) ;
765+ defineTestVariables ( ) ;
766+
767+ expect ( testComponent . selectedTab ( ) ) . toBeUndefined ( ) ;
768+ } ) ;
769+
770+ it ( 'should maintain selection when a new tab is added' , ( ) => {
771+ expect ( testComponent . selectedTab ( ) ) . toBe ( 'tab2' ) ;
772+
773+ testComponent . tabsData . set ( [
774+ { value : 'tab1' , label : 'Tab 1' , content : 'Content 1' } ,
775+ { value : 'tab2' , label : 'Tab 2' , content : 'Content 2' } ,
776+ { value : 'tab3' , label : 'Tab 3' , content : 'Content 3' } ,
777+ { value : 'tab4' , label : 'Tab 4' , content : 'Content 4' } ,
778+ ] ) ;
779+ fixture . detectChanges ( ) ;
780+ defineTestVariables ( ) ;
781+
782+ expect ( testComponent . selectedTab ( ) ) . toBe ( 'tab2' ) ;
783+ } ) ;
784+ } ) ;
785+
786+ describe ( 'Content lazy rendering' , ( ) => {
787+ beforeEach ( ( ) => {
788+ setupTestTabs ( ) ;
789+ updateTabs ( {
790+ initialTabs : [
791+ { value : 'tab1' , label : 'Tab 1' , content : 'Content 1' } ,
792+ { value : 'tab2' , label : 'Tab 2' , content : 'Content 2' } ,
793+ ] ,
794+ selectedTab : 'tab1' ,
795+ } ) ;
796+ } ) ;
797+
798+ it ( 'should not render content of unselected tabs' , ( ) => {
799+ expect ( tabPanelElements [ 0 ] . textContent ?. trim ( ) ) . toContain ( 'Content 1' ) ;
800+ expect ( tabPanelElements [ 1 ] . textContent ?. trim ( ) ) . not . toContain ( 'Content 2' ) ;
801+ } ) ;
802+
803+ it ( 'should render content when tab becomes selected' , ( ) => {
804+ updateTabs ( { selectedTab : 'tab2' } ) ;
805+
806+ expect ( tabPanelElements [ 0 ] . textContent ?. trim ( ) ) . not . toContain ( 'Content 1' ) ;
807+ expect ( tabPanelElements [ 1 ] . textContent ?. trim ( ) ) . toContain ( 'Content 2' ) ;
808+ } ) ;
809+ } ) ;
810+
811+ describe ( 'Custom IDs' , ( ) => {
812+ let customIdFixture : ComponentFixture < TestTabsCustomIdComponent > ;
813+ let customIdComponent : TestTabsCustomIdComponent ;
814+
815+ beforeEach ( ( ) => {
816+ TestBed . configureTestingModule ( {
817+ providers : [ provideFakeDirectionality ( 'ltr' ) ] ,
818+ } ) ;
819+ customIdFixture = TestBed . createComponent ( TestTabsCustomIdComponent ) ;
820+ customIdComponent = customIdFixture . componentInstance ;
821+ fixture = customIdFixture as any ;
822+ customIdFixture . detectChanges ( ) ;
823+ } ) ;
824+
825+ it ( 'should use custom ID for tab and link to panel' , async ( ) => {
826+ const tabEl = customIdFixture . nativeElement . querySelector ( '#custom-tab-id' ) ;
827+ const panelEl = customIdFixture . nativeElement . querySelector ( '#custom-panel-id' ) ;
828+
829+ expect ( tabEl ) . toBeTruthy ( ) ;
830+ expect ( panelEl ) . toBeTruthy ( ) ;
831+
832+ expect ( tabEl . getAttribute ( 'aria-controls' ) ) . toBe ( 'custom-panel-id' ) ;
833+ expect ( panelEl . getAttribute ( 'aria-labelledby' ) ) . toBe ( 'custom-tab-id' ) ;
834+ } ) ;
835+ } ) ;
743836} ) ;
744837
745838@Component ( {
@@ -798,3 +891,21 @@ class TestTabsComponent {
798891 focusMode = signal < 'roving' | 'activedescendant' > ( 'roving' ) ;
799892 selectionMode = signal < 'follow' | 'explicit' > ( 'follow' ) ;
800893}
894+
895+ @Component ( {
896+ template : `
897+ <div ngTabs>
898+ <ul ngTabList [(selectedTab)]="selectedTab">
899+ <li ngTab value="tab1" id="custom-tab-id">Tab 1</li>
900+ </ul>
901+ <div ngTabPanel value="tab1" id="custom-panel-id">
902+ <ng-template ngTabContent>Content 1</ng-template>
903+ </div>
904+ </div>
905+ ` ,
906+ imports : [ Tabs , TabList , Tab , TabPanel , TabContent ] ,
907+ changeDetection : ChangeDetectionStrategy . Eager ,
908+ } )
909+ class TestTabsCustomIdComponent {
910+ selectedTab = signal ( 'tab1' ) ;
911+ }
0 commit comments