ngx-opalbytes-components 1.18.0 → 1.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +103 -0
- package/cli/cao-comp.cjs +186 -0
- package/fesm2022/ngx-opalbytes-components.mjs +331 -76
- package/fesm2022/ngx-opalbytes-components.mjs.map +1 -1
- package/package.json +4 -1
- package/src/lib/shared/components/autocomplete/autocomplete.component.html +126 -0
- package/src/lib/shared/components/autocomplete/autocomplete.component.scss +150 -0
- package/src/lib/shared/components/autocomplete/autocomplete.component.ts +552 -0
- package/src/lib/shared/components/base-button/base-button.component.html +33 -0
- package/src/lib/shared/components/base-button/base-button.component.scss +14 -0
- package/src/lib/shared/components/base-button/base-button.component.spec.ts +64 -0
- package/src/lib/shared/components/base-button/base-button.component.ts +48 -0
- package/src/lib/shared/components/base-icon/base-icon.css +0 -0
- package/src/lib/shared/components/base-icon/base-icon.html +11 -0
- package/src/lib/shared/components/base-icon/base-icon.spec.ts +23 -0
- package/src/lib/shared/components/base-icon/base-icon.ts +18 -0
- package/src/lib/shared/components/base-input/base-input.css +193 -0
- package/src/lib/shared/components/base-input/base-input.html +43 -0
- package/src/lib/shared/components/base-input/base-input.spec.ts +146 -0
- package/src/lib/shared/components/base-input/base-input.ts +133 -0
- package/src/lib/shared/components/base-time-range/base-time-range.css +27 -0
- package/src/lib/shared/components/base-time-range/base-time-range.html +23 -0
- package/src/lib/shared/components/base-time-range/base-time-range.spec.ts +27 -0
- package/src/lib/shared/components/base-time-range/base-time-range.ts +61 -0
- package/src/lib/shared/components/base-toggle/base-toggle.component.html +28 -0
- package/src/lib/shared/components/base-toggle/base-toggle.component.scss +45 -0
- package/src/lib/shared/components/base-toggle/base-toggle.component.spec.ts +22 -0
- package/src/lib/shared/components/base-toggle/base-toggle.component.ts +138 -0
- package/src/lib/shared/components/card/card.component.css +264 -0
- package/src/lib/shared/components/card/card.component.html +103 -0
- package/src/lib/shared/components/card/card.component.ts +63 -0
- package/src/lib/shared/components/date-picker/date-picker.html +82 -0
- package/src/lib/shared/components/date-picker/date-picker.scss +8 -0
- package/src/lib/shared/components/date-picker/date-picker.spec.ts +23 -0
- package/src/lib/shared/components/date-picker/date-picker.ts +165 -0
- package/src/lib/shared/components/drop-down/drop-down.css +61 -0
- package/src/lib/shared/components/drop-down/drop-down.html +96 -0
- package/src/lib/shared/components/drop-down/drop-down.spec.ts +335 -0
- package/src/lib/shared/components/drop-down/drop-down.ts +335 -0
- package/src/lib/shared/components/footer/footer.css +29 -0
- package/src/lib/shared/components/footer/footer.html +10 -0
- package/src/lib/shared/components/footer/footer.spec.ts +22 -0
- package/src/lib/shared/components/footer/footer.ts +15 -0
- package/src/lib/shared/components/index.ts +19 -0
- package/src/lib/shared/components/kpi/kpi.css +0 -0
- package/src/lib/shared/components/kpi/kpi.html +11 -0
- package/src/lib/shared/components/kpi/kpi.spec.ts +38 -0
- package/src/lib/shared/components/kpi/kpi.ts +25 -0
- package/src/lib/shared/components/links-button/links-button.css +70 -0
- package/src/lib/shared/components/links-button/links-button.html +23 -0
- package/src/lib/shared/components/links-button/links-button.spec.ts +139 -0
- package/src/lib/shared/components/links-button/links-button.ts +45 -0
- package/src/lib/shared/components/paginator/paginator.css +0 -0
- package/src/lib/shared/components/paginator/paginator.html +10 -0
- package/src/lib/shared/components/paginator/paginator.spec.ts +23 -0
- package/src/lib/shared/components/paginator/paginator.ts +25 -0
- package/src/lib/shared/components/stepper/step/step.css +8 -0
- package/src/lib/shared/components/stepper/step/step.html +1 -0
- package/src/lib/shared/components/stepper/step/step.spec.ts +131 -0
- package/src/lib/shared/components/stepper/step/step.ts +14 -0
- package/src/lib/shared/components/stepper/stepper.css +17 -0
- package/src/lib/shared/components/stepper/stepper.html +11 -0
- package/src/lib/shared/components/stepper/stepper.spec.ts +238 -0
- package/src/lib/shared/components/stepper/stepper.ts +57 -0
- package/src/lib/shared/components/tab-group/tab-group.component.html +84 -0
- package/src/lib/shared/components/tab-group/tab-group.component.scss +151 -0
- package/src/lib/shared/components/tab-group/tab-group.spec.ts +22 -0
- package/src/lib/shared/components/tab-group/tab-group.ts +218 -0
- package/src/lib/shared/components/tab-panel/tab-content.directive.ts +7 -0
- package/src/lib/shared/components/tab-panel/tab-label.directive.ts +7 -0
- package/src/lib/shared/components/tab-panel/tab-panel.css +0 -0
- package/src/lib/shared/components/tab-panel/tab-panel.spec.ts +22 -0
- package/src/lib/shared/components/tab-panel/tab-panel.ts +31 -0
- package/src/lib/shared/components/time-picker/time-picker.css +84 -0
- package/src/lib/shared/components/time-picker/time-picker.html +21 -0
- package/src/lib/shared/components/time-picker/time-picker.spec.ts +23 -0
- package/src/lib/shared/components/time-picker/time-picker.ts +90 -0
- package/types/ngx-opalbytes-components.d.ts +70 -13
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
|
2
|
+
|
|
3
|
+
import { Footer } from "./footer";
|
|
4
|
+
|
|
5
|
+
describe("Footer", () => {
|
|
6
|
+
let component: Footer;
|
|
7
|
+
let fixture: ComponentFixture<Footer>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [Footer],
|
|
12
|
+
}).compileComponents();
|
|
13
|
+
|
|
14
|
+
fixture = TestBed.createComponent(Footer);
|
|
15
|
+
component = fixture.componentInstance;
|
|
16
|
+
fixture.detectChanges();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should create", () => {
|
|
20
|
+
expect(component).toBeTruthy();
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { CommonModule } from "@angular/common";
|
|
2
|
+
import { Component, Input } from "@angular/core";
|
|
3
|
+
|
|
4
|
+
@Component({
|
|
5
|
+
selector: "cao-footer",
|
|
6
|
+
templateUrl: "./footer.html",
|
|
7
|
+
styleUrl: "./footer.css",
|
|
8
|
+
imports: [ CommonModule ]
|
|
9
|
+
})
|
|
10
|
+
export class Footer {
|
|
11
|
+
currentYear: number = new Date().getFullYear();
|
|
12
|
+
@Input() currentRoute?:string;
|
|
13
|
+
@Input() footerText?:string;
|
|
14
|
+
@Input() isFixed = true;
|
|
15
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export * from './base-button/base-button.component'
|
|
2
|
+
export * from './base-input/base-input'
|
|
3
|
+
export * from './base-time-range/base-time-range'
|
|
4
|
+
|
|
5
|
+
export * from './autocomplete/autocomplete.component'
|
|
6
|
+
export * from './drop-down/drop-down'
|
|
7
|
+
export * from './links-button/links-button'
|
|
8
|
+
export * from './footer/footer'
|
|
9
|
+
export * from './paginator/paginator'
|
|
10
|
+
export * from './time-picker/time-picker'
|
|
11
|
+
|
|
12
|
+
export * from './stepper/stepper'
|
|
13
|
+
export * from './stepper/step/step'
|
|
14
|
+
|
|
15
|
+
export * from "./tab-group/tab-group"
|
|
16
|
+
export * from "./tab-panel/tab-panel"
|
|
17
|
+
|
|
18
|
+
export * from "./base-icon/base-icon"
|
|
19
|
+
export * from "./kpi/kpi"
|
|
File without changes
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<div
|
|
2
|
+
class="flex flex-col py-2 pt-2.25 px-3.75 rounded-card border-b w-46.25 bg-(--color-white)"
|
|
3
|
+
[style.border-color]="kpiData().borderColor">
|
|
4
|
+
<div class="flex w-full justify-end">
|
|
5
|
+
<img class="w-6 h-6 mr-2" [src]="kpiData().icon.src" [alt]="kpiData().icon.alt" />
|
|
6
|
+
</div>
|
|
7
|
+
<div class="flex flex-col pl-1.5">
|
|
8
|
+
<p class="text-2xl" [style.color]="kpiData().textColor">{{ kpiData().value }}</p>
|
|
9
|
+
<h3 class="text-base" [style.color]="kpiData().textColor">{{ kpiData().title }}</h3>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
|
2
|
+
|
|
3
|
+
import { CaoKpi, ICaoKPI } from "./kpi";
|
|
4
|
+
|
|
5
|
+
const mockKpiData: ICaoKPI = {
|
|
6
|
+
title: "Test",
|
|
7
|
+
value: "42",
|
|
8
|
+
textColor: "#000000",
|
|
9
|
+
borderColor: "#ffffff",
|
|
10
|
+
icon: { src: "icon.png", alt: "icon" },
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
describe("CaoKpi", () => {
|
|
14
|
+
let component: CaoKpi;
|
|
15
|
+
let fixture: ComponentFixture<CaoKpi>;
|
|
16
|
+
|
|
17
|
+
beforeEach(async () => {
|
|
18
|
+
await TestBed.configureTestingModule({
|
|
19
|
+
imports: [CaoKpi],
|
|
20
|
+
}).compileComponents();
|
|
21
|
+
|
|
22
|
+
fixture = TestBed.createComponent(CaoKpi);
|
|
23
|
+
component = fixture.componentInstance;
|
|
24
|
+
fixture.componentRef.setInput("kpiData", mockKpiData);
|
|
25
|
+
fixture.detectChanges();
|
|
26
|
+
await fixture.whenStable();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should create", () => {
|
|
30
|
+
expect(component).toBeTruthy();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should render the kpi data", () => {
|
|
34
|
+
const el: HTMLElement = fixture.nativeElement;
|
|
35
|
+
expect(el.textContent).toContain("42");
|
|
36
|
+
expect(el.textContent).toContain("Test");
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { CommonModule } from "@angular/common";
|
|
2
|
+
import { Component, input } from "@angular/core";
|
|
3
|
+
|
|
4
|
+
export interface ICaoImage {
|
|
5
|
+
src: string;
|
|
6
|
+
alt: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ICaoKPI {
|
|
9
|
+
title: string;
|
|
10
|
+
value: string;
|
|
11
|
+
textColor: string;
|
|
12
|
+
borderColor: string;
|
|
13
|
+
icon: ICaoImage;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@Component({
|
|
17
|
+
selector: "cao-kpi",
|
|
18
|
+
imports: [CommonModule],
|
|
19
|
+
templateUrl: "./kpi.html",
|
|
20
|
+
styleUrl: "./kpi.css",
|
|
21
|
+
standalone: true,
|
|
22
|
+
})
|
|
23
|
+
export class CaoKpi {
|
|
24
|
+
readonly kpiData = input.required<ICaoKPI>();
|
|
25
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
* {
|
|
2
|
+
text-decoration: none;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.links-info {
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
width: 280px;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.links-info h2 {
|
|
12
|
+
font-size: 24px;
|
|
13
|
+
font-weight: 700;
|
|
14
|
+
text-align: center;
|
|
15
|
+
text-transform: uppercase;
|
|
16
|
+
color: var(--title-info);
|
|
17
|
+
margin-bottom: 12%;
|
|
18
|
+
text-wrap: wrap;
|
|
19
|
+
height: 64px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.links-info .links {
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
align-items: center;
|
|
26
|
+
gap: 46px;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.links-info .links .link {
|
|
30
|
+
width: 250px;
|
|
31
|
+
height: 100px;
|
|
32
|
+
text-transform: uppercase;
|
|
33
|
+
background-color: var(--primary);
|
|
34
|
+
color: var(--white);
|
|
35
|
+
font-weight: 700;
|
|
36
|
+
padding: 30px;
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
justify-content: center;
|
|
40
|
+
text-align: center;
|
|
41
|
+
font-size: 16px;
|
|
42
|
+
border-radius: 20px;
|
|
43
|
+
box-shadow: 0 4px 4px 0 var(--shadow-black);
|
|
44
|
+
cursor: pointer;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.links-info .links .link.disabled {
|
|
48
|
+
background-color: var(--input-disabled-bg);
|
|
49
|
+
color: var(--black);
|
|
50
|
+
pointer-events: none;
|
|
51
|
+
cursor: default;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.links-info .links .link:active {
|
|
55
|
+
background-color: var(--active-success-btn);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.links-info .links .link:hover {
|
|
59
|
+
background: linear-gradient(125.78deg, var(--primary) 7.03%, var(--confirm-color) 96.73%);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@media (max-width: 768px) {
|
|
63
|
+
.links-info h2 {
|
|
64
|
+
height: 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.links-info .links {
|
|
68
|
+
padding-bottom: 20px;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<div class="links-info">
|
|
2
|
+
@if (navContent()) {
|
|
3
|
+
<h2>{{ navContent().title }}</h2>
|
|
4
|
+
<div class="links">
|
|
5
|
+
@for (link of navContent().links; track $index) {
|
|
6
|
+
<div
|
|
7
|
+
class="link"
|
|
8
|
+
role="button"
|
|
9
|
+
tabindex="0"
|
|
10
|
+
[tabIndex]="link.disabled ? -1 : 0"
|
|
11
|
+
[class.disabled]="link.disabled"
|
|
12
|
+
[attr.data-cy]="link.dataCy"
|
|
13
|
+
[attr.aria-disabled]="link.disabled"
|
|
14
|
+
(click)="isValidationRequired(link)"
|
|
15
|
+
(keydown.enter)="isValidationRequired(link)"
|
|
16
|
+
(keydown.space)="isValidationRequired(link); $event.preventDefault()"
|
|
17
|
+
>
|
|
18
|
+
{{ link.text }}
|
|
19
|
+
</div>
|
|
20
|
+
}
|
|
21
|
+
</div>
|
|
22
|
+
}
|
|
23
|
+
</div>
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { ComponentFixture, ComponentFixtureAutoDetect, TestBed } from "@angular/core/testing";
|
|
2
|
+
import { INavContent, INavLink, LinksButton } from "./links-button";
|
|
3
|
+
import { provideRouter, Routes } from "@angular/router";
|
|
4
|
+
import { Component, signal } from "@angular/core";
|
|
5
|
+
import { provideHttpClientTesting } from "@angular/common/http/testing";
|
|
6
|
+
import { provideHttpClient } from "@angular/common/http";
|
|
7
|
+
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
8
|
+
|
|
9
|
+
@Component({ template: '', standalone: true })
|
|
10
|
+
class EmptyComponent {}
|
|
11
|
+
|
|
12
|
+
const testRoutes: Routes = [
|
|
13
|
+
{ path: '', component: EmptyComponent },
|
|
14
|
+
{ path: 'reception', component: EmptyComponent },
|
|
15
|
+
{ path: 'receptio', component: EmptyComponent },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
describe("LinksButton", () => {
|
|
19
|
+
let component: LinksButton;
|
|
20
|
+
let fixture: ComponentFixture<LinksButton>;
|
|
21
|
+
|
|
22
|
+
let modalServiceSpy: {
|
|
23
|
+
showModal: ReturnType<typeof vi.fn>;
|
|
24
|
+
closeAllModals: ReturnType<typeof vi.fn>;
|
|
25
|
+
};
|
|
26
|
+
let storageServiceSpy: {
|
|
27
|
+
setItem: ReturnType<typeof vi.fn>;
|
|
28
|
+
getItem: ReturnType<typeof vi.fn>;
|
|
29
|
+
};
|
|
30
|
+
let alertServiceSpy: {
|
|
31
|
+
show: ReturnType<typeof vi.fn>;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const mockLink: INavLink = {
|
|
35
|
+
text: "INICIAR ATENDIMENTO",
|
|
36
|
+
url: "reception",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const mockDisabledLink: INavLink = {
|
|
40
|
+
text: "ATENDIMENTO",
|
|
41
|
+
url: "receptio",
|
|
42
|
+
disabled: true,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const mockDirectRoute: INavLink = {
|
|
46
|
+
text: "ATENDIMENTO",
|
|
47
|
+
url: "receptio",
|
|
48
|
+
disabled: false,
|
|
49
|
+
directRoute: true,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
beforeEach(async () => {
|
|
53
|
+
storageServiceSpy = {
|
|
54
|
+
setItem: vi.fn(),
|
|
55
|
+
getItem: vi.fn(),
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
modalServiceSpy = {
|
|
59
|
+
showModal: vi.fn(),
|
|
60
|
+
closeAllModals: vi.fn(),
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
alertServiceSpy = {
|
|
64
|
+
show: vi.fn(),
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
await TestBed.configureTestingModule({
|
|
68
|
+
imports: [LinksButton],
|
|
69
|
+
providers: [
|
|
70
|
+
provideRouter(testRoutes),
|
|
71
|
+
provideHttpClient(),
|
|
72
|
+
provideHttpClientTesting(),
|
|
73
|
+
{provide: ComponentFixtureAutoDetect, useValue: true}
|
|
74
|
+
],
|
|
75
|
+
}).compileComponents();
|
|
76
|
+
|
|
77
|
+
fixture = TestBed.createComponent(LinksButton);
|
|
78
|
+
component = fixture.componentInstance;
|
|
79
|
+
|
|
80
|
+
const defaultData: INavContent = {
|
|
81
|
+
title: "Default",
|
|
82
|
+
links: [{ text: "Default", url: "/" }],
|
|
83
|
+
};
|
|
84
|
+
component.navContent = signal(defaultData);
|
|
85
|
+
|
|
86
|
+
fixture.detectChanges();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("deveria criar", () => {
|
|
90
|
+
expect(component).toBeTruthy();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("deveria ter informações", () => {
|
|
94
|
+
const expectedData: INavContent = {
|
|
95
|
+
title: "Atendimento",
|
|
96
|
+
links: [{ text: "cuidado", url: "route" }],
|
|
97
|
+
};
|
|
98
|
+
component.navContent = signal(expectedData);
|
|
99
|
+
expect(component.navContent().title).toBe(expectedData.title);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("deve navegar para a rota", () => {
|
|
103
|
+
const expectedURL = "/";
|
|
104
|
+
component["navigateTo"](expectedURL);
|
|
105
|
+
expect(component.router.url).toBe(expectedURL);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe("validação para navegação", () => {
|
|
109
|
+
it("deve navegar caso o link esteja disponivel", () => {
|
|
110
|
+
const navigateToSpy = vi.spyOn(component as any, "navigateTo");
|
|
111
|
+
component.isValidationRequired(mockLink);
|
|
112
|
+
|
|
113
|
+
expect(navigateToSpy).toHaveBeenCalledWith(mockLink.url);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("não deve navegar caso o link esteja desabilitado", () => {
|
|
117
|
+
const navigateToSpy = vi.spyOn(component as any, "navigateTo");
|
|
118
|
+
component.isValidationRequired(mockDisabledLink);
|
|
119
|
+
|
|
120
|
+
expect(navigateToSpy).not.toHaveBeenCalled();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("deve navegar considerando a raiz do projeto", () => {
|
|
124
|
+
const directRouteSpy = vi.spyOn(component as any, "directRoute");
|
|
125
|
+
component.isValidationRequired(mockDirectRoute);
|
|
126
|
+
|
|
127
|
+
expect(directRouteSpy).toHaveBeenCalledWith(mockDirectRoute.url);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe("directRoute", () => {
|
|
132
|
+
it("deve navegar a url raiz do projeto", () => {
|
|
133
|
+
vi.spyOn(component.router, "navigate");
|
|
134
|
+
component["directRoute"](mockLink.url);
|
|
135
|
+
|
|
136
|
+
expect(component.router.navigate).toHaveBeenCalledWith([mockLink.url]);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Component, inject, Input, Signal } from "@angular/core";
|
|
2
|
+
import { ActivatedRoute, Router } from "@angular/router";
|
|
3
|
+
|
|
4
|
+
export interface INavContent {
|
|
5
|
+
title: string;
|
|
6
|
+
links: INavLink[];
|
|
7
|
+
permissions?: string[];
|
|
8
|
+
directRoute?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface INavLink {
|
|
11
|
+
text: string;
|
|
12
|
+
url: string;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
isValidationRequired?: boolean;
|
|
15
|
+
dataCy?: string;
|
|
16
|
+
directRoute?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@Component({
|
|
20
|
+
standalone: true,
|
|
21
|
+
selector: "cao-links-button",
|
|
22
|
+
templateUrl: "./links-button.html",
|
|
23
|
+
styleUrl: "./links-button.css",
|
|
24
|
+
})
|
|
25
|
+
export class LinksButton {
|
|
26
|
+
@Input({ required: true }) navContent!: Signal<INavContent>;
|
|
27
|
+
router = inject(Router);
|
|
28
|
+
route = inject(ActivatedRoute);
|
|
29
|
+
|
|
30
|
+
isValidationRequired(link: INavLink) {
|
|
31
|
+
if (link.disabled) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
link.directRoute ? this.directRoute(link.url) : this.navigateTo(link.url);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private navigateTo(url: string) {
|
|
38
|
+
this.router.navigate([url], { relativeTo: this.route });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private directRoute(url: string) {
|
|
42
|
+
this.router.navigate([url]);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<mat-paginator
|
|
2
|
+
#paginator
|
|
3
|
+
aria-label="Select page"
|
|
4
|
+
[length]="totalItems"
|
|
5
|
+
[pageSize]="pageSize"
|
|
6
|
+
[pageSizeOptions]="pageSizeOptions"
|
|
7
|
+
[pageIndex]="currentPage - 1"
|
|
8
|
+
[hidePageSize]="false"
|
|
9
|
+
[showFirstLastButtons]="showFirstLastButtons"
|
|
10
|
+
(page)="onPageEvent($event)" />
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { Paginator } from './paginator';
|
|
4
|
+
|
|
5
|
+
describe('Paginator', () => {
|
|
6
|
+
let component: Paginator;
|
|
7
|
+
let fixture: ComponentFixture<Paginator>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [Paginator]
|
|
12
|
+
})
|
|
13
|
+
.compileComponents();
|
|
14
|
+
|
|
15
|
+
fixture = TestBed.createComponent(Paginator);
|
|
16
|
+
component = fixture.componentInstance;
|
|
17
|
+
await fixture.whenStable();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should create', () => {
|
|
21
|
+
expect(component).toBeTruthy();
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
|
2
|
+
import { PageEvent } from '@angular/material/paginator';
|
|
3
|
+
|
|
4
|
+
import { MaterialModule } from '../../../material.module';
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector: 'cao-paginator',
|
|
8
|
+
imports: [MaterialModule],
|
|
9
|
+
templateUrl: './paginator.html',
|
|
10
|
+
styleUrl: './paginator.css',
|
|
11
|
+
})
|
|
12
|
+
export class Paginator {
|
|
13
|
+
@Input() totalItems = 0;
|
|
14
|
+
@Input() pageSize = 10;
|
|
15
|
+
@Input() currentPage = 1;
|
|
16
|
+
@Input() pageSizeOptions: number[] = [5, 10, 20, 50];
|
|
17
|
+
@Input() customClass = "";
|
|
18
|
+
@Input() showFirstLastButtons = true;
|
|
19
|
+
|
|
20
|
+
@Output() readonly pageEvent = new EventEmitter<PageEvent>();
|
|
21
|
+
|
|
22
|
+
onPageEvent(event: PageEvent): void {
|
|
23
|
+
this.pageEvent.emit(event)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<div class="step d-flex aic jcc" [ngClass]="{'active': isActive}"> {{ title }} </div>
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
+
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
|
3
|
+
|
|
4
|
+
import { StepComponent } from "./step";
|
|
5
|
+
import { By } from "@angular/platform-browser";
|
|
6
|
+
|
|
7
|
+
describe("StepComponent", () => {
|
|
8
|
+
let component: StepComponent;
|
|
9
|
+
let fixture: ComponentFixture<StepComponent>;
|
|
10
|
+
|
|
11
|
+
beforeEach(async () => {
|
|
12
|
+
await TestBed.configureTestingModule({
|
|
13
|
+
imports: [StepComponent],
|
|
14
|
+
}).compileComponents();
|
|
15
|
+
|
|
16
|
+
fixture = TestBed.createComponent(StepComponent);
|
|
17
|
+
component = fixture.componentInstance;
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("deve criar o componente", () => {
|
|
21
|
+
expect(component).toBeTruthy();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("deve exibir o título corretamente", async() => {
|
|
25
|
+
const testTitle = "Passo 1";
|
|
26
|
+
component.title = testTitle;
|
|
27
|
+
await fixture.whenStable() // Primeira detecção de mudanças
|
|
28
|
+
|
|
29
|
+
const element = fixture.debugElement.query(By.css(".step"));
|
|
30
|
+
expect(element.nativeElement.textContent.trim()).toBe(testTitle);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("deve aplicar a classe active quando isActive for true", async() => {
|
|
34
|
+
component.isActive = true;
|
|
35
|
+
await fixture.whenStable() // Primeira detecção de mudanças
|
|
36
|
+
|
|
37
|
+
const element = fixture.debugElement.query(By.css(".step"));
|
|
38
|
+
expect(element.nativeElement.classList.contains("active")).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("não deve aplicar a classe active quando isActive for false", async () => {
|
|
42
|
+
component.isActive = false;
|
|
43
|
+
await fixture.whenStable() // Primeira detecção de mudanças
|
|
44
|
+
|
|
45
|
+
const element = fixture.debugElement.query(By.css(".step"));
|
|
46
|
+
expect(element.nativeElement.classList.contains("active")).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("deve atualizar o estado ativo quando a propriedade isActive mudar", async() => {
|
|
50
|
+
// Configuração inicial
|
|
51
|
+
component.isActive = false;
|
|
52
|
+
|
|
53
|
+
let element = fixture.debugElement.query(By.css(".step"));
|
|
54
|
+
|
|
55
|
+
expect(element.nativeElement.classList.contains("active")).toBe(false);
|
|
56
|
+
|
|
57
|
+
// Mudança e nova detecção
|
|
58
|
+
component.isActive = true;
|
|
59
|
+
await fixture.whenStable()
|
|
60
|
+
element = fixture.debugElement.query(By.css(".step"));
|
|
61
|
+
|
|
62
|
+
expect(element.nativeElement.classList.contains("active")).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Testes adicionais que podem ser úteis
|
|
66
|
+
describe("Propriedades adicionais", () => {
|
|
67
|
+
it("deve ter title como string vazia por padrão", () => {
|
|
68
|
+
// Não chame fixture.detectChanges() aqui para evitar o erro
|
|
69
|
+
expect(component.title).toBe("");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("deve ter isActive como false por padrão", () => {
|
|
73
|
+
// Não chame fixture.detectChanges() aqui para evitar o erro
|
|
74
|
+
expect(component.isActive).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("deve manter o template renderizado corretamente", async() => {
|
|
78
|
+
await fixture.whenStable() // Apenas uma detecção
|
|
79
|
+
const element = fixture.debugElement.query(By.css(".step"));
|
|
80
|
+
expect(element).toBeTruthy();
|
|
81
|
+
expect(element.nativeElement).toBeInstanceOf(HTMLElement);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe("Comportamento de DOM", () => {
|
|
86
|
+
it("deve lidar com título vazio corretamente", async() => {
|
|
87
|
+
component.title = "";
|
|
88
|
+
await fixture.whenStable() // Apenas uma detecção
|
|
89
|
+
|
|
90
|
+
const element = fixture.debugElement.query(By.css(".step"));
|
|
91
|
+
expect(element.nativeElement.textContent.trim()).toBe("");
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("deve renderizar corretamente com título longo", async() => {
|
|
95
|
+
const longTitle = "Este é um título muito longo para o passo do stepper";
|
|
96
|
+
component.title = longTitle;
|
|
97
|
+
await fixture.whenStable() // Apenas uma detecção
|
|
98
|
+
|
|
99
|
+
const element = fixture.debugElement.query(By.css(".step"));
|
|
100
|
+
expect(element.nativeElement.textContent.trim()).toBe(longTitle);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Alternativa: Usar fixture.changeDetectorRef.detectChanges() com cuidado
|
|
105
|
+
describe("Solução alternativa para detecção de mudanças", () => {
|
|
106
|
+
it("deve evitar ExpressionChangedAfterItHasBeenCheckedError", () => {
|
|
107
|
+
// Solução 1: Usar async e await
|
|
108
|
+
const longTitle = "Este é um título muito longo para o passo do stepper";
|
|
109
|
+
component.title = longTitle;
|
|
110
|
+
|
|
111
|
+
// Detecção de mudanças com tratamento de erro
|
|
112
|
+
fixture.detectChanges(false); // Passar false para evitar throwOnError
|
|
113
|
+
|
|
114
|
+
const element = fixture.debugElement.query(By.css(".step"));
|
|
115
|
+
expect(element.nativeElement.textContent.trim()).toBe(longTitle);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("deve usar tick e fakeAsync para testes assíncronos", async () => {
|
|
119
|
+
// Solução 2: Usar fakeAsync (requer importação)
|
|
120
|
+
const longTitle = "Este é um título muito longo para o passo do stepper";
|
|
121
|
+
component.title = longTitle;
|
|
122
|
+
|
|
123
|
+
// Aguardar ciclo de detecção
|
|
124
|
+
await fixture.whenStable();
|
|
125
|
+
await fixture.whenStable()
|
|
126
|
+
|
|
127
|
+
const element = fixture.debugElement.query(By.css(".step"));
|
|
128
|
+
expect(element.nativeElement.textContent.trim()).toBe(longTitle);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { CommonModule } from "@angular/common";
|
|
2
|
+
import { Component, Input } from "@angular/core";
|
|
3
|
+
|
|
4
|
+
@Component({
|
|
5
|
+
selector: "cao-step",
|
|
6
|
+
standalone: true,
|
|
7
|
+
templateUrl: "./step.html",
|
|
8
|
+
styleUrl: "./step.css",
|
|
9
|
+
imports: [CommonModule],
|
|
10
|
+
})
|
|
11
|
+
export class StepComponent {
|
|
12
|
+
@Input() title = "";
|
|
13
|
+
@Input() isActive = false;
|
|
14
|
+
}
|