Skip to content

Commit

Permalink
feat(chart): inclusão propriedade p-data-label para chart tipo line
Browse files Browse the repository at this point in the history
Implementação da propriedade 'p-data-label', onde é possível passar a propriedade `fixed`.
Com essa propriedade (fixed=tue), irar alterar o grafico do tipo line.
- O valor da series ficar sendo exibido fixado com uma background transparente.
- Não será mostrado o tooltip ao hover do bullet.
- Será adicionado uma opacidade a todas outras series quando selecionado uma delas.
- O hover da serie ficará ate selecionar outra ou sair do gráfico.

fixes DTHFUI-10134
  • Loading branch information
bruno-severino authored and anderson-gregorio-totvs committed Nov 29, 2024
1 parent 82b3529 commit 182d394
Show file tree
Hide file tree
Showing 22 changed files with 629 additions and 28 deletions.
1 change: 1 addition & 0 deletions projects/ui/src/lib/components/po-chart/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export * from './interfaces/po-chart-serie.interface';
export * from './enums/po-chart-type.enum';
export * from './interfaces/po-chart-axis-options.interface';
export * from './interfaces/po-chart-options.interface';
export * from './interfaces/po-chart-serie-data-label.interface';

export * from './po-chart.component';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,10 @@ export interface PoChartPathCoordinates {

/** O texto de exibição no tooltip. */
tooltipLabel?: string;

/** Indica se é elemento não está em foco. */
isBlur?: boolean;

/** Indica se o valor da série está mostrando fixado na tela. */
isFixed?: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,7 @@ export interface PoChartPointsCoordinates {

/** Coordenada vertical. */
yCoordinate: number;

/** Indica se o valor da série está mostrando fixado na tela. */
isFixed?: boolean;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { PoChartType } from '../enums/po-chart-type.enum';

/**
* @usedBy PoChartComponent
*
* @description
*
* Interface que define as propriedades de exibição dos rótulos das séries no `po-chart`.
*
* > Aplicável apenas para gráficos do tipo `line`.
*/
export interface PoChartDataLabel {
/**
* @optional
*
* @description
*
* Indica se o texto associado aos pontos da série deve permanecer fixo na exibição do gráfico.
*
* - Quando definido como `true`:
* - O *tooltip* não será exibido.
* - As outras séries ficarão com opacidade reduzida ao passar o mouse sobre a série ativa.
*
* > Disponível apenas para o tipo de gráfico `PoChartType.Line`.
*/
fixed?: boolean;
}
25 changes: 25 additions & 0 deletions projects/ui/src/lib/components/po-chart/po-chart-base.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { PoChartType } from './enums/po-chart-type.enum';
import { PoChartOptions } from './interfaces/po-chart-options.interface';
import { PoChartSerie } from './interfaces/po-chart-serie.interface';
import { PoColorService } from '../../services/po-color/po-color.service';
import { PoChartDataLabel } from './interfaces/po-chart-serie-data-label.interface';

const poChartDefaultHeight = 400;
const poChartMinHeight = 200;
Expand Down Expand Up @@ -210,6 +211,30 @@ export abstract class PoChartBaseComponent implements OnChanges {
return this._options;
}

/**
* @optional
*
* @description
*
* Permite configurar as propriedades de exibição dos rótulos das séries no gráfico.
*
* Essa configuração possibilita fixar os valores das séries diretamente no gráfico, alterando o comportamento visual:
* - Os valores das séries permanecem visíveis, sem a necessidade de hover.
* - O *tooltip* não será exibido.
* - Os marcadores (*bullets*) terão seu estilo ajustado.
* - As outras séries ficarão com opacidade reduzida ao passar o mouse sobre a série ativa.
*
* > Disponível apenas para gráficos do tipo `line`.
*
* #### Exemplo de utilização:
* ```typescript
* dataLabel: PoChartDataLabel = {
* fixed: true,
* };
* ```
*/
@Input('p-data-label') dataLabel?: PoChartDataLabel;

constructor(protected colorService: PoColorService) {}

get isTypeCircular() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
[attr.viewBox]="viewBox"
[attr.width]="containerSize.svgWidth"
[attr.height]="containerSize.svgHeight"
(mouseenter)="onChartMouseEnter()"
(mouseleave)="onChartMouseLeave()"
>
<!-- axis -->
<svg:g
Expand Down Expand Up @@ -55,6 +57,8 @@
[p-range]="range"
[p-series]="seriesByType['line']"
[p-container-size]="containerSize"
[p-data-label]="dataLabel"
[p-insideChart]="insideChart"
(p-point-hover)="onSerieHover($event)"
(p-point-click)="onSerieClick($event)"
></svg:g>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,83 @@ describe('PoChartContainerComponent', () => {

expect(fnError).not.toThrow();
});

it('onChartMouseEnter: should set `insideChart` to true', () => {
component.insideChart = false;
component.onChartMouseEnter();

expect(component.insideChart).toBeTrue();
});

it('onChartMouseLeave: should set `insideChart` to false', () => {
component.insideChart = true;
component.onChartMouseLeave();

expect(component.insideChart).toBeFalse();
});

it('onChartMouseEnter and onChartMouseLeave: should toggle `insideChart` between true and false', () => {
component.insideChart = false;

component.onChartMouseEnter();
expect(component.insideChart).toBeTrue();

component.onChartMouseLeave();
expect(component.insideChart).toBeFalse();
});

describe('updateRangeAxis', () => {
it('should set `range.maxValue` and `axisOptions.maxRange` to at least the highest value + 5', () => {
component.options = { axis: { minRange: 0, maxRange: 46 } };
component.series = [{ label: 'Category', data: [10, 20, 45], type: PoChartType.Line }];
component.dataLabel = { fixed: true };

component.updateRangeAxis();

expect(component.range.maxValue).toBe(50); // Valor máximo da série + 5
expect(component.axisOptions.maxRange).toBe(50);
});

it('should not change `range.maxValue` or `axisOptions.maxRange` if already greater than the highest value + 5', () => {
component.options = { axis: { minRange: 0, maxRange: 60 } };
component.series = [{ label: 'Category', data: [10, 20, 45], type: PoChartType.Line }];
component.dataLabel = { fixed: true };

component.updateRangeAxis();

expect(component.range.maxValue).toBe(60); // Não deve mudar
expect(component.axisOptions.maxRange).toBe(60);
});

it('should not update `range.maxValue` or `axisOptions.maxRange` if `dataLabel.fixed` is false', () => {
component.options = { axis: { minRange: 0, maxRange: 46 } };
component.series = [{ label: 'Category', data: [10, 20, 45], type: PoChartType.Line }];
component.dataLabel = { fixed: false };

component.updateRangeAxis();

expect(component.range.maxValue).toBe(46); // Não deve mudar
expect(component.axisOptions.maxRange).toBe(46);
});
});

it('ngOnChanges: should call `updateRangeAxis` if `dataLabel` has changed', () => {
const changes = { dataLabel: { currentValue: { fixed: true }, previousValue: {}, firstChange: false } };
const spyUpdateRangeAxis = spyOn(component, 'updateRangeAxis');

component.ngOnChanges(<any>changes);

expect(spyUpdateRangeAxis).toHaveBeenCalled();
});

it('ngOnChanges: should not call `updateRangeAxis` if `dataLabel` has not changed', () => {
const changes = { type: { currentValue: PoChartType.Line, previousValue: PoChartType.Bar, firstChange: false } };
const spyUpdateRangeAxis = spyOn(component, 'updateRangeAxis');

component.ngOnChanges(<any>changes);

expect(spyUpdateRangeAxis).not.toHaveBeenCalled();
});
});

describe('Properties: ', () => {
Expand Down Expand Up @@ -403,5 +480,23 @@ describe('PoChartContainerComponent', () => {
expect(chartBarElementList[0]).toBeTruthy();
expect(chartBarElementList.length).toBe(6);
});

it('should call `onChartMouseEnter` and `onChartMouseLeave` on mouse enter and leave', () => {
const svgElement = nativeElement.querySelector('svg');
const spyOnChartMouseEnter = spyOn(component, 'onChartMouseEnter').and.callThrough();
const spyOnChartMouseLeave = spyOn(component, 'onChartMouseLeave').and.callThrough();

svgElement.dispatchEvent(new Event('mouseenter'));
fixture.detectChanges();

expect(spyOnChartMouseEnter).toHaveBeenCalled();
expect(component.insideChart).toBeTrue();

svgElement.dispatchEvent(new Event('mouseleave'));
fixture.detectChanges();

expect(spyOnChartMouseLeave).toHaveBeenCalled();
expect(component.insideChart).toBeFalse();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { PoChartAxisOptions } from '../interfaces/po-chart-axis-options.interfac
import { PoChartMathsService } from '../services/po-chart-maths.service';
import { PoChartMinMaxValues } from '../interfaces/po-chart-min-max-values.interface';
import { PoChartSerie } from '../interfaces/po-chart-serie.interface';
import { PoChartDataLabel } from '../interfaces/po-chart-serie-data-label.interface';

@Component({
selector: 'po-chart-container',
Expand All @@ -19,6 +20,8 @@ export class PoChartContainerComponent implements OnChanges {

@Input('p-container-size') containerSize: PoChartContainerSize;

@Input('p-data-label') dataLabel?: PoChartDataLabel;

@Output('p-serie-click') serieClick = new EventEmitter<any>();

@Output('p-serie-hover') serieHover = new EventEmitter<any>();
Expand All @@ -32,14 +35,14 @@ export class PoChartContainerComponent implements OnChanges {
seriesByType;
svgSpace;
viewBox: string;
insideChart: boolean;

private _options: PoChartOptions;
private _series: Array<PoChartSerie> = [];

@Input('p-options') set options(value: PoChartOptions) {
if (value instanceof Object && !(value instanceof Array)) {
this._options = value;

this.verifyAxisOptions(this._options);
}
}
Expand Down Expand Up @@ -70,6 +73,24 @@ export class PoChartContainerComponent implements OnChanges {
this.setViewBox();
this.setSvgSpace();
}
if (changes.dataLabel) {
this.updateRangeAxis();
}
}

updateRangeAxis() {
if (this.dataLabel?.fixed === true) {
// minimo valor para o grafico = maior numero + 5 de respingo
const calculatedMaxValue = this.mathsService.calculateMinAndMaxValues(this.series).maxValue + 5;

// Certifique-se de que o `maxValue` nunca seja menor que o maior valor mais 5.
const newMaxValue = Math.max(calculatedMaxValue, this.range.maxValue);

if (this.range.maxValue !== newMaxValue) {
this.range.maxValue = newMaxValue;
this.axisOptions.maxRange = newMaxValue;
}
}
}

getCategoriesCoordinates(value: Array<number>): void {
Expand Down Expand Up @@ -147,4 +168,12 @@ export class PoChartContainerComponent implements OnChanges {
};
}
}

onChartMouseEnter() {
this.insideChart = true;
}

onChartMouseLeave() {
this.insideChart = false;
}
}
Loading

0 comments on commit 182d394

Please sign in to comment.