mirror of
https://github.com/ceph/ceph
synced 2026-08-02 07:03:18 +00:00
Merge pull request #68495 from rhcs-dashboard/76169-inconsistant-tooltip-timestamp-format-performance-chart
Reviewed-by: Afreen Misbah <afreen@ibm.com>
This commit is contained in:
commit
7b29dcc7e6
@ -14,6 +14,7 @@ const jestConfig = {
|
||||
'~/(.*)$': '<rootDir>/src/$1',
|
||||
'^@carbon/icons/es/(.*)$': '@carbon/icons/lib/$1.js',
|
||||
'^lodash-es$': 'lodash',
|
||||
'^@carbon/charts$': '<rootDir>/node_modules/@carbon/charts/dist/index.mjs'
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'html', 'js', 'json', 'mjs', 'cjs'],
|
||||
preset: 'jest-preset-angular',
|
||||
|
||||
@ -10,7 +10,6 @@ describe('AreaChartComponent', () => {
|
||||
let component: AreaChartComponent;
|
||||
let fixture: ComponentFixture<AreaChartComponent>;
|
||||
let numberFormatterService: NumberFormatterService;
|
||||
let datePipe: DatePipe;
|
||||
|
||||
const mockData: ChartPoint[] = [
|
||||
{
|
||||
@ -41,23 +40,15 @@ describe('AreaChartComponent', () => {
|
||||
unitlessLabels: ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']
|
||||
};
|
||||
|
||||
const datePipeMock = {
|
||||
transform: jest.fn().mockReturnValue('01 Jan, 00:00:00')
|
||||
};
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ChartsModule, AreaChartComponent],
|
||||
providers: [
|
||||
{ provide: NumberFormatterService, useValue: numberFormatterMock },
|
||||
{ provide: DatePipe, useValue: datePipeMock }
|
||||
],
|
||||
providers: [DatePipe, { provide: NumberFormatterService, useValue: numberFormatterMock }],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(AreaChartComponent);
|
||||
component = fixture.componentInstance;
|
||||
numberFormatterService = TestBed.inject(NumberFormatterService);
|
||||
datePipe = TestBed.inject(DatePipe);
|
||||
});
|
||||
|
||||
it('should mount', () => {
|
||||
@ -133,23 +124,36 @@ describe('AreaChartComponent', () => {
|
||||
expect(component.chartOptions?.tooltip?.enabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should format tooltip with custom date format', () => {
|
||||
const testDate = new Date('2024-01-01T12:30:45Z');
|
||||
const formattedDate = '01 Jan, 12:30:45';
|
||||
const defaultHTML = '<div><p class="value">2024-01-01T12:30:45Z</p></div>';
|
||||
it('should wire tooltip customHTML with arity 2', () => {
|
||||
component.chartTitle = 'Test Chart';
|
||||
component.dataUnit = 'B/s';
|
||||
component.rawData = mockData;
|
||||
|
||||
(datePipe.transform as jest.Mock).mockReturnValue(formattedDate);
|
||||
(numberFormatterService.formatFromTo as jest.Mock).mockReturnValue('4.00 KiB/s');
|
||||
|
||||
const result = component.formatChartTooltip(defaultHTML, [{ date: testDate }]);
|
||||
fixture.detectChanges();
|
||||
component.ngOnChanges({
|
||||
rawData: new SimpleChange(null, mockData, false)
|
||||
});
|
||||
|
||||
expect(datePipe.transform).toHaveBeenCalledWith(testDate, 'dd MMM, HH:mm:ss');
|
||||
expect(result).toContain(formattedDate);
|
||||
expect(result).not.toContain('2024-01-01T12:30:45Z');
|
||||
expect(component.chartOptions?.tooltip?.customHTML?.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should return default HTML if tooltip data is empty', () => {
|
||||
it('should replace x-value label with Time in tooltip HTML', () => {
|
||||
const defaultHTML =
|
||||
'<ul class="multi-tooltip"><li><div class="datapoint-tooltip">' +
|
||||
'<div class="label"><p>x-value</p></div><p class="value">ignored</p></div></li></ul>';
|
||||
|
||||
const result = component.formatChartTooltip(defaultHTML);
|
||||
|
||||
expect(result).toContain('<p>Time</p>');
|
||||
expect(result).not.toContain('x-value');
|
||||
expect(result).toContain('<p class="value">ignored</p>');
|
||||
});
|
||||
|
||||
it('should return default HTML if tooltip has no x-value label', () => {
|
||||
const defaultHTML = '<div><p>Default</p></div>';
|
||||
const result = component.formatChartTooltip(defaultHTML, []);
|
||||
const result = component.formatChartTooltip(defaultHTML);
|
||||
expect(result).toBe(defaultHTML);
|
||||
});
|
||||
|
||||
|
||||
@ -13,11 +13,11 @@ import {
|
||||
ChartTabularData,
|
||||
ToolbarControlTypes,
|
||||
ScaleTypes,
|
||||
ChartsModule
|
||||
ChartsModule,
|
||||
TickRotations
|
||||
} from '@carbon/charts-angular';
|
||||
import merge from 'lodash.merge';
|
||||
import { NumberFormatterService } from '../../services/number-formatter.service';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { ChartPoint } from '../../models/area-chart-point';
|
||||
import {
|
||||
DECIMAL,
|
||||
@ -26,6 +26,12 @@ import {
|
||||
getDivisor,
|
||||
getLabels
|
||||
} from '../../helpers/unit-format-utils';
|
||||
import { DatePipe } from '@angular/common';
|
||||
|
||||
const DEFAULT_TICKS_COUNT = 4;
|
||||
const FIVE_MINUTE_SPAN_SECONDS = 300;
|
||||
const TIME_SPAN_TOLERANCE_SECONDS = 30;
|
||||
const TOOLTIP_TIME_FORMAT = 'MMM d, hh:mm a';
|
||||
|
||||
@Component({
|
||||
selector: 'cd-area-chart',
|
||||
@ -59,9 +65,10 @@ export class AreaChartComponent implements OnChanges {
|
||||
|
||||
private cdr = inject(ChangeDetectorRef);
|
||||
private numberFormatter: NumberFormatterService = inject(NumberFormatterService);
|
||||
private datePipe = inject(DatePipe);
|
||||
private lastEmittedRawValues?: Record<string, number>;
|
||||
|
||||
private datePipe = inject(DatePipe);
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes['rawData'] && this.rawData?.length) {
|
||||
this.updateChart();
|
||||
@ -129,7 +136,22 @@ export class AreaChartComponent implements OnChanges {
|
||||
this.lastEmittedRawValues = { ...latestEntry.values };
|
||||
}
|
||||
|
||||
// Calculate time span in seconds from raw data timestamps
|
||||
private getTimeSpanInSeconds(): number {
|
||||
if (!this.rawData || this.rawData.length < 2) {
|
||||
return 0;
|
||||
}
|
||||
const firstTimestamp = new Date(this.rawData[0].timestamp).getTime();
|
||||
const lastTimestamp = new Date(this.rawData[this.rawData.length - 1].timestamp).getTime();
|
||||
return (lastTimestamp - firstTimestamp) / 1000;
|
||||
}
|
||||
|
||||
private getChartOptions(max: number, labels: string[], divisor: number): AreaChartOptions {
|
||||
const timeSpan = this.getTimeSpanInSeconds();
|
||||
const isFiveMinuteSpan =
|
||||
timeSpan > 0 &&
|
||||
timeSpan >= FIVE_MINUTE_SPAN_SECONDS - TIME_SPAN_TOLERANCE_SECONDS &&
|
||||
timeSpan <= FIVE_MINUTE_SPAN_SECONDS + TIME_SPAN_TOLERANCE_SECONDS;
|
||||
return {
|
||||
legend: {
|
||||
enabled: this.legendEnabled
|
||||
@ -139,8 +161,8 @@ export class AreaChartComponent implements OnChanges {
|
||||
mapsTo: 'date',
|
||||
scaleType: ScaleTypes.TIME,
|
||||
ticks: {
|
||||
number: 4,
|
||||
rotateIfSmallerThan: 0
|
||||
number: DEFAULT_TICKS_COUNT,
|
||||
rotation: isFiveMinuteSpan ? TickRotations.ALWAYS : TickRotations.AUTO
|
||||
}
|
||||
},
|
||||
left: {
|
||||
@ -161,9 +183,18 @@ export class AreaChartComponent implements OnChanges {
|
||||
tooltip: {
|
||||
enabled: true,
|
||||
showTotal: false,
|
||||
valueFormatter: (value: number): string =>
|
||||
(this.formatValueForChart(value, labels, divisor, this.decimals) || value).toString(),
|
||||
customHTML: (data, defaultHTML) => this.formatChartTooltip(defaultHTML, data)
|
||||
truncation: {
|
||||
type: 'none'
|
||||
},
|
||||
valueFormatter: (value: number | Date, label: string): string => {
|
||||
if (value instanceof Date && label === 'x-value') {
|
||||
return this.datePipe.transform(value, TOOLTIP_TIME_FORMAT) ?? '';
|
||||
}
|
||||
return (
|
||||
this.formatValueForChart(value, labels, divisor, this.decimals) || value
|
||||
).toString();
|
||||
},
|
||||
customHTML: (_data, defaultHTML) => this.formatChartTooltip(defaultHTML)
|
||||
},
|
||||
points: {
|
||||
enabled: false
|
||||
@ -193,15 +224,8 @@ export class AreaChartComponent implements OnChanges {
|
||||
};
|
||||
}
|
||||
|
||||
// Custom tooltip formatter to replace default timestamp with a formatted one.
|
||||
formatChartTooltip(defaultHTML: string, data: { date: Date }[]): string {
|
||||
if (!data?.length) return defaultHTML;
|
||||
|
||||
const formattedTime = this.datePipe.transform(data[0].date, 'dd MMM, HH:mm:ss');
|
||||
return defaultHTML.replace(
|
||||
/<p class="value">.*?<\/p>/,
|
||||
`<p class="value">${formattedTime}</p>`
|
||||
);
|
||||
formatChartTooltip(defaultHTML: string): string {
|
||||
return defaultHTML.replace(/<p>x-value<\/p>/i, `<p>${$localize`Time`}</p>`);
|
||||
}
|
||||
|
||||
// Uses number formatter service to convert chart value based on unit and divisor.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user