Skip to content
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

perf(charts): revise the d3 charts library #765

Merged
merged 1 commit into from
Aug 19, 2023
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
2 changes: 1 addition & 1 deletion libs/client-d3-charts/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rfprodz/client-d3-charts",
"version": "1.3.16",
"version": "1.3.17",
"description": "Angular chart components based on D3JS (https://d3js.org).",
"keywords": [
"angular-charts",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div class="container" *ngIf="barChartConfig$ | async as config">
<app-bar-chart [chartId]="'bar-example-1'" [data]="config.data" [options]="config.options"></app-bar-chart>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
:host {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;

.container {
display: flex;
flex: 1 1 auto;
flex-wrap: wrap;
width: 100%;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { ComponentFixture, TestBed, TestModuleMetadata } from '@angular/core/testing';
import { firstValueFrom } from 'rxjs';

import { AppChartExamplesBarComponent } from './chart-examples-bar.component';

describe('AppChartExamplesBarComponent', () => {
const testBedConfig: TestModuleMetadata = {
declarations: [AppChartExamplesBarComponent],
};

let fixture: ComponentFixture<AppChartExamplesBarComponent>;
let component: AppChartExamplesBarComponent;

beforeEach(async () => {
await TestBed.configureTestingModule(testBedConfig).compileComponents();
fixture = TestBed.createComponent(AppChartExamplesBarComponent);
component = fixture.debugElement.componentInstance;
fixture.detectChanges();
});

it('should be defined', () => {
expect(component).toBeDefined();
});

it('the chart options should have expected structure', async () => {
const config = await firstValueFrom(component.barChartConfig$);
expect(config.options).toEqual({
chartTitle: 'Example bar chart',
xAxisTitle: 'long x axis title',
yAxisTitle: 'long y axis title',
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { first, map, switchMap, timer } from 'rxjs';

import { IBarChartOptions, TBarChartData } from '../../interfaces/bar-chart.interface';

/**
* BAr chart examples.
*/
@Component({
selector: 'app-chart-examples-bar',
templateUrl: './chart-examples-bar.component.html',
styleUrls: ['./chart-examples-bar.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppChartExamplesBarComponent {
/**
* Sample bar chart data.
*/
private get barChartData() {
return <TBarChartData>[
{ title: 'one', value: 1 },
{ title: 'two', value: 2 },
{ title: 'three', value: 3 },
{ title: 'four', value: 4 },
{ title: 'five', value: 5 },
];
}

private readonly breakpoint$ = this.breakpointObserver
.observe([Breakpoints.XSmall, Breakpoints.Small, Breakpoints.Medium, Breakpoints.Large, Breakpoints.XLarge])
.pipe(map(result => Object.keys(result.breakpoints).find(item => result.breakpoints[item]) ?? 'unknown'));

public readonly barChartConfig$ = this.breakpoint$.pipe(
switchMap(() => {
const timeout = 100;
return timer(timeout).pipe(
first(),
map(() => ({ data: this.barChartData, options: this.barChartOptions() })),
);
}),
);

constructor(private readonly breakpointObserver: BreakpointObserver) {}

/**
* Example bar chart options.
*/
private barChartOptions() {
return <Partial<IBarChartOptions>>{
chartTitle: 'Example bar chart',
xAxisTitle: 'long x axis title',
yAxisTitle: 'long y axis title',
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<div class="container" *ngIf="forceDirectedChartConfig$ | async as config">
<app-force-directed-chart
[chartId]="'force-directed-example-1'"
[data]="config.data"
[options]="config.options"
></app-force-directed-chart>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
:host {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;

.container {
display: flex;
flex: 1 1 auto;
flex-wrap: wrap;
width: 100%;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { ComponentFixture, TestBed, TestModuleMetadata } from '@angular/core/testing';
import { firstValueFrom } from 'rxjs';

import { AppChartExamplesForceDirectedComponent } from './chart-examples-force-directed.component';

describe('AppChartExamplesForceDirectedComponent', () => {
const testBedConfig: TestModuleMetadata = {
declarations: [AppChartExamplesForceDirectedComponent],
};

let fixture: ComponentFixture<AppChartExamplesForceDirectedComponent>;
let component: AppChartExamplesForceDirectedComponent;

beforeEach(async () => {
await TestBed.configureTestingModule(testBedConfig).compileComponents();
fixture = TestBed.createComponent(AppChartExamplesForceDirectedComponent);
component = fixture.debugElement.componentInstance;
fixture.detectChanges();
});

it('should be defined', () => {
expect(component).toBeDefined();
});

it('the chart options should have expected structure', async () => {
const config = await firstValueFrom(component.forceDirectedChartConfig$);
expect(config.options).toEqual({
chartTitle: 'Example force directed chart',
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { first, map, switchMap, timer } from 'rxjs';

import {
IForceDirectedChartData,
IForceDirectedChartOptions,
IForceDirectedGraphEntity,
} from '../../interfaces/force-directed-chart.interface';

@Component({
selector: 'app-chart-examples-force-directed',
templateUrl: './chart-examples-force-directed.component.html',
styleUrls: ['./chart-examples-force-directed.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppChartExamplesForceDirectedComponent {
/**
* Sample force directed chart data.
*/
private get forceDirectedChartData() {
const input = {
domains: ['first', 'second', 'third'],
entities: [
{ name: 'one', domains: ['first'], img: '' },
{ name: 'two', domains: ['second'], img: '' },
{ name: 'three', domains: ['third'], img: '' },
{ name: 'four', domains: ['first', 'second'], img: '' },
{ name: 'five', domains: ['second'], img: '' },
{ name: 'six', domains: ['third', 'second'], img: '' },
{ name: 'seven', domains: ['second'], img: '' },
{ name: 'eight', domains: ['third'], img: '' },
],
};
const domains: IForceDirectedChartData['domains'] = input.domains.map((name, index) => ({ index, name, value: 1 }));
const entities: IForceDirectedChartData['entities'] = input.entities.map((app, index) => ({
index: index,
name: app.name,
domains: [...app.domains],
img: app.img,
linksCount: app.domains.length,
}));
const nodes: IForceDirectedGraphEntity[] = [...entities];
const links: IForceDirectedChartData['links'] = entities
.map(entity => {
return entity.domains.map(domain => {
const source = domains.find(value => domain === value.name)?.index ?? -1;
const target = entity.index;
return { source, target };
});
})
.reduce((accumulator, item) => (Array.isArray(item) ? [...accumulator, ...item] : [...accumulator, item]), [])
.filter(link => link.source !== -1 && link.target !== -1 && typeof link.target !== 'undefined');
const chartData: IForceDirectedChartData = {
domains,
entities: entities.map(item => ({
...item,
linksCount: links.reduce((acc, link) => (link.target === item.index ? acc + 1 : acc), 0),
})),
links,
nodes,
};
return chartData;
}

private readonly breakpoint$ = this.breakpointObserver
.observe([Breakpoints.XSmall, Breakpoints.Small, Breakpoints.Medium, Breakpoints.Large, Breakpoints.XLarge])
.pipe(map(result => Object.keys(result.breakpoints).find(item => result.breakpoints[item]) ?? 'unknown'));

public readonly forceDirectedChartConfig$ = this.breakpoint$.pipe(
switchMap(() => {
const timeout = 100;
return timer(timeout).pipe(
first(),
map(() => ({ data: this.forceDirectedChartData, options: this.forceDirectedChartOptions() })),
);
}),
);

constructor(private readonly breakpointObserver: BreakpointObserver) {}

/**
* Example force directed chart options.
*/
private forceDirectedChartOptions() {
return <Partial<IForceDirectedChartOptions>>{
chartTitle: 'Example force directed chart',
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<div class="container" *ngIf="lineChartConfig$ | async as config">
<app-line-chart [chartId]="'line-example-1'" [data]="[config.data[0]]" [options]="config.options"></app-line-chart>

<app-line-chart [chartId]="'line-example-2'" [data]="[config.data[1]]" [options]="config.optionsDateDdMmYy"></app-line-chart>

<app-line-chart
[chartId]="'line-example-3'"
[data]="[config.data[1], config.data[2]]"
[options]="config.optionsDateDdMmYyyy"
></app-line-chart>

<app-line-chart [chartId]="'line-example-4'" [data]="config.data" [options]="config.optionsDateMmYyyy"></app-line-chart>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
:host {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;

.container {
display: flex;
flex: 1 1 auto;
flex-wrap: wrap;
width: 100%;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ComponentFixture, TestBed, TestModuleMetadata } from '@angular/core/testing';
import { firstValueFrom } from 'rxjs';

import { AppChartExamplesLineComponent } from './chart-examples-line.component';

describe('AppChartExamplesLineComponent', () => {
const testBedConfig: TestModuleMetadata = {
declarations: [AppChartExamplesLineComponent],
};

let fixture: ComponentFixture<AppChartExamplesLineComponent>;
let component: AppChartExamplesLineComponent;

beforeEach(async () => {
await TestBed.configureTestingModule(testBedConfig).compileComponents();
fixture = TestBed.createComponent(AppChartExamplesLineComponent);
component = fixture.debugElement.componentInstance;
fixture.detectChanges();
});

it('should be defined', () => {
expect(component).toBeDefined();
});

it('the chart options should have expected structure', async () => {
const config = await firstValueFrom(component.lineChartConfig$);
expect(config.options).toEqual({
chartTitle: 'Example line chart, date format default',
dateFormat: 'default',
xAxisTitle: 'Date range',
yAxisTitle: 'Value range',
});
});
});
Loading