Skip to content

Commit 4d973f4

Browse files
CSimoesJrarthur-polidorio
authored andcommitted
feat(chart): implementa grafico tipo linha
Implementa propriedades para o grafico; Implementa testes unitários. Fixes DTHFUI-11041
1 parent 9b766fe commit 4d973f4

21 files changed

+2263
-20
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
"colors": "1.4.0",
8181
"core-js": "3.33.3",
8282
"custom-idle-queue": "3.0.1",
83+
"echarts": "^5.6.0",
8384
"eslint-plugin-sonarjs": "^0.23.0",
8485
"gulp-clean": "^0.4.0",
8586
"gulp-run": "^1.7.1",

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

+3
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import { PoToolbarModule } from './po-toolbar/po-toolbar.module';
4747
import { PoTreeViewModule } from './po-tree-view/po-tree-view.module';
4848
import { PoWidgetModule } from './po-widget/po-widget.module';
4949
import { PoToasterModule } from './po-toaster';
50+
import { PoChartNewModule } from './po-chart-new/po-chart-new.module';
5051
@NgModule({
5152
imports: [
5253
PoAccordionModule,
@@ -56,6 +57,7 @@ import { PoToasterModule } from './po-toaster';
5657
PoButtonGroupModule,
5758
PoCalendarModule,
5859
PoChartModule,
60+
PoChartNewModule,
5961
PoContainerModule,
6062
PoDisclaimerGroupModule,
6163
PoDisclaimerModule,
@@ -105,6 +107,7 @@ import { PoToasterModule } from './po-toaster';
105107
PoButtonGroupModule,
106108
PoCalendarModule,
107109
PoChartModule,
110+
PoChartNewModule,
108111
PoContainerModule,
109112
PoDisclaimerGroupModule,
110113
PoDisclaimerModule,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { PoChartNewBaseComponent } from './po-chart-new-base.component';
2+
import { PoLanguageService } from '../../services/po-language/po-language.service';
3+
import { PoChartLiterals } from '../po-chart/interfaces/po-chart-literals.interface';
4+
import { PoChartType } from '../po-chart/enums/po-chart-type.enum';
5+
import { poChartLiteralsDefault } from '../po-chart/interfaces/po-chart-literals-default.interface';
6+
7+
class PoChartNewBaseComponentMock extends PoChartNewBaseComponent {}
8+
9+
describe('PoChartNewBaseComponent', () => {
10+
let component: PoChartNewBaseComponentMock;
11+
let languageService: PoLanguageService;
12+
13+
const mockSeries = [{ label: 'Serie 1', data: [1, 2, 3] }];
14+
const mockCategories = ['Jan', 'Fev', 'Mar'];
15+
const mockLiterals: PoChartLiterals = { downloadCSV: 'Custom Download' };
16+
17+
beforeEach(() => {
18+
languageService = new PoLanguageService();
19+
component = new PoChartNewBaseComponentMock(languageService);
20+
});
21+
22+
it('should be created', () => {
23+
expect(component).toBeTruthy();
24+
});
25+
26+
describe('Properties:', () => {
27+
it('p-title: should set title correctly', () => {
28+
const testTitle = 'Chart Title';
29+
component.title = testTitle;
30+
expect(component.title).toBe(testTitle);
31+
});
32+
33+
it('p-series: should set series correctly', () => {
34+
component.series = mockSeries;
35+
expect(component.series).toEqual(mockSeries);
36+
});
37+
38+
it('p-categories: should set categories correctly', () => {
39+
component.categories = mockCategories;
40+
expect(component.categories).toEqual(mockCategories);
41+
});
42+
43+
it('p-height: should handle height correctly with default, valid and minimum values', () => {
44+
expect(component.height).toBe(400);
45+
46+
component.height = 300;
47+
expect(component.height).toBe(300);
48+
49+
component.height = 100;
50+
expect(component.height).toBe(200);
51+
52+
component.height = null;
53+
expect(component.height).toBe(400);
54+
55+
component.height = undefined;
56+
expect(component.height).toBe(400);
57+
});
58+
59+
it('p-type: should set type correctly', () => {
60+
component.type = PoChartType.Line;
61+
expect(component.type).toBe(PoChartType.Line);
62+
});
63+
64+
it('p-literals: should set literals correctly', () => {
65+
component.literals = mockLiterals;
66+
expect(component.literals.downloadCSV).toBe('Custom Download');
67+
});
68+
69+
it('p-literals: should use default literals if not provided', () => {
70+
expect(component.literals).toEqual(poChartLiteralsDefault[languageService.getShortLanguage()]);
71+
});
72+
73+
it('p-literals: should use default literals if value is invalid', () => {
74+
component.literals = null;
75+
expect(component.literals).toEqual(poChartLiteralsDefault[languageService.getShortLanguage()]);
76+
77+
component.literals = undefined;
78+
expect(component.literals).toEqual(poChartLiteralsDefault[languageService.getShortLanguage()]);
79+
80+
component.literals = 'invalid' as any;
81+
expect(component.literals).toEqual(poChartLiteralsDefault[languageService.getShortLanguage()]);
82+
83+
component.literals = 123 as any;
84+
expect(component.literals).toEqual(poChartLiteralsDefault[languageService.getShortLanguage()]);
85+
86+
component.literals = [] as any;
87+
expect(component.literals).toEqual(poChartLiteralsDefault[languageService.getShortLanguage()]);
88+
});
89+
90+
it('t-id: should set id correctly', () => {
91+
const testId = 'customId';
92+
component.id = testId;
93+
expect(component.id).toBe(testId);
94+
});
95+
});
96+
97+
describe('Outputs:', () => {
98+
it('p-series-click: should emit event', () => {
99+
const mockEvent = { label: 'Test', value: 10 };
100+
spyOn(component.seriesClick, 'emit');
101+
102+
component.seriesClick.emit(mockEvent);
103+
expect(component.seriesClick.emit).toHaveBeenCalledWith(mockEvent);
104+
});
105+
106+
it('p-series-hover: should emit event', () => {
107+
const mockEvent = { label: 'Test', value: 10 };
108+
spyOn(component.seriesHover, 'emit');
109+
110+
component.seriesHover.emit(mockEvent);
111+
expect(component.seriesHover.emit).toHaveBeenCalledWith(mockEvent);
112+
});
113+
});
114+
115+
describe('Constructor:', () => {
116+
it('should set language based on languageService', () => {
117+
const shortLanguage = languageService.getShortLanguage();
118+
expect(component['language']).toBe(shortLanguage);
119+
});
120+
});
121+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
import { Directive, EventEmitter, Input, Output } from '@angular/core';
2+
import { poLocaleDefault } from '../../services/po-language/po-language.constant';
3+
import { PoLanguageService } from '../../services/po-language/po-language.service';
4+
import { PoChartType } from '../po-chart/enums/po-chart-type.enum';
5+
import { poChartLiteralsDefault } from '../po-chart/interfaces/po-chart-literals-default.interface';
6+
import { PoChartLiterals } from '../po-chart/interfaces/po-chart-literals.interface';
7+
import { PoChartOptions } from '../po-chart/interfaces/po-chart-options.interface';
8+
import { PoChartDataLabel } from '../po-chart/interfaces/po-chart-serie-data-label.interface';
9+
import { PoChartSerie } from '../po-chart/interfaces/po-chart-serie.interface';
10+
import { PoPopupAction } from '../po-popup';
11+
12+
const poChartMinHeight = 200;
13+
const poChartDefaultHeight = 400;
14+
15+
/**
16+
* @description
17+
*
18+
* O `po-chart` é um componente para renderização de dados através de gráficos, com isso facilitando a compreensão e tornando a
19+
* visualização destes dados mais agradável.
20+
*
21+
* Através de suas principais propriedades é possível definir atributos, tais como tipo de gráfico, altura, título, cores customizadas, opções para os eixos, entre outros.
22+
*
23+
* O componente permite utilizar em conjunto séries do tipo linha e coluna.
24+
*
25+
* Além disso, também é possível definir uma ação que será executada ao clicar em determinado elemento do gráfico
26+
* e outra que será executada ao passar o *mouse* sobre o elemento.
27+
*
28+
* #### Guia de uso para Gráficos
29+
*
30+
* > Veja nosso [guia de uso para gráficos](/guides/guide-charts) para auxiliar na construção do seu gráfico,
31+
* informando em qual caso utilizar, o que devemos evitar e boas práticas relacionada a cores.
32+
*/
33+
@Directive()
34+
export abstract class PoChartNewBaseComponent {
35+
private _literals?: PoChartLiterals;
36+
private language: string;
37+
38+
@Input('t-id') id: string = 'myChart';
39+
40+
/** Define o título do gráfico. */
41+
@Input('p-title') title?: string;
42+
43+
/**
44+
* @description
45+
*
46+
* Define os elementos do gráfico que serão criados dinamicamente.
47+
*/
48+
@Input('p-series') series: Array<PoChartSerie>;
49+
50+
/**
51+
* @optional
52+
*
53+
* @description
54+
*
55+
* Define os nomes das categorias que serão plotadas no eixo X do gráfico caso seja do tipo `bar`, ou então nos eixos Y do grid de gráficos dos tipos `area`, `columnn` e `line`.
56+
*
57+
* > Gráficos do tipo `bar` dimensionam a área do gráfico de acordo com a largura do maior texto de categorias. No entanto, é uma boa prática optar por palavras curtas para que a leitura do gráfico não seja prejudicada.
58+
*
59+
* > Caso não seja especificado um valor para a categoria, será plotado um hífen na categoria referente a cada série.
60+
*/
61+
@Input('p-categories') categories?: Array<string>;
62+
63+
@Input('p-custom-actions') customActions?: Array<PoPopupAction>;
64+
65+
/**
66+
* @optional
67+
*
68+
* @description
69+
*
70+
* Objeto com as configurações usadas no `po-chart`.
71+
*
72+
* É possível, por exemplo, definir as configurações de exibição das legendas,
73+
* configurar os eixos(*axis*) para os gráficos dos tipos `area`, `line`, `column` e `bar` da seguinte forma:
74+
*
75+
* ```
76+
* chartOptions: PoChartOptions = {
77+
* legend: true,
78+
* axis: {
79+
* minRange: 0,
80+
* maxRange: 100,
81+
* gridLines: 5,
82+
* },
83+
* };
84+
* ```
85+
*/
86+
@Input('p-options') options?: PoChartOptions;
87+
88+
/**
89+
* @optional
90+
*
91+
* @description
92+
*
93+
* Permite configurar as propriedades de exibição dos rótulos das séries no gráfico.
94+
*
95+
* Essa configuração possibilita fixar os valores das séries diretamente no gráfico, alterando o comportamento visual:
96+
* - Os valores das séries permanecem visíveis, sem a necessidade de hover.
97+
* - O *tooltip* não será exibido.
98+
* - Os marcadores (*bullets*) terão seu estilo ajustado.
99+
* - As outras séries ficarão com opacidade reduzida ao passar o mouse sobre a série ativa.
100+
*
101+
* > Disponível apenas para gráficos do tipo `line`.
102+
*
103+
* #### Exemplo de utilização:
104+
* ```typescript
105+
* dataLabel: PoChartDataLabel = {
106+
* fixed: true,
107+
* };
108+
* ```
109+
*/
110+
@Input('p-data-label') dataLabel?: PoChartDataLabel;
111+
112+
/**
113+
* @optional
114+
*
115+
* @description
116+
*
117+
* Define a altura do gráfico em px.
118+
*
119+
* > O valor mínimo aceito nesta propriedade é 200.
120+
*
121+
* @default `400`
122+
*/
123+
@Input('p-height')
124+
set height(value: number) {
125+
this._height = Math.max(value ?? poChartDefaultHeight, poChartMinHeight);
126+
}
127+
128+
get height(): number {
129+
return this._height;
130+
}
131+
132+
private _height: number = poChartDefaultHeight;
133+
134+
/**
135+
* @optional
136+
*
137+
* @description
138+
*
139+
* Define o tipo de gráfico.
140+
*
141+
* É possível também combinar gráficos dos tipos linha e coluna. Para isso, opte pela declaração de `type` conforme a interface `PoChartSerie`.
142+
*
143+
* > Note que, se houver declaração de tipo de gráfico tanto em `p-type` quanto em `PochartSerie.type`, o valor `{ type }` da primeira série anulará o valor definido em `p-type`.
144+
*
145+
* Se não passado valor, o padrão será relativo à primeira série passada em `p-series`:
146+
* - Se `p-series = [{ data: [1,2,3] }]`: será `PoChartType.Column`.
147+
* - Se `p-series = [{ data: 1 }]`: será `PoChartType.Pie`.
148+
*
149+
* > Veja os valores válidos no *enum* `PoChartType`.
150+
*/
151+
@Input('p-type') type: PoChartType;
152+
153+
/**
154+
* @optional
155+
*
156+
* @description
157+
*
158+
* Objeto com as literais usadas no `po-chart`.
159+
*
160+
* Para utilizar basta passar a literal que deseja customizar:
161+
*
162+
* ```
163+
* const customLiterals: PoChartLiterals = {
164+
* downloadCSV: 'Obter CSV',
165+
* };
166+
* ```
167+
*
168+
* E para carregar a literal customizada, basta apenas passar o objeto para o componente.
169+
*
170+
* ```
171+
* <po-chart
172+
* [p-literals]="customLiterals">
173+
* </po-chart>
174+
* ```
175+
*
176+
* > O objeto padrão de literais será traduzido de acordo com o idioma do
177+
* [`PoI18nService`](/documentation/po-i18n) ou do browser.
178+
*/
179+
@Input('p-literals') set literals(value: PoChartLiterals) {
180+
if (value instanceof Object && !(value instanceof Array)) {
181+
this._literals = {
182+
...poChartLiteralsDefault[poLocaleDefault],
183+
...poChartLiteralsDefault[this.language],
184+
...value
185+
};
186+
} else {
187+
this._literals = poChartLiteralsDefault[this.language];
188+
}
189+
}
190+
191+
get literals() {
192+
return this._literals || poChartLiteralsDefault[this.language];
193+
}
194+
195+
/**
196+
* @optional
197+
*
198+
* @description
199+
*
200+
* Evento executado quando o usuário clicar sobre um elemento do gráfico.
201+
*
202+
* O evento emitirá o seguinte parâmetro:
203+
* - *donut* e *pie*: um objeto contendo a categoria e valor da série.
204+
* - *area*, *line*, *column* e *bar*: um objeto contendo o nome da série, valor e categoria do eixo do gráfico.
205+
*/
206+
@Output('p-series-click')
207+
seriesClick: EventEmitter<any> = new EventEmitter<any>();
208+
209+
/**
210+
* @optional
211+
*
212+
* @description
213+
*
214+
* Evento executado quando o usuário passar o *mouse* sobre um elemento do gráfico.
215+
*
216+
* O evento emitirá o seguinte parâmetro de acordo com o tipo de gráfico:
217+
* - *donut* e *pie*: um objeto contendo a categoria e valor da série.
218+
* - *area*, *line*, *column* e *bar*: um objeto contendo a categoria, valor da série e categoria do eixo do gráfico.
219+
*/
220+
@Output('p-series-hover')
221+
seriesHover: EventEmitter<any> = new EventEmitter<any>();
222+
223+
constructor(languageService: PoLanguageService) {
224+
this.language = languageService.getShortLanguage();
225+
}
226+
}

0 commit comments

Comments
 (0)