Skip to content

feat: adding gauge widget #347

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions projects/components/src/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,6 @@ export * from './filtering/filter-modal/in-filter-modal.component';
// Filter Parser
export * from './filtering/filter/parser/filter-parser-lookup.service';

// Gauge
export * from './gauge/gauge.component';
export * from './gauge/gauge.module';

// Header
export * from './header/application/application-header.component';
export * from './header/application/application-header.module';
Expand Down
5 changes: 5 additions & 0 deletions projects/observability/src/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,3 +210,8 @@ export * from './shared/dashboard/data/graphql/specifiers/entity-specification.m

// Explorer service
export * from './pages/explorer/explorer-service';

// Gauge
export * from './shared/components/gauge/gauge.component';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doubled up gauge component/module

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

export * from './shared/components/gauge/gauge.module';
export * from './shared/dashboard/widgets/gauge/gauge-widget';
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormattingModule } from '@hypertrace/common';
import { LayoutChangeModule } from '../layout/layout-change.module';
import { TooltipModule } from './../tooltip/tooltip.module';
import { LayoutChangeModule, TooltipModule } from '@hypertrace/components';
import { GaugeComponent } from './gauge.component';

@NgModule({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { Color, FormattingModule } from '@hypertrace/common';
import { LoadAsyncModule, TitledContentComponent } from '@hypertrace/components';
import { RENDERER_API } from '@hypertrace/hyperdash-angular';
import { createComponentFactory } from '@ngneat/spectator/jest';
import { MockComponent } from 'ng-mocks';
import { EMPTY, of } from 'rxjs';
import { GaugeComponent } from './../../../components/gauge/gauge.component';
import { GaugeWidgetRendererComponent } from './gauge-widget-renderer.component';
import { GaugeWidgetModel } from './gauge-widget.model';

describe('Gauge widget renderer component', () => {
let mockModel: Partial<GaugeWidgetModel> = {};
const componentFactory = createComponentFactory({
component: GaugeWidgetRendererComponent,
shallow: true,
imports: [FormattingModule, LoadAsyncModule],
providers: [
{
provide: RENDERER_API,
useFactory: () => ({
getTimeRange: jest.fn(),
model: mockModel,
change$: EMPTY,
dataRefresh$: EMPTY,
timeRangeChanged$: EMPTY
})
}
],
declarations: [MockComponent(GaugeComponent), MockComponent(TitledContentComponent)]
});

test('should render provided data with title', () => {
mockModel = {
title: 'Test title',
getData: jest.fn(() =>
of({
value: 5,
maxValue: 10,
thresholds: [
{
start: 0,
end: 6,
label: 'Medium',
color: Color.Brown1
}
]
})
)
};

const spectator = componentFactory();
expect(spectator.query(TitledContentComponent)!.title).toBe('TEST TITLE');

const gaugeComponent = spectator.query(GaugeComponent);
expect(gaugeComponent).toExist();
expect(gaugeComponent!.value).toEqual(5);
expect(gaugeComponent!.maxValue).toEqual(10);
expect(gaugeComponent!.thresholds).toEqual([
{
start: 0,
end: 6,
label: 'Medium',
color: Color.Brown1
}
]);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { WidgetRenderer } from '@hypertrace/dashboards';
import { Renderer } from '@hypertrace/hyperdash';
import { Observable } from 'rxjs';
import { GaugeWidgetData } from './gauge-widget';
import { GaugeWidgetModel } from './gauge-widget.model';

@Renderer({ modelClass: GaugeWidgetModel })
@Component({
selector: 'ht-gauge-widget-renderer',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<ht-titled-content [title]="this.model.title | htDisplayTitle" *htLoadAsync="this.data$ as gaugeData">
<ht-gauge
class="fill-container"
[value]="gaugeData.value"
[maxValue]="gaugeData.maxValue"
[thresholds]="gaugeData.thresholds"
></ht-gauge>
</ht-titled-content>
`
})
export class GaugeWidgetRendererComponent extends WidgetRenderer<GaugeWidgetModel, GaugeWidgetData> {
protected fetchData(): Observable<GaugeWidgetData> {
return this.model.getData();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Color } from '@hypertrace/common';
import { createModelFactory } from '@hypertrace/dashboards/testing';
import { MODEL_PROPERTY_TYPES } from '@hypertrace/hyperdash-angular';
import { runFakeRxjs } from '@hypertrace/test-utils';
import { of } from 'rxjs';
import { GaugeWidgetData } from './gauge-widget';
import { GaugeWidgetModel } from './gauge-widget.model';

describe('Gauge widget model', () => {
test('uses colors from color map', () => {
const modelFactory = createModelFactory();

const data: GaugeWidgetData = {
value: 5,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm - I think I'd expect the max and thresholds to come from the widget model rather than the data model - those seem like they're independent of where we actually get the value from, and would allow dropping in our existing aggregation data sources.

maxValue: 10,
thresholds: [
{
start: 0,
end: 6,
label: 'Medium',
color: Color.Brown1
}
]
};

const spectator = modelFactory(GaugeWidgetModel, {
api: {
getData: () => of(data)
},
providers: [
{
provide: MODEL_PROPERTY_TYPES,
useValue: []
}
],
properties: {
title: 'Test Title'
}
});

runFakeRxjs(({ expectObservable }) => {
expectObservable(spectator.model.getData()).toBe('(x|)', {
x: {
value: 5,
maxValue: 10,
thresholds: [
{
start: 0,
end: 6,
label: 'Medium',
color: Color.Brown1
}
]
}
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Model, ModelApi, ModelProperty, STRING_PROPERTY } from '@hypertrace/hyperdash';
import { ModelInject, MODEL_API } from '@hypertrace/hyperdash-angular';
import { Observable } from 'rxjs';
import { GaugeWidgetData } from './gauge-widget';

@Model({
type: 'gauge-widget'
})
export class GaugeWidgetModel {
@ModelProperty({
key: 'title',
type: STRING_PROPERTY.type
})
public title?: string;

@ModelInject(MODEL_API)
private readonly api!: ModelApi;

public getData(): Observable<GaugeWidgetData> {
return this.api.getData<GaugeWidgetData>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormattingModule } from '@hypertrace/common';
import { LoadAsyncModule, TitledContentModule } from '@hypertrace/components';
import { DashboardCoreModule } from '@hypertrace/hyperdash-angular';
import { GaugeModule } from '../../../components/gauge/gauge.module';
import { GaugeWidgetRendererComponent } from './gauge-widget-renderer.component';
import { GaugeWidgetModel } from './gauge-widget.model';

@NgModule({
declarations: [GaugeWidgetRendererComponent],
imports: [
DashboardCoreModule.with({
models: [GaugeWidgetModel],
renderers: [GaugeWidgetRendererComponent]
}),
GaugeModule,
CommonModule,
TitledContentModule,
LoadAsyncModule,
FormattingModule
]
})
export class GaugeWidgetModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { GaugeThreshold } from '../../../components/gauge/gauge.component';

export interface GaugeWidgetData {
value: number;
maxValue: number;
thresholds: GaugeThreshold[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ObservabilityTableCellRendererModule } from '../../components/table/obs
import { CardListWidgetModule } from './card-list/card-list-widget.module';
import { CartesianWidgetModule } from './charts/cartesian-widget/cartesian-widget.module';
import { DonutWidgetModule } from './donut/donut-widget.module';
import { GaugeWidgetModule } from './gauge/gauge-widget.module';
import { MetricDisplayWidgetModule } from './metric-display/metric-display-widget.module';
import { RadarWidgetModule } from './radar/radar-widget.module';
import { TopNWidgetModule } from './top-n/top-n-widget.module';
Expand All @@ -17,7 +18,8 @@ import { TopologyWidgetModule } from './topology/topology-widget.module';
ObservabilityTableCellRendererModule,
DonutWidgetModule,
CartesianWidgetModule,
CardListWidgetModule
CardListWidgetModule,
GaugeWidgetModule
]
})
export class ObservabilityDashboardWidgetsModule {}