Skip to content

Commit 9c9eea2

Browse files
committed
let SankeyNode that it is the main node, improved tooltip format
1 parent e2e6661 commit 9c9eea2

File tree

2 files changed

+41
-22
lines changed

2 files changed

+41
-22
lines changed

src/sankey.ts

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ export class SankeyChart {
6161
}
6262

6363
buildNodesConfig(): Array<SeriesSankeyNodesOptionsObject> {
64+
const self = this;
65+
6466
let nodes: Array<SeriesSankeyNodesOptionsObject> = [];
6567
nodes.push({
6668
id: String(this.mainNodeId),
@@ -69,10 +71,10 @@ export class SankeyChart {
6971
dataLabels: {
7072
className: "main-node-label",
7173
nodeFormatter: function (): string {
72-
const node = new SankeyNode(this as Highcharts.SankeyNodeObject);
73-
return `${this.name}: ${node.getTotalWeight() == 0 ? '' : numberFormatColored(node.getTotalWeight())}`;
74+
const node = new SankeyNode(self, this as Highcharts.SankeyNodeObject);
75+
return node.toString();
7476
}
75-
}
77+
},
7678
});
7779

7880
new Map([...config.categories].filter(([categoryId, category]) => categoryId !== this.mainNodeId))
@@ -134,39 +136,47 @@ export class SankeyChart {
134136
align: 'right',
135137
padding: 30,
136138
nodeFormatter: function (): string {
137-
const node = new SankeyNode(this as Highcharts.SankeyNodeObject);
139+
const node = new SankeyNode(self, this as Highcharts.SankeyNodeObject);
138140

139141
return node.name + ': ' + (new NodeValidator(node, config).validate() ? '' : NodeValidator.warningSign)
140-
+ numberFormat(node.getSum()) + ' '
142+
+ numberFormat(node.getValue()) + ' '
141143
+ (node.getPercentage() && node.getPercentage() < 100 ? "<span class='badge text-bg-secondary'>" + Math.round(node.getPercentage()) + "% </span>" : "");
142144
}
143145
},
144146
tooltip: {
145147
// tooltip for link
146148
pointFormatter: function (): string {
147149
const point = this as any;
148-
return point.fromNode.name + " → " + point.toNode.name + ": " + numberFormat(point.weight/config.scalingFactor) + "<br /><br /><span class='small'>(Klick entfernt die Kategorie aus dem Chart.)</span>";
150+
return point.fromNode.name + " → " + point.toNode.name + ": " + numberFormat(point.weight/config.scalingFactor) + "<br><br><span class='small'>(Klick entfernt die Kategorie aus dem Chart.)</span>";
149151
},
150152
// tooltip for node
151153
nodeFormatter: function (): string {
152-
const node = new SankeyNode(this as Highcharts.SankeyNodeObject);
154+
const node = new SankeyNode(self, this as Highcharts.SankeyNodeObject);
153155

154156
let weightsDetailTooltip = '';
155157
node.getLinksTo().filter(link => link.from !== String(self.mainNodeId) && link.weight > 0)
158+
.sort((a, b) => b.weight - a.weight)
156159
.forEach(function (link: any) {
157-
weightsDetailTooltip += '+ ' + numberFormat(link.weight/config.scalingFactor) + ' (' + link.fromNode.name + ')<br />';
160+
weightsDetailTooltip += '+ ' + numberFormat(link.weight/config.scalingFactor) + ' (' + link.fromNode.name + ')<br>';
158161
});
162+
if (node.isMain) {
163+
weightsDetailTooltip += '= ' + numberFormat(node.getTotalIncomingWeight()) + '<br><br>';
164+
}
159165

160166
node.getLinksFrom().filter(link => link.to !== String(self.mainNodeId) && link.weight > 0)
167+
.sort((a, b) => b.weight - a.weight)
161168
.forEach(function (link: any) {
162-
weightsDetailTooltip += '- ' + numberFormat(link.weight/config.scalingFactor) + ' (' + link.toNode.name + ')<br />';
169+
weightsDetailTooltip += '- ' + numberFormat(link.weight/config.scalingFactor) + ' (' + link.toNode.name + ')<br>';
163170
});
171+
if (node.isMain) {
172+
weightsDetailTooltip += '= ' + numberFormat(node.getTotalOutgoingWeight()) + '<br>';
173+
}
164174

165175
const validator = new NodeValidator(node, config);
166176
validator.validate();
167177

168-
return node.toString() + '<br />'
169-
+ weightsDetailTooltip + '<br />'
178+
return node.toString() + '<br>'
179+
+ weightsDetailTooltip + '<br>'
170180
+ validator.messages;
171181
}
172182
},
@@ -185,9 +195,9 @@ export class SankeyChart {
185195
link.graphic.element.setAttribute('data-testid', `chart-link-${link.custom?.category?.id}`);
186196
});
187197
((this.series[0] as any).nodes as Array<Highcharts.SankeyNodeObject>).forEach((point: any, index) => {
188-
const node = new SankeyNode(point);
198+
const node = new SankeyNode(self, point);
189199
point.graphic.element.setAttribute('data-testid', `chart-node-${point.id}`);
190-
point.graphic.element.setAttribute('data-value', point.id === '1' ? node.getTotalWeight() : node.getSum());
200+
point.graphic.element.setAttribute('data-value', node.getValue());
191201
if (point.dataLabel && point.dataLabel.element) {
192202
point.dataLabel.element.setAttribute('data-testid', `chart-node-label-${point.id}`);
193203
}
@@ -217,7 +227,7 @@ export class SankeyChart {
217227
throw new Error('cannot find node with id ' + nodeId);
218228
}
219229

220-
return new SankeyNode(node);
230+
return new SankeyNode(this, node);
221231
}
222232

223233
getOutgoingWeights(): Array<number> {
@@ -229,21 +239,30 @@ export class SankeyNode {
229239
public name: string = '';
230240
public categoryId: number;
231241
public label: string = '';
232-
private readonly node: SeriesSankeyNodesOptionsObject;
242+
public isMain: boolean = false;
243+
private readonly node: Highcharts.SankeyNodeObject;
244+
private readonly chart: SankeyChart;
233245

234-
constructor(node: SeriesSankeyNodesOptionsObject) {
246+
constructor(chart: SankeyChart, node: Highcharts.SankeyNodeObject) {
247+
this.chart = chart;
235248
this.node = node;
236249
this.name = node.name;
237250
// @ts-ignore
238251
this.label = node.dataLabels !== null && typeof node.dataLabels !== 'undefined' && node.dataLabels.length > 0 ? node.dataLabels[0].textStr : '';
239252
this.categoryId = parseInt((node as any).point.id);
253+
this.isMain = this.categoryId === this.chart.mainNodeId;
240254
}
241255

242256
public toString(): string {
243-
return `${this.name}: ${this.getSum() == 0 ? '' : numberFormatColored(this.getSum())}`;
257+
const format = this.isMain ? numberFormatColored : numberFormat;
258+
return `${this.name}: ${this.getValue() == 0 ? '' : format(this.getValue())}`;
259+
}
260+
261+
public getValue(): number {
262+
return this.isMain ? this.getTotalWeight() : this.getSum();
244263
}
245264

246-
public getSum(): number {
265+
private getSum(): number {
247266
return 'getSum' in this.node ? (this.node as any).getSum() / config.scalingFactor : 0;
248267
}
249268

@@ -253,9 +272,9 @@ export class SankeyNode {
253272
return null;
254273
}
255274

256-
const parentNode = new SankeyNode((linksTo[0] as any).fromNode);
275+
const parentNode = new SankeyNode(this.chart, (linksTo[0] as any).fromNode);
257276

258-
return (this.getSum() / parentNode.getTotalOutgoingWeight()) * 100;
277+
return (this.getValue() / parentNode.getTotalOutgoingWeight()) * 100;
259278
}
260279

261280
public getLinksFrom(): Array<PointOptionsObject> {
@@ -274,7 +293,7 @@ export class SankeyNode {
274293
return this.getLinksFrom().map(point => point.weight).reduce((pv, cv) => pv + cv, 0) / config.scalingFactor;
275294
}
276295

277-
public getTotalWeight(): number {
296+
private getTotalWeight(): number {
278297
return this.getTotalIncomingWeight() - this.getTotalOutgoingWeight();
279298
}
280299
}

tests/chart.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {test, expect, Page, Locator} from '@playwright/test';
22
import {NodeValidator} from "../src/validators";
3-
import {SankeyChart, SankeyNode} from "../src/sankey";
3+
import {SankeyChart} from "../src/sankey";
44

55
const mainNodeId: number = 1;
66

0 commit comments

Comments
 (0)