Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
<div class="tab-content">
<div class="tab-pane p-4 fade active show" role="tabpanel">
<ng-content></ng-content>
</div>
</div>
} @else if (tabs) {
<div class="tab-content">
@for (tab of tabs; track tab.id) {
<div
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
import { Component } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { ItTabContainerComponent } from './tab-container.component';
import { ItTabItemComponent } from '../tab-item/tab-item.component';
import { tb_base } from '../../../../../test';

@Component({
selector: 'it-test-single-pane',
template: `
<it-tab-container [singlePane]="true">
<it-tab-item label="Tab 1" [active]="true"></it-tab-item>
<it-tab-item label="Tab 2"></it-tab-item>
<p class="shared-content">Shared content for all tabs</p>
</it-tab-container>
`,
imports: [ItTabContainerComponent, ItTabItemComponent],
})
class SinglePaneHostComponent {}

describe('ItTabContainerComponent', () => {
let component: ItTabContainerComponent;
let fixture: ComponentFixture<ItTabContainerComponent>;
Expand All @@ -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<SinglePaneHostComponent>;

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);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<ng-content>` 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
*/
Expand Down
13 changes: 13 additions & 0 deletions src/app/tabs/tabs-examples/tabs-examples.component.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,16 @@

<it-source-display html="{$ sanitize(htmlDynamic) $}" typescript="{$ sanitize(typescriptDynamic) $}" >
</it-source-display>

{% 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 %}

<it-tabs-single-pane-example></it-tabs-single-pane-example>

<it-source-display html="{$ sanitize(htmlSinglePane) $}" typescript="{$ sanitize(typescriptSinglePane) $}" >
</it-source-display>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<h3>Single Pane Tab</h3>
<p class="mb-4">
Con <code>[singlePane]="true"</code> le tab utilizzano un unico pannello condiviso. Solo il contenuto della tab attiva viene mostrato,
senza contenitori multipli.
</p>

<it-tab-container [singlePane]="true">
<it-tab label="Account" active>
<p>Contenuto del pannello Account. Questo è il pannello condiviso unico (singlePane).</p>
</it-tab>
<it-tab label="Impostazioni">
<p>Contenuto del pannello Impostazioni. Il pannello precedente viene sostituito.</p>
</it-tab>
<it-tab label="Notifiche" [disabled]="true">
<p>Questo pannello è disabilitato e non è selezionabile.</p>
</it-tab>
</it-tab-container>
Original file line number Diff line number Diff line change
@@ -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 {}
9 changes: 8 additions & 1 deletion src/app/tabs/tabs.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
Loading