Skip to content

Commit

Permalink
Kamu UI 148 extend node content (#152)
Browse files Browse the repository at this point in the history
* Changed template content for graph nodes
* Resolved avatarUrl by account name.
  • Loading branch information
dmitriy-borzenko authored Sep 8, 2023
1 parent 58d7803 commit e27efdf
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 41 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Deleting and renaming a dataset on Settings tab
- Added icons for dataset's tabs
- Added more content for graph's node
### Changed
- Upgraded to Node.JS generation 16.x
- Upgraded to Angular 14.3
Expand Down
2 changes: 2 additions & 0 deletions src/app/api/mock/dataset.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ export const MOCK_NODES = [
isRoot: true,
isCurrent: true,
color: "#7aa3e5",
accountName: "kamu",
},
meta: {
forceDimensions: false,
Expand All @@ -255,6 +256,7 @@ export const MOCK_NODES = [
isRoot: false,
isCurrent: false,
color: "#a8385d",
accountName: "kamu",
},
meta: {
forceDimensions: false,
Expand Down
56 changes: 27 additions & 29 deletions src/app/components/lineage-graph/lineage-graph.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
<svg:g class="node">
<svg:rect
[attr.width]="node.dimension.width"
[attr.height]="node.dimension.height"
[attr.height]="node.dimension.height * 1.1"
[attr.rx]="5"
[attr.fill]="node.data.isRoot ? '#ffa500' : '#add8e6'"
[attr.cursor]="'pointer'"
Expand All @@ -45,18 +45,39 @@
<svg:text
alignment-baseline="central"
[attr.x]="10"
[attr.y]="node.dimension.height / 2"
[attr.y]="node.dimension.height * 0.65"
[attr.font-weight]="node.data.isCurrent ? '500' : 'normal'"
[attr.cursor]="'pointer'"
(click)="!node.data.isCurrent && onClickNode(node)"
>
{{ node.label }}
</svg:text>
<svg:g *ngIf="!node.data.isRoot" [attr.transform]="'translate(' + node.dimension.width + ', 0)'">
<image
[attr.x]="10"
[attr.y]="8"
[attr.width]="18"
[attr.height]="18"
attr.data-test-id="account-avatar-{{ node.label }}"
[attr.xlink:href]="(getAccountUrl(node.data.accountName) | async)?.avatarUrl || DEFAULT_AVATAR_URL"
/>
<svg:text alignment-baseline="central" [attr.x]="35" [attr.y]="16" [attr.font-weight]="'normal'">
{{ node.data.accountName }}
</svg:text>
<svg:g [attr.transform]="'translate(' + node.dimension.width / 2 + ', 0)'">
<!-- Temporary solution - in future icon will be choosen on explicit public/private attribute in the dataset -->
<image
[attr.x]="-12"
[attr.y]="-12"
[attr.width]="20"
[attr.height]="20"
[attr.href]="node.data.isRoot ? '../assets/images/public.png' : '../assets/images/private.png'"
/>
</svg:g>
<svg:g [attr.transform]="'translate(' + node.dimension.width + ', 0)'">
<svg:rect
x="-20"
[attr.x]="node.data.isRoot ? -20 : -40"
y="-12"
width="40"
[attr.width]="node.data.isRoot ? 40 : 80"
height="24"
rx="10"
[attr.stroke]="'#262626'"
Expand All @@ -70,7 +91,7 @@
fill="black"
style="font-size: 12px"
>
SQL
{{ node.data.isRoot ? "Root" : "Derivative" }}
</svg:text>
</svg:g>
</svg:g>
Expand All @@ -79,29 +100,6 @@
<ng-template #linkTemplate let-link>
<svg:g class="edge">
<svg:path class="line" stroke-width="2" marker-end="url(#arrow)"></svg:path>
<!--svg:text class="edge-label" text-anchor="middle">
<textPath
class="text-path"
[attr.href]="'#' + link.id"
[style.dominant-baseline]="link.dominantBaseline"
startOffset="50%"
>
{{ link.label }}
</textPath>
</svg:text-->
</svg:g>
</ng-template>

<!-- <ng-template #clusterTemplate let-cluster>-->
<!-- <svg:g class="node cluster">-->
<!-- <svg:rect-->
<!-- [attr.width]="cluster.dimension.width"-->
<!-- [attr.height]="cluster.dimension.height"-->
<!-- [attr.fill]="cluster.data.customColor || cluster.data.color"-->
<!-- />-->
<!-- <svg:text>-->
<!-- {{cluster.label}}-->
<!-- </svg:text>-->
<!-- </svg:g>-->
<!-- </ng-template>-->
</ngx-graph>
23 changes: 20 additions & 3 deletions src/app/components/lineage-graph/lineage-graph.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { ComponentFixture, TestBed, fakeAsync, flush, tick } from "@angular/core/testing";
import { LineageGraphComponent } from "./lineage-graph.component";
import { NgxGraphModule } from "@swimlane/ngx-graph";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { MOCK_CLUSTERS, MOCK_LINKS, MOCK_NODES } from "src/app/api/mock/dataset.mock";
import { SimpleChange } from "@angular/core";
import { Apollo, ApolloModule } from "apollo-angular";
import { ApolloTestingModule } from "apollo-angular/testing";
import { AccountService } from "src/app/services/account.service";
import { of } from "rxjs";
import { mockAccountDetails } from "src/app/api/mock/auth.mock";
import { findElementByDataTestId } from "src/app/common/base-test.helpers.spec";

describe("LineageGraphComponent", () => {
let component: LineageGraphComponent;
let fixture: ComponentFixture<LineageGraphComponent>;
let accountService: AccountService;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [LineageGraphComponent],
imports: [NgxGraphModule, BrowserAnimationsModule],
providers: [Apollo],
imports: [NgxGraphModule, BrowserAnimationsModule, ApolloModule, ApolloTestingModule],
}).compileComponents();

fixture = TestBed.createComponent(LineageGraphComponent);
accountService = TestBed.inject(AccountService);
component = fixture.componentInstance;
component.view = [500, 600];
component.links = MOCK_LINKS;
Expand All @@ -40,7 +49,15 @@ describe("LineageGraphComponent", () => {
clusters: new SimpleChange(MOCK_CLUSTERS, [MOCK_CLUSTERS[0]], false),
});
fixture.detectChanges();

expect(component.graphNodes).toEqual([MOCK_NODES[0]]);
});

it("should check render account avatar", fakeAsync(() => {
spyOn(accountService, "getAccountInfoByName").and.returnValue(of(mockAccountDetails));
tick();
fixture.detectChanges();
const node = findElementByDataTestId(fixture, `account-avatar-${MOCK_NODES[0].label}`);
expect(node).toBeDefined();
flush();
}));
});
11 changes: 11 additions & 0 deletions src/app/components/lineage-graph/lineage-graph.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import {
} from "@angular/core";
import { Edge, MiniMapPosition } from "@swimlane/ngx-graph";
import { ClusterNode, Node } from "@swimlane/ngx-graph/lib/models/node.model";
import { Observable } from "rxjs";
import { AccountDetailsFragment } from "src/app/api/kamu.graphql.interface";
import AppValues from "src/app/common/app.values";
import { AccountService } from "src/app/services/account.service";

@Component({
selector: "app-lineage-graph",
Expand Down Expand Up @@ -38,6 +42,9 @@ export class LineageGraphComponent implements OnChanges, OnInit {
public miniMapPosition: MiniMapPosition;
public graphClusters: ClusterNode[];
public graphNodes: Node[];
public readonly DEFAULT_AVATAR_URL = AppValues.DEFAULT_AVATAR_URL;

constructor(private accountService: AccountService) {}

public ngOnInit(): void {
this.graphNodes = this.nodes;
Expand All @@ -61,4 +68,8 @@ export class LineageGraphComponent implements OnChanges, OnInit {
public onClickNode(node: Node): void {
this.onClickNodeEvent.emit(node);
}

public getAccountUrl(accountName: string): Observable<AccountDetailsFragment> {
return this.accountService.getAccountInfoByName(accountName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export class LineageComponent extends BaseComponent implements OnInit {
}

if (cluster.label === dataset.kind) {
cluster.childNodeIds.push(dataset.id );
cluster.childNodeIds.push(dataset.id);
}
return cluster;
});
Expand All @@ -92,27 +92,29 @@ export class LineageComponent extends BaseComponent implements OnInit {
const uniqueDatasets: Record<string, DatasetBasicsFragment> = {};
edges.forEach((edge: DatasetBasicsFragment[]) =>
edge.forEach((dataset: DatasetBasicsFragment) => {
uniqueDatasets[dataset.id ] = dataset;
uniqueDatasets[dataset.id] = dataset;
}),
);

for (const [id, dataset] of Object.entries(uniqueDatasets)) {
this.lineageGraphNodes.push({
id: this.sanitizeID(id),
label: dataset.name ,
label: dataset.name,
data: {
id: dataset.id ,
name: dataset.name ,
id: dataset.id,
name: dataset.name,
kind: dataset.kind,
isRoot: dataset.kind === DatasetKind.Root,
isCurrent: dataset.id === currentDataset.id,
access: "private",
accountName: dataset.owner.name,
},
});
}

edges.forEach((edge: DatasetBasicsFragment[]) => {
const source: string = this.sanitizeID(edge[0].id );
const target: string = this.sanitizeID(edge[1].id );
const source: string = this.sanitizeID(edge[0].id);
const target: string = this.sanitizeID(edge[1].id);

this.lineageGraphLink.push({
id: `${source}__and__${target}`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ p
h2
font-weight: 400
margin: 0
dl
margin: 0
dl
margin: 0

.p-responsive
padding-left: 16px
Expand Down
Binary file added src/assets/images/private.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/images/public.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit e27efdf

Please sign in to comment.