Skip to content

Commit c725f07

Browse files
CSimoesJrbruno-severino
authored andcommitted
feat(context-tabs): novo componente
Implementa novo componente Context-Tabs fixes contexts-tabs/DTHFUI-11652
1 parent 0cc4852 commit c725f07

28 files changed

+1915
-44
lines changed

projects/ui/src/lib/components/components.module.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import { PoSearchModule } from './po-search';
4141
import { PoSlideModule } from './po-slide/po-slide.module';
4242
import { PoStepperModule } from './po-stepper/po-stepper.module';
4343
import { PoTableModule } from './po-table/po-table.module';
44+
import { PoContextTabsModule } from './po-context-tabs/po-context-tabs.module';
4445
import { PoTabsModule } from './po-tabs/po-tabs.module';
4546
import { PoTagModule } from './po-tag/po-tag.module';
4647
import { PoToolbarModule } from './po-toolbar/po-toolbar.module';
@@ -84,6 +85,7 @@ import { PoToasterModule } from './po-toaster';
8485
PoStepperModule,
8586
PoTableModule,
8687
PoTabsModule,
88+
PoContextTabsModule,
8789
PoTagModule,
8890
PoToolbarModule,
8991
PoTreeViewModule,
@@ -132,6 +134,7 @@ import { PoToasterModule } from './po-toaster';
132134
PoSlideModule,
133135
PoStepperModule,
134136
PoTableModule,
137+
PoContextTabsModule,
135138
PoTabsModule,
136139
PoTagModule,
137140
PoToolbarModule,

projects/ui/src/lib/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export * from './po-progress/index';
3939
export * from './po-slide/index';
4040
export * from './po-stepper/index';
4141
export * from './po-table/index';
42+
export * from './po-context-tabs/index';
4243
export * from './po-tabs/index';
4344
export * from './po-tag/index';
4445
export * from './po-toolbar/index';
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export * from './po-context-tab-button/po-context-tab-button.component';
2+
export * from './po-context-tabs.component';
3+
4+
export * from './po-context-tabs.module';
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<div
2+
#tabButtom
3+
role="tab"
4+
[p-tooltip]="widthButton > 239 || showTooltip ? label : ''"
5+
p-tooltip-position="top"
6+
class="po-tab-button-default"
7+
[id]="id"
8+
[ngClass]="{ 'po-tab-focusable': !disabled && !hide }"
9+
[attr.tabindex]="disabled ? null : 0"
10+
[attr.aria-selected]="active"
11+
[class.po-tab-button-active]="active"
12+
[class.po-tab-button-disabled]="disabled"
13+
(click)="onClick()"
14+
(focusin)="onFocusIn()"
15+
(focusout)="onFocusOut()"
16+
(keyup.enter)="onClick()"
17+
>
18+
<div class="po-tab-button-content">
19+
<span [ngClass]="!hideClose ? 'po-context-tab-button-label' : 'po-tab-button-label'" class="po-text-ellipsis">
20+
{{ label }}
21+
</span>
22+
<div
23+
class="po-tab-button-content-close-icon"
24+
*ngIf="!hideClose"
25+
(click)="closeTab($event)"
26+
(keydown)="closeTab($event)"
27+
>
28+
<po-icon
29+
[tabIndex]="activeCloseIcon ? 0 : -1"
30+
[attr.aria-label]="literals.close"
31+
p-icon="ICON_CLOSE"
32+
class="po-tab-button-icon-close"
33+
></po-icon>
34+
</div>
35+
</div>
36+
</div>
37+
<div [ngClass]="{ 'po-tab-border-active': active, 'po-tab-border-disabled': !active }"></div>
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { configureTestSuite } from '../../../util-test/util-expect.spec';
4+
import { PoContextTabButtonComponent } from './po-context-tab-button.component';
5+
6+
describe('PoContextTabButtonComponent:', () => {
7+
let component: PoContextTabButtonComponent;
8+
let fixture: ComponentFixture<PoContextTabButtonComponent>;
9+
let nativeElement: any;
10+
11+
configureTestSuite(() => {
12+
TestBed.configureTestingModule({
13+
declarations: [PoContextTabButtonComponent]
14+
});
15+
});
16+
17+
beforeEach(() => {
18+
fixture = TestBed.createComponent(PoContextTabButtonComponent);
19+
component = fixture.componentInstance;
20+
21+
nativeElement = fixture.debugElement.nativeElement;
22+
});
23+
24+
it('should be created', () => {
25+
expect(component).toBeTruthy();
26+
});
27+
28+
describe('Methods:', () => {
29+
it('afterViewInit: should apply initial properties', () => {
30+
component.ngAfterViewInit();
31+
32+
expect(component.afterViewChecked).toBeTrue();
33+
});
34+
35+
it('ngOnChanges: should emit `changeState` if hide currentValue is true', () => {
36+
spyOn(component.changeState, 'emit');
37+
38+
component.ngOnChanges(<any>{ hide: { currentValue: true } });
39+
40+
expect(component.changeState.emit).toHaveBeenCalled();
41+
});
42+
43+
it('ngOnChanges: should emit `changeState` if disabled currentValue is true', () => {
44+
spyOn(component.changeState, 'emit');
45+
46+
component.ngOnChanges(<any>{ disabled: { currentValue: true } });
47+
48+
expect(component.changeState.emit).toHaveBeenCalled();
49+
});
50+
51+
it('ngOnChanges: shouldn`t emit `changeState` if hide or disabled currentValue is false', () => {
52+
spyOn(component.changeState, 'emit');
53+
54+
component.ngOnChanges(<any>{ disabled: { currentValue: false }, hide: { currentValue: false } });
55+
56+
expect(component.changeState.emit).not.toHaveBeenCalled();
57+
});
58+
59+
it('ngOnChanges: should emit `changeVisible` if hide currentValue is true', () => {
60+
spyOn(component.changeVisible, 'emit');
61+
62+
component.afterViewChecked = true;
63+
component.ngOnChanges(<any>{ hide: { currentValue: true } });
64+
65+
expect(component.changeVisible.emit).toHaveBeenCalled();
66+
});
67+
68+
it('should emit close when closeTab is called with Enter key and not disabled', () => {
69+
const event = new KeyboardEvent('keydown', { key: 'Enter' });
70+
spyOn(event, 'preventDefault');
71+
spyOn(event, 'stopPropagation');
72+
spyOn(component.close, 'emit');
73+
74+
component.disabled = false;
75+
76+
component.closeTab(event as any);
77+
78+
expect(event.preventDefault).toHaveBeenCalled();
79+
expect(event.stopPropagation).toHaveBeenCalled();
80+
expect(component.close.emit).toHaveBeenCalled();
81+
});
82+
83+
it('should stopPropagation when closeTab is called with ArrowLeft or ArrowRight key', () => {
84+
const event = new KeyboardEvent('keydown', { code: 'ArrowLeft', key: 'ArrowLeft' });
85+
spyOn(event, 'preventDefault');
86+
spyOn(event, 'stopPropagation');
87+
spyOn(component.close, 'emit');
88+
89+
component.closeTab(event as any);
90+
91+
expect(event.preventDefault).toHaveBeenCalled();
92+
expect(event.stopPropagation).toHaveBeenCalled();
93+
expect(component.close.emit).not.toHaveBeenCalled();
94+
95+
const event2 = new KeyboardEvent('keydown', { code: 'ArrowRight', key: 'ArrowLeft' });
96+
spyOn(event2, 'preventDefault');
97+
spyOn(event2, 'stopPropagation');
98+
99+
component.closeTab(event2 as any);
100+
101+
expect(event2.preventDefault).toHaveBeenCalled();
102+
expect(event2.stopPropagation).toHaveBeenCalled();
103+
});
104+
105+
it('should not emit close if component is disabled', () => {
106+
const event = new KeyboardEvent('keydown', { key: 'Enter' });
107+
spyOn(component.close, 'emit');
108+
109+
component.disabled = true;
110+
111+
component.closeTab(event as any);
112+
113+
expect(component.close.emit).not.toHaveBeenCalled();
114+
});
115+
116+
it('should activate close icon on focus in and deactivate on focus out', () => {
117+
component.disabled = false;
118+
119+
component.onFocusIn();
120+
expect(component.activeCloseIcon).toBeTrue();
121+
122+
component.onFocusOut();
123+
expect(component.activeCloseIcon).toBeFalse();
124+
});
125+
});
126+
});
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { AfterViewInit, Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
2+
3+
import { PoTabButtonComponent } from '../../po-tabs/po-tab-button/po-tab-button.component';
4+
5+
/**
6+
* @docsPrivate
7+
*
8+
* @description
9+
*
10+
* Componente responsável por manipular os botões de aba.
11+
*/
12+
@Component({
13+
selector: 'po-context-tab-button',
14+
templateUrl: './po-context-tab-button.component.html',
15+
standalone: false
16+
})
17+
export class PoContextTabButtonComponent extends PoTabButtonComponent implements OnChanges, AfterViewInit {
18+
@Input('p-hide-close') hideClose: boolean = false;
19+
@Input('p-show-tooltip') showTooltip: boolean = false;
20+
@Input('p-literals') literals;
21+
@Output('p-close') close: EventEmitter<any> = new EventEmitter<any>();
22+
23+
// Função sera emitida quando ocorre mudança da visibilidade da tab
24+
@Output('p-change-visible') changeVisible = new EventEmitter();
25+
26+
activeCloseIcon = false;
27+
afterViewChecked = false;
28+
29+
ngAfterViewInit(): void {
30+
this.afterViewChecked = true;
31+
this.widthButton = this.tabButtom.nativeElement.offsetWidth;
32+
this.changeDetector.detectChanges();
33+
}
34+
35+
ngOnChanges(changes: SimpleChanges) {
36+
if (changes.hide?.currentValue || changes.disabled?.currentValue) {
37+
this.changeState.emit(this);
38+
}
39+
40+
if (!changes.hide?.firstChange && changes.hide && this.afterViewChecked) {
41+
this.changeVisible.emit(this);
42+
}
43+
}
44+
45+
closeTab(event) {
46+
if (event.code === 'ArrowLeft' || event.code === 'ArrowRight') {
47+
event.preventDefault();
48+
event.stopPropagation();
49+
}
50+
51+
if ((!event.key || event?.key === 'Enter') && !this.disabled) {
52+
event.preventDefault();
53+
event.stopPropagation();
54+
this.close.emit(this.tabButtom);
55+
}
56+
}
57+
58+
onFocusIn() {
59+
if (!this.disabled) {
60+
this.activeCloseIcon = true;
61+
}
62+
}
63+
64+
onFocusOut() {
65+
this.activeCloseIcon = false;
66+
}
67+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<div class="po-tabs-container">
2+
<div #containerTabs class="po-tabs-header">
3+
<div class="po-tabs-button-wrapper" role="tablist">
4+
<ng-container *ngFor="let tab of tabs; trackBy: trackByFn">
5+
<po-context-tab-button
6+
*ngIf="!tab.removed"
7+
#tabButton
8+
class="po-tab-button"
9+
[class.po-tab-button-aa]="size === 'small'"
10+
[p-active]="tab.active"
11+
[p-disabled]="tab.disabled"
12+
[p-show-tooltip]="tab.showTooltip"
13+
[p-hide]="tab.hide"
14+
[p-id]="tab.id"
15+
[hidden]="tab.hide"
16+
[p-hide-close]="tab.hideClose"
17+
[attr.disabled]="tab.disabled"
18+
[p-label]="tab.label"
19+
[p-literals]="literals"
20+
[id]="tab.id"
21+
(keyup.enter)="closeListbox()"
22+
(p-activated)="onTabActive(tab)"
23+
(p-change-state)="onTabChangeState(tab)"
24+
(p-change-visible)="onChangeVisibilityTab(tab)"
25+
(p-click)="selectedTab(tab)"
26+
(p-close)="closeTab(tab)"
27+
>
28+
</po-context-tab-button>
29+
</ng-container>
30+
31+
<po-tab-dropdown
32+
#tabDropdown
33+
*ngIf="isShowTabDropdown"
34+
class="po-tab-dropdown"
35+
[class.po-tab-dropdown-device]="!tabsDefault?.length"
36+
[p-label]="literals.moreTabs"
37+
[p-size]="size"
38+
[p-tabs]="overflowedTabs"
39+
(p-change-state)="onTabChangeState($event)"
40+
(p-click)="onTabActiveByDropdown($event)"
41+
>
42+
</po-tab-dropdown>
43+
</div>
44+
</div>
45+
</div>
46+
<div class="po-tabs-content">
47+
<ng-content></ng-content>
48+
</div>

0 commit comments

Comments
 (0)