Skip to content

Commit

Permalink
refactor: handling grouped, with interval and single data point
Browse files Browse the repository at this point in the history
  • Loading branch information
anandtiwary committed Dec 5, 2023
1 parent 8668136 commit f209661
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable max-lines */
import { Injectable, InjectionToken } from '@angular/core';
import { isEqualIgnoreFunctions } from '@hypertrace/common';
import { TimeDuration, isEqualIgnoreFunctions } from '@hypertrace/common';
import {
CoreTableCellRendererType,
FilterBuilderLookupService,
Expand Down Expand Up @@ -67,6 +67,14 @@ export class ExplorerDashboardBuilder {
'selectable-interval': false,
'series-from-data': true,
'legend-position': LegendPosition.Bottom,
'default-interval':
request.interval instanceof TimeDuration
? {
type: 'time-duration',
value: request.interval.value,
unit: request.interval.unit,
}
: undefined,
'selection-handler': {
type: 'cartesian-explorer-selection-handler',
'show-context-menu': false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ export class CartesianChartComponent<TData> implements OnChanges, OnDestroy {
@Input()
public rangeSelectionEnabled: boolean = false;

@Input()
public selectableInterval?: boolean;

@Input()
public intervalOptions?: IntervalValue[];

Expand Down Expand Up @@ -160,10 +163,11 @@ export class CartesianChartComponent<TData> implements OnChanges, OnDestroy {
this.chart.withLegend(this.legend);
}

if (this.intervalOptions !== undefined && this.selectedInterval !== undefined) {
if (this.selectedInterval !== undefined) {
this.chart.withIntervalData({
selectableInterval: this.selectableInterval ?? false,
initial: this.selectedInterval,
options: this.intervalOptions,
options: this.intervalOptions ?? [this.selectedInterval],
changeObserver: this.selectedIntervalChange,
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ export class DefaultCartesianChart<TData> implements CartesianChart<TData> {
this.legend = new CartesianLegend<TData>(
this.activeSeries,
this.injector,
this.scaleBuilder.getDataAccessorForDomain(AxisType.X),
this.intervalData,
this.seriesSummaries,
).draw(this.chartContainerElement, this.legendPosition);
Expand All @@ -456,10 +457,13 @@ export class DefaultCartesianChart<TData> implements CartesianChart<TData> {
});
} else {
// The legend also contains the interval selector, so even without a legend we need to create an element for that
this.legend = new CartesianLegend<TData>([], this.injector, this.intervalData, this.seriesSummaries).draw(
this.chartContainerElement,
LegendPosition.None,
);
this.legend = new CartesianLegend<TData>(

Check warning on line 460 in projects/observability/src/shared/components/cartesian/d3/chart/cartesian-chart.ts

View check run for this annotation

Codecov / codecov/patch

projects/observability/src/shared/components/cartesian/d3/chart/cartesian-chart.ts#L460

Added line #L460 was not covered by tests
[],
this.injector,
this.scaleBuilder.getDataAccessorForDomain(AxisType.X),
this.intervalData,
this.seriesSummaries,
).draw(this.chartContainerElement, LegendPosition.None);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export class CartesianIntervalControlComponent {
}

export interface CartesianIntervalData {
selectableInterval: boolean;
options: IntervalValue[];
initial: IntervalValue;
changeObserver: Observer<IntervalValue>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
} from './cartesian-interval-control.component';
import { CartesianSummaryComponent, SUMMARIES_DATA } from './cartesian-summary.component';
import { FILTER_BUTTON_WRAPPER, FilterButtonWrapperComponent } from '@hypertrace/components';
import { defaultXDataAccessor } from '../scale/default-data-accessors';

export class CartesianLegend<TData> {
private static readonly CSS_CLASS: string = 'legend';
Expand All @@ -34,24 +33,37 @@ export class CartesianLegend<TData> {
private activeSeries: Series<TData>[];
private intervalControl?: ComponentRef<unknown>;
private summaryControl?: ComponentRef<unknown>;
private readonly isGroupedSeries: boolean;
private readonly hasInterval: boolean;

public constructor(
private readonly series: Series<TData>[],
private readonly injector: Injector,
private readonly xDataAccessor: (data: TData) => string,
private readonly intervalData?: CartesianIntervalData,
private readonly summaries: Summary[] = [],
) {
const isGroupedSeries =
this.isGroupedSeries =
this.series.length > 0 &&
this.series.every(seriesEntry => !isEmpty(seriesEntry.groupName) && seriesEntry.groupName !== seriesEntry.name);
this.legendGroups = isGroupedSeries
this.hasInterval = this.intervalData !== undefined;

this.legendGroups = this.isGroupedSeries
? Object.entries(groupBy(this.series, seriesEntry => seriesEntry.groupName)).map(([title, data]) => ({
title: title,
data: data.map(d => ({ text: d.name, series: d })),
}))
: this.hasInterval
? this.series.map(s => ({

Check warning on line 57 in projects/observability/src/shared/components/cartesian/d3/legend/cartesian-legend.ts

View check run for this annotation

Codecov / codecov/patch

projects/observability/src/shared/components/cartesian/d3/legend/cartesian-legend.ts#L57

Added line #L57 was not covered by tests
title: s.name,
data: s.data.map(d => ({

Check warning on line 59 in projects/observability/src/shared/components/cartesian/d3/legend/cartesian-legend.ts

View check run for this annotation

Codecov / codecov/patch

projects/observability/src/shared/components/cartesian/d3/legend/cartesian-legend.ts#L59

Added line #L59 was not covered by tests
text: isEmpty(this.xDataAccessor(d)?.toString()) ? s.name : this.xDataAccessor(d)!.toString(),
series: s,
})),
}))
: this.series.map(s => ({
title: s.name,
data: s.data.map(d => ({ text: defaultXDataAccessor(d), series: s })),
data: [{ text: s.name, series: s }],
}));

this.activeSeries = [...this.series];
Expand All @@ -69,7 +81,7 @@ export class CartesianLegend<TData> {
this.drawLegendEntries(this.legendElement);
this.drawReset(this.legendElement);

if (this.intervalData) {
if (this.intervalData?.selectableInterval) {
this.intervalControl = this.drawIntervalControl(this.legendElement, this.intervalData);
}

Expand Down Expand Up @@ -165,16 +177,18 @@ export class CartesianLegend<TData> {
.text(entry => entry.text)
.on('click', entry => (this.series.length > 1 ? this.updateActiveSeries(entry.series) : undefined));

this.drawFilterControl(legendEntry.node()!).location.nativeElement.classList.add('filter');
if (this.isGroupedSeries || this.hasInterval) {
this.drawFilterControl(legendEntry.node()!).location.nativeElement.classList.add('filter');

this.updateLegendClassesAndStyle();

if (showFilters) {
legendEntry
.on('mouseover', () => legendEntry.select('.filter').style('visibility', 'visible'))
.on('mouseout', () => legendEntry.select('.filter').style('visibility', 'hidden'));
if (showFilters) {
legendEntry
.on('mouseover', () => legendEntry.select('.filter').style('visibility', 'visible'))
.on('mouseout', () => legendEntry.select('.filter').style('visibility', 'hidden'));

Check warning on line 186 in projects/observability/src/shared/components/cartesian/d3/legend/cartesian-legend.ts

View check run for this annotation

Codecov / codecov/patch

projects/observability/src/shared/components/cartesian/d3/legend/cartesian-legend.ts#L185-L186

Added lines #L185 - L186 were not covered by tests
}
}

this.updateLegendClassesAndStyle();

return legendEntry;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,16 @@ export class CartesianScaleBuilder<TData> {
return {
...this.resolveMinMax(axisType),
bounds: this.bounds,
dataAccessor: this.getDataAccessor(axisType) as <TDomain>(data: TData) => TDomain,
dataAccessor: this.getDataAccessorForDomain(axisType),
allSeriesAndBandSeries: this.allSeriesAndBandSeries,
seriesState: seriesState,
};
}

public getDataAccessorForDomain(axisType: AxisType): <TDomain>(data: TData) => TDomain {
return this.getDataAccessor(axisType) as <TDomain>(data: TData) => TDomain;
}

private getDataAccessor(axisType: AxisType): (data: TData) => unknown {
if (axisType === AxisType.X) {
return this.getXDataAccessor();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { CartesianDataFetcher, CartesianResult, CartesianWidgetModel } from './c
[showYAxis]="this.model.showYAxis"
[timeRange]="this.timeRange"
[rangeSelectionEnabled]="!!this.model.selectionHandler"
[selectableInterval]="this.model.selectableInterval"
[selectedInterval]="this.selectedInterval"
[intervalOptions]="this.intervalOptions"
[legend]="this.model.legendPosition"
Expand All @@ -43,18 +44,19 @@ export class CartesianWidgetRendererComponent<TSeriesInterval, TData> extends In
CartesianWidgetModel<TSeriesInterval>,
CartesianData<TSeriesInterval>
> {
public selectedInterval?: IntervalValue;
public intervalOptions?: IntervalValue[];
private fetcher?: CartesianDataFetcher<TSeriesInterval>;

public constructor(
@Inject(RENDERER_API) api: RendererApi<CartesianWidgetModel<TSeriesInterval>>,
changeDetector: ChangeDetectorRef,
private readonly intervalDurationService: IntervalDurationService,
) {
super(api, changeDetector);
this.selectedInterval = this.model.defaultInterval?.getDuration();
}

public selectedInterval?: IntervalValue;
public intervalOptions?: IntervalValue[];
private fetcher?: CartesianDataFetcher<TSeriesInterval>;

public onIntervalChange(interval: IntervalValue): void {
this.selectedInterval = interval;
this.updateDataObservable();
Expand All @@ -79,7 +81,8 @@ export class CartesianWidgetRendererComponent<TSeriesInterval, TData> extends In
this.selectedInterval = this.getBestIntervalMatch(intervalOptions, this.selectedInterval ?? defaultInterval);
this.intervalOptions = intervalOptions; // The only thing this flag controls is whether options are available (and thus, the selector)
} else {
this.selectedInterval = this.getBestIntervalMatch(intervalOptions, defaultInterval);
// TODO: why do we need to set this if interval is not supported.
// this.selectedInterval = this.getBestIntervalMatch(intervalOptions, defaultInterval);
}
}),
switchMap(() => this.buildDataObservable()),
Expand Down

0 comments on commit f209661

Please sign in to comment.