diff --git a/projects/design-angular-kit/src/lib/components/core/tab/tab-container/tab-container.component.html b/projects/design-angular-kit/src/lib/components/core/tab/tab-container/tab-container.component.html index af19f79c5..86b2f6189 100644 --- a/projects/design-angular-kit/src/lib/components/core/tab/tab-container/tab-container.component.html +++ b/projects/design-angular-kit/src/lib/components/core/tab/tab-container/tab-container.component.html @@ -59,7 +59,13 @@ [class.col-lg-9]="inverted && vertical" [class.col-8]="!inverted && vertical" [class.col-md-9]="!inverted && vertical"> - @if (tabs) { + @if (singlePane) { +
+
+ +
+
+ } @else if (tabs) {
@for (tab of tabs; track tab.id) {
+ + +

Shared content for all tabs

+ + `, + imports: [ItTabContainerComponent, ItTabItemComponent], +}) +class SinglePaneHostComponent {} + describe('ItTabContainerComponent', () => { let component: ItTabContainerComponent; let fixture: ComponentFixture; @@ -18,4 +33,51 @@ describe('ItTabContainerComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + it('should accept singlePane input', () => { + component.singlePane = true; + expect(component.singlePane).toBeTruthy(); + }); +}); + +describe('ItTabContainerComponent (singlePane)', () => { + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + ...tb_base, + imports: [...(tb_base as { imports: unknown[] }).imports, SinglePaneHostComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(SinglePaneHostComponent); + fixture.detectChanges(); + }); + + it('should render shared content via ng-content', () => { + const el: HTMLElement = fixture.nativeElement; + const shared = el.querySelector('.shared-content'); + expect(shared).toBeTruthy(); + expect(shared?.textContent?.trim()).toBe('Shared content for all tabs'); + }); + + it('should render tab headers', () => { + const el: HTMLElement = fixture.nativeElement; + const links = el.querySelectorAll('.nav-link'); + expect(links.length).toBe(2); + expect(links[0].textContent?.trim()).toContain('Tab 1'); + expect(links[1].textContent?.trim()).toContain('Tab 2'); + }); + + it('should render only one tab-pane (the single shared pane)', () => { + const el: HTMLElement = fixture.nativeElement; + const panes = el.querySelectorAll('.tab-pane'); + expect(panes.length).toBe(1); + }); + + it('should have the single pane always active and visible', () => { + const el: HTMLElement = fixture.nativeElement; + const pane = el.querySelector('.tab-pane'); + expect(pane?.classList.contains('active')).toBe(true); + expect(pane?.classList.contains('show')).toBe(true); + }); }); diff --git a/projects/design-angular-kit/src/lib/components/core/tab/tab-container/tab-container.component.ts b/projects/design-angular-kit/src/lib/components/core/tab/tab-container/tab-container.component.ts index 610bb5591..3432c2663 100644 --- a/projects/design-angular-kit/src/lib/components/core/tab/tab-container/tab-container.component.ts +++ b/projects/design-angular-kit/src/lib/components/core/tab/tab-container/tab-container.component.ts @@ -62,6 +62,14 @@ export class ItTabContainerComponent extends ItAbstractComponent implements OnDe */ @Input({ transform: inputToBoolean }) editable?: boolean; + /** + * When true, renders N tab headers but a single shared content pane. + * Use `` inside the container to provide the shared content. + * Useful when tabs act as filters on the same view rather than navigation. + * @default false + */ + @Input({ transform: inputToBoolean }) singlePane?: boolean; + /** * The tab items */ diff --git a/src/app/tabs/tabs-examples/tabs-examples.component.tpl b/src/app/tabs/tabs-examples/tabs-examples.component.tpl index ea4a70f76..95989bcaa 100644 --- a/src/app/tabs/tabs-examples/tabs-examples.component.tpl +++ b/src/app/tabs/tabs-examples/tabs-examples.component.tpl @@ -25,3 +25,16 @@ + +{% set htmlSinglePane %} + {% include "../tabs-single-pane-example/tabs-single-pane-example.component.html" %} +{% endset %} + +{% set typescriptSinglePane %} + {% include "../tabs-single-pane-example/tabs-single-pane-example.component.ts" %} +{% endset %} + + + + + diff --git a/src/app/tabs/tabs-single-pane-example/tabs-single-pane-example.component.html b/src/app/tabs/tabs-single-pane-example/tabs-single-pane-example.component.html new file mode 100644 index 000000000..e9dfc34b2 --- /dev/null +++ b/src/app/tabs/tabs-single-pane-example/tabs-single-pane-example.component.html @@ -0,0 +1,17 @@ +

Single Pane Tab

+

+ Con [singlePane]="true" le tab utilizzano un unico pannello condiviso. Solo il contenuto della tab attiva viene mostrato, + senza contenitori multipli. +

+ + + +

Contenuto del pannello Account. Questo è il pannello condiviso unico (singlePane).

+
+ +

Contenuto del pannello Impostazioni. Il pannello precedente viene sostituito.

+
+ +

Questo pannello è disabilitato e non è selezionabile.

+
+
diff --git a/src/app/tabs/tabs-single-pane-example/tabs-single-pane-example.component.ts b/src/app/tabs/tabs-single-pane-example/tabs-single-pane-example.component.ts new file mode 100644 index 000000000..82ea07b93 --- /dev/null +++ b/src/app/tabs/tabs-single-pane-example/tabs-single-pane-example.component.ts @@ -0,0 +1,7 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'it-tabs-single-pane-example', + templateUrl: './tabs-single-pane-example.component.html', +}) +export class TabsSinglePaneExampleComponent {} diff --git a/src/app/tabs/tabs.module.ts b/src/app/tabs/tabs.module.ts index 823407357..2e5874fa6 100644 --- a/src/app/tabs/tabs.module.ts +++ b/src/app/tabs/tabs.module.ts @@ -9,9 +9,16 @@ import { TabsExampleComponent } from './tabs-example/tabs-example.component'; import { TabsExamplesComponent } from './tabs-examples/tabs-examples.component'; import { TabsIndexComponent } from './tabs-index/tabs-index.component'; import { TabsDynamicExampleComponent } from './tabs-dynamic-example/tabs-dynamic-example.component'; +import { TabsSinglePaneExampleComponent } from './tabs-single-pane-example/tabs-single-pane-example.component'; @NgModule({ imports: [CommonModule, FormsModule, ReactiveFormsModule, SharedModule, TabsRoutingModule], - declarations: [TabsExampleComponent, TabsExamplesComponent, TabsIndexComponent, TabsDynamicExampleComponent], + declarations: [ + TabsExampleComponent, + TabsExamplesComponent, + TabsIndexComponent, + TabsDynamicExampleComponent, + TabsSinglePaneExampleComponent, + ], }) export class TabsModule {}