Skip to content

Commit a6ffb69

Browse files
samir-ayoubjhosefmarks
authored andcommitted
feat(chart): adiciona gráfico do tipo gauge
O gráfico do tipo gauge permite a visualização de uma única série com um alcance entre 0 e 100%. Fixes DTHFUI-1738
1 parent 8db50ef commit a6ffb69

31 files changed

+1542
-245
lines changed

projects/ui/src/lib/components/po-chart/enums/po-chart-type.enum.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ export enum PoChartType {
1212
*/
1313
Donut = 'donut',
1414

15+
/**
16+
* O gráfico de *gauge* fornece como opção uma melhor relação de intensidade de dados que nos gráficos de pizza padrão ou rosca, uma vez
17+
* que o centro em branco pode ser usado para exibir dados adicionais relacionados.
18+
*/
19+
Gauge = 'gauge',
20+
1521
/**
1622
* Exibe os dados em formato circular, dividindo proporcionalmente em fatias.
1723
*/

projects/ui/src/lib/components/po-chart/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from './enums/po-chart-type.enum';
22
export * from './po-chart-types/po-chart-donut/po-chart-donut-series.interface';
3+
export * from './po-chart-types/po-chart-gauge/po-chart-gauge-series.interface';
34
export * from './po-chart-types/po-chart-pie/po-chart-pie-series.interface';
45
export * from './po-chart.component';
56

projects/ui/src/lib/components/po-chart/po-chart-base.component.spec.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ describe('PoChartBaseComponent:', () => {
4545
expect(component.rebuildComponent).toHaveBeenCalled();
4646
});
4747

48+
it('p-height: should call `setDefaultHeight` if height isn`t defined`', () => {
49+
spyOn(component, <any>'setDefaultHeight').and.callThrough();
50+
51+
const expectedHeight = component.height;
52+
53+
expect(component.height).toBe(expectedHeight);
54+
expect(component['setDefaultHeight']).toHaveBeenCalled();
55+
});
56+
4857
it('p-height: should update property with 400 if invalid values.', () => {
4958
const invalidValues = [null, undefined, '', 'string', {}, [], false, true];
5059

@@ -67,6 +76,26 @@ describe('PoChartBaseComponent:', () => {
6776
expectPropertiesValues(component, 'type', invalidValues, PoChartType.Pie);
6877
});
6978

79+
it('p-series: should update property with valid values', () => {
80+
const validValues = [[{ value: 1, category: 'value' }], [], { value: 1, description: 'value' }];
81+
82+
expectPropertiesValues(component, 'series', validValues, validValues);
83+
});
84+
85+
it('p-series: should update property if invalid values', () => {
86+
const invalidValues = [undefined, null, '', false, 0];
87+
88+
expectPropertiesValues(component, 'series', invalidValues, []);
89+
});
90+
91+
it('p-series: should call `transformObjectToArrayObject` if series is an object', () => {
92+
spyOn(component, <any>'transformObjectToArrayObject');
93+
94+
component.series = { value: 1, description: 'value' };
95+
96+
expect(component['transformObjectToArrayObject']).toHaveBeenCalledWith(component.series);
97+
});
98+
7099
});
71100

72101
describe('Methods:', () => {
@@ -93,6 +122,36 @@ describe('PoChartBaseComponent:', () => {
93122
expect(component.seriesHover.emit).toHaveBeenCalledWith(eventMock);
94123
});
95124

125+
it('setDefaultHeight: should return `200` if type is `Gauge`', () => {
126+
component.type = PoChartType.Gauge;
127+
128+
const expectedResult = component['setDefaultHeight']();
129+
130+
expect(expectedResult).toBe(200);
131+
});
132+
133+
it('setDefaultHeight: should return `400` if type is different from `Gauge`', () => {
134+
component.type = PoChartType.Pie;
135+
136+
const expectedResult = component['setDefaultHeight']();
137+
138+
expect(expectedResult).toBe(400);
139+
});
140+
141+
it('transformObjectToArrayObject: should return an array containing the serie`s object', () => {
142+
const serie = {value: 1, description: 'description'};
143+
const expectedResult = component['transformObjectToArrayObject'](serie);
144+
145+
expect(expectedResult).toEqual([serie]);
146+
});
147+
148+
it('transformObjectToArrayObject: should return an empty array if the serie is an object without length', () => {
149+
const serie = <any>{};
150+
const expectedResult = component['transformObjectToArrayObject'](serie);
151+
152+
expect(expectedResult).toEqual([]);
153+
});
154+
96155
});
97156

98157
});

projects/ui/src/lib/components/po-chart/po-chart-base.component.ts

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { EventEmitter, Input, Output } from '@angular/core';
22

33
import { convertToInt, isTypeof } from '../../utils/util';
44

5+
import { PoChartGaugeSerie } from './po-chart-types/po-chart-gauge/po-chart-gauge-series.interface';
56
import { PoChartType } from './enums/po-chart-type.enum';
67
import { PoDonutChartSeries } from './po-chart-types/po-chart-donut/po-chart-donut-series.interface';
78
import { PoPieChartSeries } from './po-chart-types/po-chart-pie/po-chart-pie-series.interface';
@@ -10,31 +11,33 @@ const poChartDefaultHeight = 400;
1011
const poChartMinHeight = 200;
1112
const poChartTypeDefault = PoChartType.Pie;
1213

14+
export type PoChartSeries = Array<PoDonutChartSeries | PoPieChartSeries | PoChartGaugeSerie>;
15+
1316
/**
1417
* @description
1518
*
1619
* O `po-chart` é um componente para renderização de dados através de gráficos, com isso facilitando a compreensão e tornando a
1720
* visualização destes dados mais agradável.
1821
*
19-
* Este componente também possibilita a definição das seguintes propriedades:
20-
* - altura
21-
* - series
22-
* - tipo
23-
* - título
22+
* Através de suas principais propriedades é possível definir o tipo de gráfico, uma altura e um título.
2423
*
25-
* Além das definições de propriedades, também é possível definir uma ação que será executada ao clicar em determinado elemento do gráfico
24+
* Além disso, também é possível definir uma ação que será executada ao clicar em determinado elemento do gráfico
2625
* e outra que será executada ao passar o *mouse* sobre o elemento.
2726
*
2827
* #### Boas práticas
2928
*
3029
* - Para que o gráfico não fique ilegível e incompreensível, evite uma quantia excessiva de séries.
31-
*
30+
* - Para exibir a intensidade de um único dado dê preferência ao tipo `gauge`.
3231
*/
3332
export abstract class PoChartBaseComponent {
3433

35-
private _height?: number = poChartDefaultHeight;
34+
private _height: number;
35+
private _series: Array<PoDonutChartSeries | PoPieChartSeries> | PoChartGaugeSerie;
3636
private _type: PoChartType = poChartTypeDefault;
3737

38+
// manipulação das séries tratadas internamente para preservar 'p-series';
39+
protected chartSeries: PoChartSeries;
40+
3841
public readonly poChartType = PoChartType;
3942

4043
/**
@@ -44,7 +47,11 @@ export abstract class PoChartBaseComponent {
4447
*
4548
* Define a altura do gráfico.
4649
*
47-
* > O valor mínimo que pode ser informado é 200.
50+
* O valor padrão dos gráficos são:
51+
* - para o tipo *gauge*: `200px`;
52+
* - para os demais tipos: `400px`.
53+
*
54+
* > O valor mínimo aceito nesta propriedade é 200.
4855
*
4956
* @default `400px`
5057
*/
@@ -55,7 +62,7 @@ export abstract class PoChartBaseComponent {
5562
if (isTypeof(value, 'number')) {
5663
height = intValue <= poChartMinHeight ? poChartMinHeight : intValue;
5764
} else {
58-
height = poChartDefaultHeight;
65+
height = this.setDefaultHeight();
5966
}
6067

6168
this._height = height;
@@ -64,7 +71,7 @@ export abstract class PoChartBaseComponent {
6471
}
6572

6673
get height(): number {
67-
return this._height;
74+
return this._height || this.setDefaultHeight();
6875
}
6976

7077
/**
@@ -75,8 +82,17 @@ export abstract class PoChartBaseComponent {
7582
* > A coleção de objetos deve implementar alguma das interfaces abaixo:
7683
* - `PoDonutChartSeries`
7784
* - `PoPieChartSeries`
85+
* - `PoChartGaugeSerie`
7886
*/
79-
@Input('p-series') series: Array<PoDonutChartSeries | PoPieChartSeries>;
87+
@Input('p-series') set series(value: PoChartGaugeSerie | Array<PoDonutChartSeries | PoPieChartSeries>) {
88+
this._series = value || [];
89+
90+
this.chartSeries = Array.isArray(this._series) ? [...this._series] : this.transformObjectToArrayObject(this._series);
91+
}
92+
93+
get series() {
94+
return this._series;
95+
}
8096

8197
/** Define o título do gráfico. */
8298
@Input('p-title') title?: string;
@@ -103,20 +119,28 @@ export abstract class PoChartBaseComponent {
103119
}
104120

105121
/**
122+
* @optional
123+
*
124+
* @description
125+
*
106126
* Evento executado quando o usuário clicar sobre um elemento do gráfico.
107127
*
108128
* > Será passado por parâmetro um objeto contendo a categoria e valor da série.
109129
*/
110130
@Output('p-series-click')
111-
seriesClick?: EventEmitter<any> = new EventEmitter<any>();
131+
seriesClick = new EventEmitter<PoDonutChartSeries | PoPieChartSeries | PoChartGaugeSerie>();
112132

113133
/**
134+
* @optional
135+
*
136+
* @description
137+
*
114138
* Evento executado quando o usuário passar o *mouse* sobre um elemento do gráfico.
115139
*
116140
* > Será passado por parâmetro um objeto contendo a categoria e valor da série.
117141
*/
118142
@Output('p-series-hover')
119-
seriesHover?: EventEmitter<any> = new EventEmitter<any>();
143+
seriesHover = new EventEmitter<PoDonutChartSeries | PoPieChartSeries | PoChartGaugeSerie>();
120144

121145
onSeriesClick(event: any): void {
122146
this.seriesClick.emit(event);
@@ -126,6 +150,14 @@ export abstract class PoChartBaseComponent {
126150
this.seriesHover.emit(event);
127151
}
128152

153+
private setDefaultHeight() {
154+
return this.type === PoChartType.Gauge ? poChartMinHeight : poChartDefaultHeight;
155+
}
156+
157+
private transformObjectToArrayObject(serie: PoChartGaugeSerie) {
158+
return typeof serie === 'object' && Object.keys(serie).length ? [{...serie}] : [];
159+
}
160+
129161
abstract rebuildComponent(): void;
130162

131163
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<div class="po-chart-legend">
2+
<div class="po-chart-legend-container">
3+
<div class="po-chart-legend-item" *ngFor="let serie of series; let i = index">
4+
<div class="po-chart-legend-square" [style.background]="colors[i]"></div>
5+
<span class="po-chart-legend-text">{{ serie.category }}</span>
6+
</div>
7+
</div>
8+
</div>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { PoChartLegendComponent } from './po-chart-legend.component';
4+
5+
describe('PoChartLegendComponent:', () => {
6+
7+
let component: PoChartLegendComponent;
8+
let fixture: ComponentFixture<PoChartLegendComponent>;
9+
10+
beforeEach(async(() => {
11+
TestBed.configureTestingModule({
12+
declarations: [PoChartLegendComponent]
13+
})
14+
.compileComponents();
15+
}));
16+
17+
beforeEach(() => {
18+
fixture = TestBed.createComponent(PoChartLegendComponent);
19+
component = fixture.componentInstance;
20+
});
21+
22+
it('should be created', () => {
23+
expect(component instanceof PoChartLegendComponent).toBeTruthy();
24+
});
25+
26+
describe('Templates:', () => {
27+
28+
it('should apply valid text and color values', () => {
29+
component.series = <any>[{value: 10, category: '1'}, {value: 20, category: '2'}];
30+
component.colors = ['red', 'green'];
31+
32+
fixture.detectChanges();
33+
34+
const legendSquare = fixture.debugElement.nativeElement.querySelectorAll('.po-chart-legend-square');
35+
const legendText = fixture.debugElement.nativeElement.querySelectorAll('.po-chart-legend-text');
36+
37+
expect(legendSquare[0].getAttribute('style')).toBe('background: red;');
38+
expect(legendText[0].textContent.trim()).toBe(component.series[0].category);
39+
expect(legendSquare[1].getAttribute('style')).toBe('background: green;');
40+
expect(legendText[1].textContent.trim()).toBe(component.series[1].category);
41+
});
42+
43+
});
44+
45+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
2+
3+
import { PoCircularChartSeries } from '../po-chart-types/po-chart-circular/po-chart-circular-series.interface';
4+
5+
@Component({
6+
selector: 'po-chart-legend',
7+
templateUrl: './po-chart-legend.component.html',
8+
changeDetection: ChangeDetectionStrategy.OnPush
9+
})
10+
export class PoChartLegendComponent {
11+
12+
@Input('p-colors') colors: Array<string>;
13+
14+
@Input('p-series') series: PoCircularChartSeries;
15+
16+
}

projects/ui/src/lib/components/po-chart/po-chart-types/po-chart-circular/po-chart-circular.constant.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,12 @@ const poChartAnimationDurationTime = 1500;
22

33
export const poChartAngleStepInterval = Math.PI * 2 / Math.floor( poChartAnimationDurationTime / 60 );
44
export const poChartCompleteCircle = 0.0001;
5+
export const poChartGaugeStartAngle = -Math.PI;
56
export const poChartPadding: number = 24;
67
export const poChartStartAngle = -Math.PI / 2;
8+
9+
// tamanho da largura da serie proporcional ao grafico do tipo Donut, o valor 0.27 fica proximo de 32px
10+
export const poChartDonutSerieWidth = 0.27;
11+
12+
// tamanho da largura da serie proporcional ao grafico do tipo Gauge, o valor 0.04 fica proximo de 8px
13+
export const poChartGaugeSerieWidth = 0.06;

0 commit comments

Comments
 (0)