ngx-opalbytes-components 1.19.0 → 1.21.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 +456 -0
- package/fesm2022/ngx-opalbytes-components.mjs +23 -7
- 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/autocomplete/autocomplete.stories.ts +133 -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-button/base-button.stories.ts +102 -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.stories.ts +56 -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.stories.ts +137 -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.stories.ts +54 -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/base-toggle/base-toggle.stories.ts +63 -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/card/card.stories.ts +114 -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.stories.ts +91 -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.stories.ts +90 -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.stories.ts +51 -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.stories.ts +65 -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.stories.ts +75 -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.stories.ts +56 -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.stories.ts +77 -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.stories.ts +102 -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.stories.ts +54 -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.stories.ts +66 -0
- package/src/lib/shared/components/time-picker/time-picker.ts +90 -0
- package/types/ngx-opalbytes-components.d.ts +23 -4
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
+
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
|
3
|
+
import { FormsModule } from "@angular/forms";
|
|
4
|
+
import { CaoBaseInput } from "./base-input";
|
|
5
|
+
import { EventEmitter } from '@angular/core';
|
|
6
|
+
|
|
7
|
+
describe("CaoBaseInputComponent", () => {
|
|
8
|
+
let component: CaoBaseInput;
|
|
9
|
+
let fixture: ComponentFixture<CaoBaseInput>;
|
|
10
|
+
|
|
11
|
+
beforeEach(async () => {
|
|
12
|
+
await TestBed.configureTestingModule({
|
|
13
|
+
imports: [FormsModule, CaoBaseInput],
|
|
14
|
+
}).compileComponents();
|
|
15
|
+
|
|
16
|
+
fixture = TestBed.createComponent(CaoBaseInput);
|
|
17
|
+
component = fixture.componentInstance;
|
|
18
|
+
fixture.detectChanges();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("deve ser criado", () => {
|
|
22
|
+
expect(component).toBeTruthy();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("deve ter type e placeholder padrão definidos como 'text' e ''", () => {
|
|
26
|
+
expect(component.type).toBe("text");
|
|
27
|
+
expect(component.placeholder).toBe("");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("deve aplicar o placeholder corretamente", () => {
|
|
31
|
+
component.placeholder = "Digite aqui";
|
|
32
|
+
fixture.detectChanges();
|
|
33
|
+
const input = fixture.nativeElement.querySelector("input");
|
|
34
|
+
expect(input.placeholder).toBe("Digite aqui");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("deve aplicar o tipo de input corretamente", () => {
|
|
38
|
+
component.type = "password";
|
|
39
|
+
fixture.detectChanges();
|
|
40
|
+
const input = fixture.nativeElement.querySelector("input");
|
|
41
|
+
expect(input.type).toBe("password");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("deve aplicar o maxlength corretamente", () => {
|
|
45
|
+
component.maxLength = 5;
|
|
46
|
+
fixture.detectChanges();
|
|
47
|
+
const input = fixture.nativeElement.querySelector("input");
|
|
48
|
+
expect(input.getAttribute("maxlength")).toBe("5");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("deve escrever o valor corretamente", () => {
|
|
52
|
+
component.writeValue("valor inicial");
|
|
53
|
+
expect(component.value).toBe("valor inicial");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("deve lidar com valores nulos ou indefinidos no writeValue", () => {
|
|
57
|
+
component.writeValue(null);
|
|
58
|
+
expect(component.value).toBe("");
|
|
59
|
+
component.writeValue(undefined);
|
|
60
|
+
expect(component.value).toBe("");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("deve registrar função onChange corretamente", () => {
|
|
64
|
+
const mockFn = vi.fn();
|
|
65
|
+
component.registerOnChange(mockFn);
|
|
66
|
+
component.onChange("teste");
|
|
67
|
+
expect(mockFn).toHaveBeenCalledWith("teste");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("deve registrar função onTouched corretamente", () => {
|
|
71
|
+
const mockFn = vi.fn();
|
|
72
|
+
component.registerOnTouched(mockFn);
|
|
73
|
+
component.onTouched();
|
|
74
|
+
expect(mockFn).toHaveBeenCalledWith();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("deve desabilitar o input quando setDisabledState(true) é chamado", () => {
|
|
78
|
+
component.setDisabledState(true);
|
|
79
|
+
fixture.detectChanges();
|
|
80
|
+
expect(component.isDisabled).toBeTruthy();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("deve atualizar o valor quando o input é alterado", () => {
|
|
84
|
+
const input = fixture.nativeElement.querySelector("input");
|
|
85
|
+
input.value = "novo valor";
|
|
86
|
+
input.dispatchEvent(new Event("input"));
|
|
87
|
+
fixture.detectChanges();
|
|
88
|
+
expect(component.value).toBe("novo valor");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("deve emitir o evento valueChange quando o valor é alterado", () => {
|
|
92
|
+
const emitSpy = vi.spyOn(component.valueChange as EventEmitter<string>, 'emit');
|
|
93
|
+
const input = fixture.nativeElement.querySelector("input");
|
|
94
|
+
input.value = "valor teste";
|
|
95
|
+
input.dispatchEvent(new Event("input"));
|
|
96
|
+
fixture.detectChanges();
|
|
97
|
+
expect(emitSpy).toHaveBeenCalledWith("valor teste");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("deve emitir evento searchClick ao chamar onSearchClick", () => {
|
|
101
|
+
const emitSpy = vi.spyOn(component.searchClick as EventEmitter<void>, 'emit');
|
|
102
|
+
component.onSearchClick();
|
|
103
|
+
expect(emitSpy).toHaveBeenCalledWith();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("deve chamar onTouched quando o input perde o foco", () => {
|
|
107
|
+
const mockTouchedFn = vi.fn();
|
|
108
|
+
component.registerOnTouched(mockTouchedFn);
|
|
109
|
+
fixture.detectChanges();
|
|
110
|
+
|
|
111
|
+
const input = fixture.nativeElement.querySelector("input");
|
|
112
|
+
input.dispatchEvent(new Event("blur"));
|
|
113
|
+
|
|
114
|
+
expect(mockTouchedFn).toHaveBeenCalledWith();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("deve ativar isFocused ao focar", () => {
|
|
118
|
+
const input = fixture.nativeElement.querySelector("input");
|
|
119
|
+
input.dispatchEvent(new Event("focus"));
|
|
120
|
+
expect(component.isFocused).toBeTruthy();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("deve desativar isFocused ao desfocar", () => {
|
|
124
|
+
const input = fixture.nativeElement.querySelector("input");
|
|
125
|
+
input.dispatchEvent(new Event("focus"));
|
|
126
|
+
input.dispatchEvent(new Event("blur"));
|
|
127
|
+
expect(component.isFocused).toBeFalsy();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("deve retornar o valor de controlName via controlNameValue", () => {
|
|
131
|
+
component.controlName = "cpf";
|
|
132
|
+
expect(component.controlNameValue).toBe("cpf");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("deve aplicar ícone do material se isLucideIcon for true", () => {
|
|
136
|
+
component.isLucideIcon = true;
|
|
137
|
+
component.leadingIcon = "home";
|
|
138
|
+
expect(component._leadingIcon).toBe("home");
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("deve manter a máscara definida corretamente", () => {
|
|
142
|
+
component.mask = "000.000.000-00";
|
|
143
|
+
fixture.detectChanges();
|
|
144
|
+
expect(component.mask).toBe("000.000.000-00");
|
|
145
|
+
});
|
|
146
|
+
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/angular-vite';
|
|
2
|
+
import { fn } from 'storybook/test';
|
|
3
|
+
|
|
4
|
+
import { CaoBaseInput } from './base-input';
|
|
5
|
+
|
|
6
|
+
const meta: Meta<CaoBaseInput> = {
|
|
7
|
+
title: 'Componentes/Base Input',
|
|
8
|
+
component: CaoBaseInput,
|
|
9
|
+
tags: ['autodocs'],
|
|
10
|
+
argTypes: {
|
|
11
|
+
type: {
|
|
12
|
+
control: 'select',
|
|
13
|
+
options: ['text', 'textarea', 'number', 'password', 'date'],
|
|
14
|
+
},
|
|
15
|
+
placeholder: { control: 'text' },
|
|
16
|
+
inputClass: { control: 'text' },
|
|
17
|
+
value: { control: 'text' },
|
|
18
|
+
errorMessage: { control: 'text' },
|
|
19
|
+
isDisabled: { control: 'boolean' },
|
|
20
|
+
maxLength: { control: { type: 'number', min: 1 } },
|
|
21
|
+
searchMode: { control: 'boolean' },
|
|
22
|
+
showBorder: { control: 'boolean' },
|
|
23
|
+
isLucideIcon: { control: 'boolean' },
|
|
24
|
+
iconColor: { control: 'color' },
|
|
25
|
+
mask: { control: 'text' },
|
|
26
|
+
tooltip: { control: 'text' },
|
|
27
|
+
dataCy: { control: 'text' },
|
|
28
|
+
textareaRows: { control: 'text' },
|
|
29
|
+
decimalMarker: { control: 'select', options: ['.', ','] },
|
|
30
|
+
thousandSeparator: { control: 'text' },
|
|
31
|
+
allowNegativeNumbers: { control: 'boolean' },
|
|
32
|
+
separatorLimit: { control: 'text' },
|
|
33
|
+
},
|
|
34
|
+
args: {
|
|
35
|
+
valueChange: fn(),
|
|
36
|
+
searchClick: fn(),
|
|
37
|
+
inputChange: fn(),
|
|
38
|
+
placeholder: 'Digite algo...',
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default meta;
|
|
43
|
+
type Story = StoryObj<CaoBaseInput>;
|
|
44
|
+
|
|
45
|
+
export const Padrao: Story = {
|
|
46
|
+
args: {
|
|
47
|
+
placeholder: 'Digite seu nome',
|
|
48
|
+
type: 'text',
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const ComLeadingIcon: Story = {
|
|
53
|
+
args: {
|
|
54
|
+
placeholder: 'Buscar usuário',
|
|
55
|
+
type: 'text',
|
|
56
|
+
leadingIcon: 'Search',
|
|
57
|
+
isLucideIcon: true,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const ComTrailingIcon: Story = {
|
|
62
|
+
args: {
|
|
63
|
+
placeholder: 'Buscar usuário',
|
|
64
|
+
type: 'text',
|
|
65
|
+
trailingIcon: 'ChevronDown',
|
|
66
|
+
isLucideIcon: true,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const ComAmbosIcones: Story = {
|
|
71
|
+
args: {
|
|
72
|
+
placeholder: 'Valor monetário',
|
|
73
|
+
type: 'text',
|
|
74
|
+
leadingIcon: 'DollarSign',
|
|
75
|
+
trailingIcon: 'X',
|
|
76
|
+
isLucideIcon: true,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const PadraoComIconeSVG: Story = {
|
|
81
|
+
args: {
|
|
82
|
+
placeholder: 'Buscar',
|
|
83
|
+
type: 'text',
|
|
84
|
+
leadingIcon: 'https://lucide.dev/icons/search.svg',
|
|
85
|
+
isLucideIcon: false,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const AreaDeTexto: Story = {
|
|
90
|
+
args: {
|
|
91
|
+
placeholder: 'Descreva o problema...',
|
|
92
|
+
type: 'textarea',
|
|
93
|
+
textareaRows: '4',
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const Senha: Story = {
|
|
98
|
+
args: {
|
|
99
|
+
placeholder: 'Digite sua senha',
|
|
100
|
+
type: 'password',
|
|
101
|
+
trailingIcon: 'Eye',
|
|
102
|
+
isLucideIcon: true,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export const ModoSearch: Story = {
|
|
107
|
+
args: {
|
|
108
|
+
placeholder: 'Pesquisar itens...',
|
|
109
|
+
type: 'text',
|
|
110
|
+
searchMode: true,
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const Desabilitado: Story = {
|
|
115
|
+
args: {
|
|
116
|
+
placeholder: 'Campo desabilitado',
|
|
117
|
+
type: 'text',
|
|
118
|
+
isDisabled: true,
|
|
119
|
+
value: 'Valor bloqueado',
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export const ComErro: Story = {
|
|
124
|
+
args: {
|
|
125
|
+
placeholder: 'Digite seu e-mail',
|
|
126
|
+
type: 'text',
|
|
127
|
+
errorMessage: 'E-mail obrigatório',
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export const ComMascara: Story = {
|
|
132
|
+
args: {
|
|
133
|
+
placeholder: '00.000.000/0000-00',
|
|
134
|
+
type: 'text',
|
|
135
|
+
mask: '00.000.000/0000-00',
|
|
136
|
+
},
|
|
137
|
+
};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { CommonModule } from "@angular/common";
|
|
2
|
+
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
|
3
|
+
import { AbstractControl, ControlValueAccessor, NG_VALUE_ACCESSOR, FormsModule } from "@angular/forms";
|
|
4
|
+
import { MatTooltipModule } from "@angular/material/tooltip";
|
|
5
|
+
|
|
6
|
+
import { LucideDynamicIcon } from "@lucide/angular";
|
|
7
|
+
import { NgxMaskDirective, provideNgxMask } from "ngx-mask";
|
|
8
|
+
|
|
9
|
+
export type CaoTypeInputs = 'text' | 'textarea' | 'number' | 'password' | 'date'
|
|
10
|
+
|
|
11
|
+
@Component({
|
|
12
|
+
standalone: true,
|
|
13
|
+
selector: "cao-input",
|
|
14
|
+
templateUrl: "./base-input.html",
|
|
15
|
+
styleUrls: ["./base-input.css"],
|
|
16
|
+
imports: [CommonModule, FormsModule, NgxMaskDirective, MatTooltipModule, LucideDynamicIcon],
|
|
17
|
+
providers: [
|
|
18
|
+
{
|
|
19
|
+
provide: NG_VALUE_ACCESSOR,
|
|
20
|
+
useExisting: CaoBaseInput,
|
|
21
|
+
multi: true,
|
|
22
|
+
},
|
|
23
|
+
provideNgxMask(),
|
|
24
|
+
],
|
|
25
|
+
})
|
|
26
|
+
export class CaoBaseInput implements ControlValueAccessor {
|
|
27
|
+
@Input() type: CaoTypeInputs = "text" ;
|
|
28
|
+
@Input() placeholder = "";
|
|
29
|
+
@Input() inputClass = "";
|
|
30
|
+
@Input() value: string | number | boolean | Date = "";
|
|
31
|
+
@Input() errorMessage = "";
|
|
32
|
+
@Input() isDisabled = false;
|
|
33
|
+
@Input() maxLength?: number;
|
|
34
|
+
@Input() searchMode = false;
|
|
35
|
+
@Input() showBorder = false;
|
|
36
|
+
@Input() isLucideIcon = false;
|
|
37
|
+
@Input() iconColor = "";
|
|
38
|
+
@Input() mask?: string;
|
|
39
|
+
@Input() control?: AbstractControl | null;
|
|
40
|
+
@Input() tooltip = "";
|
|
41
|
+
@Input() dataCy?: string = "";
|
|
42
|
+
@Input() textareaRows?: string = "";
|
|
43
|
+
|
|
44
|
+
@Input() decimalMarker: "." | "," | [".", ","] = ",";
|
|
45
|
+
@Input() thousandSeparator = ".";
|
|
46
|
+
@Input() allowNegativeNumbers = false;
|
|
47
|
+
@Input() separatorLimit = "";
|
|
48
|
+
|
|
49
|
+
@Input() set leadingIcon(value: string) {
|
|
50
|
+
this._leadingIcon = value;
|
|
51
|
+
this.icon = true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@Input() set trailingIcon(value: string) {
|
|
55
|
+
this._trailingIcon = value;
|
|
56
|
+
this.icon = true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@Output() readonly valueChange = new EventEmitter<string>();
|
|
60
|
+
@Output() readonly searchClick = new EventEmitter<void>();
|
|
61
|
+
@Output() readonly inputChange = new EventEmitter<string>();
|
|
62
|
+
|
|
63
|
+
isFocused = false;
|
|
64
|
+
icon = false;
|
|
65
|
+
_leadingIcon?: string;
|
|
66
|
+
_trailingIcon?: string;
|
|
67
|
+
|
|
68
|
+
onChange: any = () => { };
|
|
69
|
+
onTouched: any = () => { };
|
|
70
|
+
|
|
71
|
+
get hasError() {
|
|
72
|
+
return !!this.errorMessage;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@Input() controlName = "";
|
|
76
|
+
|
|
77
|
+
get controlNameValue(): string {
|
|
78
|
+
return this.controlName;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
onInput(event: Event) {
|
|
82
|
+
const newValue = (event.target as HTMLInputElement).value;
|
|
83
|
+
this.value = newValue;
|
|
84
|
+
this.onChange(newValue);
|
|
85
|
+
this.valueChange.emit(newValue);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
onInputChange(event: Event) {
|
|
89
|
+
const newValue = (event.target as HTMLInputElement).value;
|
|
90
|
+
this.value = newValue;
|
|
91
|
+
this.inputChange.emit(newValue);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
onFocus() {
|
|
95
|
+
this.isFocused = true;
|
|
96
|
+
this.onTouched();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
onBlur() {
|
|
100
|
+
this.isFocused = false;
|
|
101
|
+
this.onTouched();
|
|
102
|
+
|
|
103
|
+
if (this.control?.touched && this.control?.valid && this.control?.value) {
|
|
104
|
+
setTimeout(() => {
|
|
105
|
+
const active = document.activeElement as HTMLElement;
|
|
106
|
+
const current = document.querySelector(`[formcontrolname="${this.controlNameValue}"]`);
|
|
107
|
+
if (!current || !active || !current.contains(active)) {
|
|
108
|
+
this.control?.markAsUntouched();
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
onSearchClick() {
|
|
115
|
+
this.searchClick.emit();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
writeValue(value: string | null | undefined): void {
|
|
119
|
+
this.value = value || "";
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
registerOnChange(fn: any): void {
|
|
123
|
+
this.onChange = fn;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
registerOnTouched(fn: any): void {
|
|
127
|
+
this.onTouched = fn;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
setDisabledState(isDisabled: boolean): void {
|
|
131
|
+
this.isDisabled = isDisabled;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
.input-data-field {
|
|
2
|
+
height: 44px;
|
|
3
|
+
border-radius: 8px;
|
|
4
|
+
font-size: 12px;
|
|
5
|
+
font-weight: 600;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.input-data-field.mat-mdc-text-field-wrapper {
|
|
9
|
+
height: 100% !important;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.input-data-field.mat-mdc-form-field-flex {
|
|
13
|
+
height: 100% !important;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.input-data-field.mat-mdc-form-field-infix {
|
|
17
|
+
height: 100%;
|
|
18
|
+
padding: 10px 0 !important;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.input-data-field.mat-mdc-notch-piece {
|
|
22
|
+
border: none;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.input-data-field.mat-date-range-input-container {
|
|
26
|
+
font-size: 12px;
|
|
27
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<label class="label-dropw-down" [for]="controlName">{{ label }}</label>
|
|
2
|
+
<mat-form-field class="input-data-field" subscriptSizing="dynamic" appearance="outline">
|
|
3
|
+
<mat-date-range-input [rangePicker]="picker" [max]="maxDate"
|
|
4
|
+
[attr.dataCy]="this.dataCy"
|
|
5
|
+
>
|
|
6
|
+
<input
|
|
7
|
+
#startDateInput
|
|
8
|
+
matStartDate
|
|
9
|
+
placeholder="DATA INÍCIO"
|
|
10
|
+
[tabIndex]="1"
|
|
11
|
+
[value]="startDate"
|
|
12
|
+
(dateInput)="onSelectStartDate($event)" />
|
|
13
|
+
<input
|
|
14
|
+
#endDateInput
|
|
15
|
+
matEndDate
|
|
16
|
+
placeholder="DATA FIM"
|
|
17
|
+
[tabIndex]="2"
|
|
18
|
+
[value]="finalDate"
|
|
19
|
+
(dateInput)="onInputMaxDate($event)" />
|
|
20
|
+
</mat-date-range-input>
|
|
21
|
+
<mat-datepicker-toggle matIconSuffix [for]="picker" />
|
|
22
|
+
<mat-date-range-picker #picker />
|
|
23
|
+
</mat-form-field>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { provideNativeDateAdapter } from '@angular/material/core';
|
|
2
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
3
|
+
|
|
4
|
+
import { BaseTimeRange } from './base-time-range';
|
|
5
|
+
|
|
6
|
+
describe('BaseTimeRange', () => {
|
|
7
|
+
let component: BaseTimeRange;
|
|
8
|
+
let fixture: ComponentFixture<BaseTimeRange>;
|
|
9
|
+
|
|
10
|
+
beforeEach(async () => {
|
|
11
|
+
await TestBed.configureTestingModule({
|
|
12
|
+
imports: [BaseTimeRange],
|
|
13
|
+
providers: [
|
|
14
|
+
provideNativeDateAdapter()
|
|
15
|
+
]
|
|
16
|
+
})
|
|
17
|
+
.compileComponents();
|
|
18
|
+
|
|
19
|
+
fixture = TestBed.createComponent(BaseTimeRange);
|
|
20
|
+
component = fixture.componentInstance;
|
|
21
|
+
await fixture.whenStable();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should create', () => {
|
|
25
|
+
expect(component).toBeTruthy();
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/angular-vite';
|
|
2
|
+
import { fn } from 'storybook/test';
|
|
3
|
+
|
|
4
|
+
import { BaseTimeRange } from './base-time-range';
|
|
5
|
+
|
|
6
|
+
const meta: Meta<BaseTimeRange> = {
|
|
7
|
+
title: 'Componentes/Base Time Range',
|
|
8
|
+
component: BaseTimeRange,
|
|
9
|
+
tags: ['autodocs'],
|
|
10
|
+
argTypes: {
|
|
11
|
+
label: { control: 'text' },
|
|
12
|
+
dataCy: { control: 'text' },
|
|
13
|
+
controlName: { control: 'text' },
|
|
14
|
+
},
|
|
15
|
+
args: {
|
|
16
|
+
rangeSelected: fn(),
|
|
17
|
+
label: 'Período',
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default meta;
|
|
22
|
+
type Story = StoryObj<BaseTimeRange>;
|
|
23
|
+
|
|
24
|
+
export const Padrao: Story = {
|
|
25
|
+
args: {
|
|
26
|
+
label: 'Período de análise',
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const ComMinimoEMaximo: Story = {
|
|
31
|
+
args: {
|
|
32
|
+
label: 'Período de análise',
|
|
33
|
+
minDate: new Date(2026, 0, 1),
|
|
34
|
+
maxDate: new Date(2026, 11, 31),
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const ComInicioEFinal: Story = {
|
|
39
|
+
args: {
|
|
40
|
+
label: 'Período de análise',
|
|
41
|
+
startDate: new Date(2026, 2, 1),
|
|
42
|
+
finalDate: new Date(2026, 2, 15),
|
|
43
|
+
minDate: new Date(2026, 0, 1),
|
|
44
|
+
maxDate: new Date(2026, 11, 31),
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const ComDataCy: Story = {
|
|
49
|
+
args: {
|
|
50
|
+
label: 'Período de análise',
|
|
51
|
+
dataCy: 'time-range-analise',
|
|
52
|
+
controlName: 'periodo',
|
|
53
|
+
},
|
|
54
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
|
|
2
|
+
import { MatDatepickerModule, MatDateRangeInput } from '@angular/material/datepicker';
|
|
3
|
+
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
4
|
+
import { MatInputModule } from '@angular/material/input';
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector: 'cao-base-time-range',
|
|
8
|
+
imports: [MatFormFieldModule, MatInputModule, MatDatepickerModule],
|
|
9
|
+
templateUrl: './base-time-range.html',
|
|
10
|
+
styleUrl: './base-time-range.css',
|
|
11
|
+
})
|
|
12
|
+
export class BaseTimeRange implements OnInit{
|
|
13
|
+
@Input() label = "";
|
|
14
|
+
@Input() max: Date | undefined;
|
|
15
|
+
@Input() minDate: Date | undefined;
|
|
16
|
+
@Input() maxDate: Date | undefined;
|
|
17
|
+
@Input() startDate: Date | undefined;
|
|
18
|
+
@Input() finalDate: Date | undefined;
|
|
19
|
+
@Input() dataCy?: string
|
|
20
|
+
@Input() controlName = "";
|
|
21
|
+
|
|
22
|
+
@Output() readonly rangeSelected = new EventEmitter<Date[]>();
|
|
23
|
+
|
|
24
|
+
@ViewChild(MatDateRangeInput) dateRangeInput!: MatDateRangeInput<Date>;
|
|
25
|
+
|
|
26
|
+
ngOnInit(): void {
|
|
27
|
+
if(!this.dataCy){
|
|
28
|
+
this.dataCy = `time-range-${this.label}`
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
onSelectStartDate(event: any) {
|
|
33
|
+
const date = event.target.value as Date;
|
|
34
|
+
this.startDate = date;
|
|
35
|
+
this.finalDate = undefined;
|
|
36
|
+
|
|
37
|
+
const maxDate = new Date(date);
|
|
38
|
+
maxDate.setDate(maxDate.getDate() + 30);
|
|
39
|
+
|
|
40
|
+
this.maxDate = maxDate;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
onInputMaxDate(event: any) {
|
|
44
|
+
const finalDate = event.target.value as Date;
|
|
45
|
+
if (this.startDate && finalDate) {
|
|
46
|
+
const data = [this.startDate, finalDate];
|
|
47
|
+
this.rangeSelected.emit(data);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
clearInputs() {
|
|
52
|
+
this.max = undefined;
|
|
53
|
+
this.minDate = undefined;
|
|
54
|
+
this.maxDate = undefined;
|
|
55
|
+
this.startDate = undefined;
|
|
56
|
+
this.finalDate = undefined;
|
|
57
|
+
|
|
58
|
+
this.dateRangeInput._startInput.value = "";
|
|
59
|
+
this.dateRangeInput._endInput.value = "";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<div class="d-flex col aic jcc base-toggle">
|
|
2
|
+
<input #checkbox type="checkbox" class="checkbox" [checked]="isChecked" [disabled]="isDisabled" />
|
|
3
|
+
<div
|
|
4
|
+
class="d-flex checkbox-element aic jcsb"
|
|
5
|
+
tabindex="0"
|
|
6
|
+
role="button"
|
|
7
|
+
[class.activated-toggle]="isChecked"
|
|
8
|
+
(click)="onToggleCheckbox()"
|
|
9
|
+
(keydown.enter)="onToggleCheckbox()"
|
|
10
|
+
(keydown.space)="onToggleCheckbox(); $event.preventDefault()">
|
|
11
|
+
@if (isChecked) {
|
|
12
|
+
<mat-icon class="eye-icon" />
|
|
13
|
+
<div class="check-text"> Ativada </div>
|
|
14
|
+
<div class="line"></div>
|
|
15
|
+
<p class="unchecked">Desativar</p>
|
|
16
|
+
}@else {
|
|
17
|
+
<mat-icon class="fa-circle" />
|
|
18
|
+
<div class="check-text"> Não ativada </div>
|
|
19
|
+
<div class="line"></div>
|
|
20
|
+
<p>Ativar</p>
|
|
21
|
+
}
|
|
22
|
+
</div>
|
|
23
|
+
@if(isChecked){
|
|
24
|
+
<span>{{ checkedTextPlaceholder }} ativada com sucesso</span>
|
|
25
|
+
} @else{
|
|
26
|
+
<span>Clique para ativar {{ uncheckedTextPlaceholder }}</span>
|
|
27
|
+
}
|
|
28
|
+
</div>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
.base-toggle {
|
|
2
|
+
input {
|
|
3
|
+
display: none;
|
|
4
|
+
visibility: hidden;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.checkbox-element {
|
|
8
|
+
border-radius: 24px;
|
|
9
|
+
cursor: pointer;
|
|
10
|
+
gap: 10px;
|
|
11
|
+
padding: 10px 20px;
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
justify-content: center;
|
|
15
|
+
border: 2px solid var(--border-toggle);
|
|
16
|
+
background-color: transparent;
|
|
17
|
+
|
|
18
|
+
.check-text {
|
|
19
|
+
font-weight: 700;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.line {
|
|
23
|
+
height: 24px;
|
|
24
|
+
width: 1px;
|
|
25
|
+
background-color: var(--data-bg);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&.activated-toggle {
|
|
29
|
+
border: 2px solid var(--border-toggle-checked);
|
|
30
|
+
background-color: var(--bg-toggle-checked);
|
|
31
|
+
|
|
32
|
+
.check-text {
|
|
33
|
+
color: var(--text-toggle-checked);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.unchecked {
|
|
37
|
+
color: var(--text-unselected-toggle-checked);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
p {
|
|
42
|
+
margin: 0;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|